cloud-mu 3.1.6 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/bin/mu-adopt +15 -12
  4. data/bin/mu-azure-tests +57 -0
  5. data/bin/mu-cleanup +2 -4
  6. data/bin/mu-configure +37 -1
  7. data/bin/mu-deploy +3 -3
  8. data/bin/mu-findstray-tests +25 -0
  9. data/bin/mu-gen-docs +2 -4
  10. data/bin/mu-load-config.rb +2 -1
  11. data/bin/mu-run-tests +37 -12
  12. data/cloud-mu.gemspec +4 -4
  13. data/cookbooks/mu-tools/attributes/default.rb +7 -0
  14. data/cookbooks/mu-tools/libraries/helper.rb +87 -3
  15. data/cookbooks/mu-tools/recipes/apply_security.rb +39 -23
  16. data/cookbooks/mu-tools/recipes/aws_api.rb +13 -0
  17. data/cookbooks/mu-tools/recipes/google_api.rb +4 -0
  18. data/cookbooks/mu-tools/recipes/rsyslog.rb +8 -1
  19. data/cookbooks/mu-tools/resources/disk.rb +33 -12
  20. data/cookbooks/mu-tools/resources/mommacat_request.rb +1 -2
  21. data/cookbooks/mu-tools/templates/centos-8/sshd_config.erb +215 -0
  22. data/extras/clean-stock-amis +10 -2
  23. data/extras/generate-stock-images +7 -3
  24. data/extras/image-generators/AWS/centos7.yaml +19 -16
  25. data/extras/image-generators/AWS/{rhel7.yaml → rhel71.yaml} +0 -0
  26. data/extras/image-generators/AWS/{win2k12.yaml → win2k12r2.yaml} +0 -0
  27. data/modules/mommacat.ru +2 -2
  28. data/modules/mu.rb +84 -97
  29. data/modules/mu/adoption.rb +359 -59
  30. data/modules/mu/cleanup.rb +67 -44
  31. data/modules/mu/cloud.rb +108 -1754
  32. data/modules/mu/cloud/database.rb +49 -0
  33. data/modules/mu/cloud/dnszone.rb +44 -0
  34. data/modules/mu/cloud/machine_images.rb +212 -0
  35. data/modules/mu/cloud/providers.rb +81 -0
  36. data/modules/mu/cloud/resource_base.rb +929 -0
  37. data/modules/mu/cloud/server.rb +40 -0
  38. data/modules/mu/cloud/server_pool.rb +1 -0
  39. data/modules/mu/cloud/ssh_sessions.rb +228 -0
  40. data/modules/mu/cloud/winrm_sessions.rb +237 -0
  41. data/modules/mu/cloud/wrappers.rb +178 -0
  42. data/modules/mu/config.rb +122 -80
  43. data/modules/mu/config/alarm.rb +2 -6
  44. data/modules/mu/config/bucket.rb +32 -3
  45. data/modules/mu/config/cache_cluster.rb +2 -2
  46. data/modules/mu/config/cdn.rb +100 -0
  47. data/modules/mu/config/collection.rb +1 -1
  48. data/modules/mu/config/container_cluster.rb +2 -2
  49. data/modules/mu/config/database.rb +84 -105
  50. data/modules/mu/config/database.yml +1 -2
  51. data/modules/mu/config/dnszone.rb +5 -4
  52. data/modules/mu/config/doc_helpers.rb +4 -5
  53. data/modules/mu/config/endpoint.rb +2 -1
  54. data/modules/mu/config/firewall_rule.rb +3 -19
  55. data/modules/mu/config/folder.rb +1 -1
  56. data/modules/mu/config/function.rb +17 -8
  57. data/modules/mu/config/group.rb +1 -1
  58. data/modules/mu/config/habitat.rb +1 -1
  59. data/modules/mu/config/job.rb +89 -0
  60. data/modules/mu/config/loadbalancer.rb +57 -11
  61. data/modules/mu/config/log.rb +1 -1
  62. data/modules/mu/config/msg_queue.rb +1 -1
  63. data/modules/mu/config/nosqldb.rb +1 -1
  64. data/modules/mu/config/notifier.rb +8 -19
  65. data/modules/mu/config/ref.rb +81 -9
  66. data/modules/mu/config/role.rb +1 -1
  67. data/modules/mu/config/schema_helpers.rb +30 -34
  68. data/modules/mu/config/search_domain.rb +1 -1
  69. data/modules/mu/config/server.rb +5 -13
  70. data/modules/mu/config/server_pool.rb +3 -7
  71. data/modules/mu/config/storage_pool.rb +1 -1
  72. data/modules/mu/config/tail.rb +10 -0
  73. data/modules/mu/config/user.rb +1 -1
  74. data/modules/mu/config/vpc.rb +13 -17
  75. data/modules/mu/defaults/AWS.yaml +106 -106
  76. data/modules/mu/defaults/Azure.yaml +1 -0
  77. data/modules/mu/defaults/Google.yaml +1 -0
  78. data/modules/mu/deploy.rb +33 -19
  79. data/modules/mu/groomer.rb +15 -0
  80. data/modules/mu/groomers/chef.rb +3 -0
  81. data/modules/mu/logger.rb +120 -144
  82. data/modules/mu/master.rb +22 -1
  83. data/modules/mu/mommacat.rb +71 -26
  84. data/modules/mu/mommacat/daemon.rb +23 -14
  85. data/modules/mu/mommacat/naming.rb +82 -3
  86. data/modules/mu/mommacat/search.rb +59 -16
  87. data/modules/mu/mommacat/storage.rb +119 -48
  88. data/modules/mu/{clouds → providers}/README.md +1 -1
  89. data/modules/mu/{clouds → providers}/aws.rb +248 -62
  90. data/modules/mu/{clouds → providers}/aws/alarm.rb +3 -3
  91. data/modules/mu/{clouds → providers}/aws/bucket.rb +275 -41
  92. data/modules/mu/{clouds → providers}/aws/cache_cluster.rb +14 -50
  93. data/modules/mu/providers/aws/cdn.rb +782 -0
  94. data/modules/mu/{clouds → providers}/aws/collection.rb +5 -5
  95. data/modules/mu/{clouds → providers}/aws/container_cluster.rb +65 -63
  96. data/modules/mu/providers/aws/database.rb +1747 -0
  97. data/modules/mu/{clouds → providers}/aws/dnszone.rb +26 -12
  98. data/modules/mu/providers/aws/endpoint.rb +1072 -0
  99. data/modules/mu/{clouds → providers}/aws/firewall_rule.rb +39 -32
  100. data/modules/mu/{clouds → providers}/aws/folder.rb +1 -1
  101. data/modules/mu/{clouds → providers}/aws/function.rb +291 -133
  102. data/modules/mu/{clouds → providers}/aws/group.rb +18 -20
  103. data/modules/mu/{clouds → providers}/aws/habitat.rb +3 -3
  104. data/modules/mu/providers/aws/job.rb +469 -0
  105. data/modules/mu/{clouds → providers}/aws/loadbalancer.rb +77 -47
  106. data/modules/mu/{clouds → providers}/aws/log.rb +5 -5
  107. data/modules/mu/{clouds → providers}/aws/msg_queue.rb +14 -11
  108. data/modules/mu/{clouds → providers}/aws/nosqldb.rb +96 -5
  109. data/modules/mu/{clouds → providers}/aws/notifier.rb +135 -63
  110. data/modules/mu/{clouds → providers}/aws/role.rb +112 -78
  111. data/modules/mu/{clouds → providers}/aws/search_domain.rb +172 -41
  112. data/modules/mu/{clouds → providers}/aws/server.rb +120 -145
  113. data/modules/mu/{clouds → providers}/aws/server_pool.rb +42 -60
  114. data/modules/mu/{clouds → providers}/aws/storage_pool.rb +21 -38
  115. data/modules/mu/{clouds → providers}/aws/user.rb +12 -16
  116. data/modules/mu/{clouds → providers}/aws/userdata/README.md +0 -0
  117. data/modules/mu/{clouds → providers}/aws/userdata/linux.erb +5 -4
  118. data/modules/mu/{clouds → providers}/aws/userdata/windows.erb +0 -0
  119. data/modules/mu/{clouds → providers}/aws/vpc.rb +141 -73
  120. data/modules/mu/{clouds → providers}/aws/vpc_subnet.rb +0 -0
  121. data/modules/mu/{clouds → providers}/azure.rb +4 -1
  122. data/modules/mu/{clouds → providers}/azure/container_cluster.rb +1 -5
  123. data/modules/mu/{clouds → providers}/azure/firewall_rule.rb +8 -1
  124. data/modules/mu/{clouds → providers}/azure/habitat.rb +0 -0
  125. data/modules/mu/{clouds → providers}/azure/loadbalancer.rb +0 -0
  126. data/modules/mu/{clouds → providers}/azure/role.rb +0 -0
  127. data/modules/mu/{clouds → providers}/azure/server.rb +32 -24
  128. data/modules/mu/{clouds → providers}/azure/user.rb +1 -1
  129. data/modules/mu/{clouds → providers}/azure/userdata/README.md +0 -0
  130. data/modules/mu/{clouds → providers}/azure/userdata/linux.erb +0 -0
  131. data/modules/mu/{clouds → providers}/azure/userdata/windows.erb +0 -0
  132. data/modules/mu/{clouds → providers}/azure/vpc.rb +4 -6
  133. data/modules/mu/{clouds → providers}/cloudformation.rb +1 -1
  134. data/modules/mu/{clouds → providers}/cloudformation/alarm.rb +3 -3
  135. data/modules/mu/{clouds → providers}/cloudformation/cache_cluster.rb +3 -3
  136. data/modules/mu/{clouds → providers}/cloudformation/collection.rb +3 -3
  137. data/modules/mu/{clouds → providers}/cloudformation/database.rb +6 -17
  138. data/modules/mu/{clouds → providers}/cloudformation/dnszone.rb +3 -3
  139. data/modules/mu/{clouds → providers}/cloudformation/firewall_rule.rb +3 -3
  140. data/modules/mu/{clouds → providers}/cloudformation/loadbalancer.rb +3 -3
  141. data/modules/mu/{clouds → providers}/cloudformation/log.rb +3 -3
  142. data/modules/mu/{clouds → providers}/cloudformation/server.rb +7 -7
  143. data/modules/mu/{clouds → providers}/cloudformation/server_pool.rb +5 -5
  144. data/modules/mu/{clouds → providers}/cloudformation/vpc.rb +3 -3
  145. data/modules/mu/{clouds → providers}/docker.rb +0 -0
  146. data/modules/mu/{clouds → providers}/google.rb +15 -6
  147. data/modules/mu/{clouds → providers}/google/bucket.rb +2 -2
  148. data/modules/mu/{clouds → providers}/google/container_cluster.rb +29 -14
  149. data/modules/mu/{clouds → providers}/google/database.rb +2 -9
  150. data/modules/mu/{clouds → providers}/google/firewall_rule.rb +3 -3
  151. data/modules/mu/{clouds → providers}/google/folder.rb +5 -9
  152. data/modules/mu/{clouds → providers}/google/function.rb +4 -4
  153. data/modules/mu/{clouds → providers}/google/group.rb +9 -17
  154. data/modules/mu/{clouds → providers}/google/habitat.rb +4 -8
  155. data/modules/mu/{clouds → providers}/google/loadbalancer.rb +2 -2
  156. data/modules/mu/{clouds → providers}/google/role.rb +46 -35
  157. data/modules/mu/{clouds → providers}/google/server.rb +26 -11
  158. data/modules/mu/{clouds → providers}/google/server_pool.rb +11 -11
  159. data/modules/mu/{clouds → providers}/google/user.rb +32 -22
  160. data/modules/mu/{clouds → providers}/google/userdata/README.md +0 -0
  161. data/modules/mu/{clouds → providers}/google/userdata/linux.erb +0 -0
  162. data/modules/mu/{clouds → providers}/google/userdata/windows.erb +0 -0
  163. data/modules/mu/{clouds → providers}/google/vpc.rb +38 -3
  164. data/modules/tests/aws-jobs-functions.yaml +46 -0
  165. data/modules/tests/centos6.yaml +15 -0
  166. data/modules/tests/centos7.yaml +15 -0
  167. data/modules/tests/centos8.yaml +12 -0
  168. data/modules/tests/ecs.yaml +2 -2
  169. data/modules/tests/eks.yaml +1 -1
  170. data/modules/tests/functions/node-function/lambda_function.js +10 -0
  171. data/modules/tests/functions/python-function/lambda_function.py +12 -0
  172. data/modules/tests/microservice_app.yaml +288 -0
  173. data/modules/tests/rds.yaml +108 -0
  174. data/modules/tests/regrooms/rds.yaml +123 -0
  175. data/modules/tests/server-with-scrub-muisms.yaml +1 -1
  176. data/modules/tests/super_complex_bok.yml +2 -2
  177. data/modules/tests/super_simple_bok.yml +2 -2
  178. data/spec/mu/clouds/azure_spec.rb +2 -2
  179. metadata +126 -98
  180. data/modules/mu/clouds/aws/database.rb +0 -1974
  181. data/modules/mu/clouds/aws/endpoint.rb +0 -596
@@ -89,7 +89,7 @@ Looking elsewhere in `cloud.rb` let's see what all we have to do:
89
89
  generic_instance_methods = [:create, :notify, :mu_name, :cloud_id, :config]
90
90
  ```
91
91
 
92
- Just the basics, for now. Here's what that will look like in the AWS layer, in the file `modules/mu/clouds/aws/function.rb`:
92
+ Just the basics, for now. Here's what that will look like in the AWS layer, in the file `modules/mu/providers/aws/function.rb`:
93
93
 
94
94
  ```
95
95
  module MU
@@ -39,7 +39,7 @@ module MU
39
39
  end
40
40
 
41
41
  # List all AWS projects available to our credentials
42
- def self.listHabitats(credentials = nil)
42
+ def self.listHabitats(credentials = nil, use_cache: true)
43
43
  cfg = credConfig(credentials)
44
44
  return [] if !cfg or !cfg['account_number']
45
45
  [cfg['account_number']]
@@ -550,7 +550,9 @@ end
550
550
  def self.credToAcct(name = nil)
551
551
  creds = credConfig(name)
552
552
 
553
- return creds['account_number'] if creds['account_number']
553
+ if creds['account_number'] and !creds['account_number'].empty?
554
+ return creds['account_number']
555
+ end
554
556
 
555
557
  acct_num = MU::Cloud::AWS.iam(credentials: name).list_users.users.first.arn.split(/:/)[4]
556
558
  acct_num.to_s
@@ -672,8 +674,8 @@ end
672
674
  next
673
675
  end
674
676
  acct_num = MU::Cloud::AWS.iam(credentials: acctname).list_users.users.first.arn.split(/:/)[4]
675
- if acct_num.to_s == name.to_s
676
- cfg['account_number'] = acct_num.to_s
677
+ cfg['account_number'] ||= acct_num.to_s
678
+ if acct_num.to_s == name.to_s
677
679
  @@acct_to_profile_map[name.to_s] = cfg
678
680
  return name_only ? name.to_s : cfg
679
681
  end
@@ -805,50 +807,47 @@ end
805
807
 
806
808
  @@instance_types ||= {}
807
809
  @@instance_types[region] ||= {}
808
- next_token = nil
809
810
 
810
- begin
811
- # Pricing API isn't widely available, so ask a region we know supports
812
- # it
813
- resp = MU::Cloud::AWS.pricing(region: "us-east-1").get_products(
814
- service_code: "AmazonEC2",
815
- filters: [
816
- {
817
- field: "productFamily",
818
- value: "Compute Instance",
819
- type: "TERM_MATCH"
820
- },
821
- {
822
- field: "tenancy",
823
- value: "Shared",
824
- type: "TERM_MATCH"
825
- },
826
- {
827
- field: "location",
828
- value: human_region,
829
- type: "TERM_MATCH"
830
- }
831
- ],
832
- next_token: next_token
833
- )
834
- resp.price_list.each { |pricing|
835
- data = JSON.parse(pricing)
836
- type = data["product"]["attributes"]["instanceType"]
837
- next if @@instance_types[region].has_key?(type)
838
- @@instance_types[region][type] = {}
839
- ["ecu", "vcpu", "memory", "storage"].each { |a|
840
- @@instance_types[region][type][a] = data["product"]["attributes"][a]
811
+ # Pricing API isn't widely available, so ask a region we know supports
812
+ # it
813
+ resp = MU::Cloud::AWS.pricing(region: "us-east-1").get_products(
814
+ service_code: "AmazonEC2",
815
+ filters: [
816
+ {
817
+ field: "productFamily",
818
+ value: "Compute Instance",
819
+ type: "TERM_MATCH"
820
+ },
821
+ {
822
+ field: "tenancy",
823
+ value: "Shared",
824
+ type: "TERM_MATCH"
825
+ },
826
+ {
827
+ field: "location",
828
+ value: human_region,
829
+ type: "TERM_MATCH"
841
830
  }
842
- @@instance_types[region][type]["memory"].sub!(/ GiB/, "")
843
- @@instance_types[region][type]["memory"] = @@instance_types[region][type]["memory"].to_f
844
- @@instance_types[region][type]["vcpu"] = @@instance_types[region][type]["vcpu"].to_f
831
+ ]
832
+ )
833
+ resp.price_list.each { |pricing|
834
+ data = JSON.parse(pricing)
835
+ type = data["product"]["attributes"]["instanceType"]
836
+ next if @@instance_types[region].has_key?(type)
837
+ @@instance_types[region][type] = {}
838
+ ["ecu", "vcpu", "memory", "storage"].each { |a|
839
+ @@instance_types[region][type][a] = data["product"]["attributes"][a]
845
840
  }
846
- next_token = resp.next_token
847
- end while resp and next_token
841
+ @@instance_types[region][type]["memory"].sub!(/ GiB/, "")
842
+ @@instance_types[region][type]["memory"] = @@instance_types[region][type]["memory"].to_f
843
+ @@instance_types[region][type]["vcpu"] = @@instance_types[region][type]["vcpu"].to_f
844
+ }
848
845
 
849
846
  @@instance_types
850
847
  end
851
848
 
849
+ @@certificates = {}
850
+
852
851
  # AWS can stash API-available certificates in Amazon Certificate Manager
853
852
  # or in IAM. Rather than make people crazy trying to get the syntax
854
853
  # correct in our Baskets of Kittens, let's have a helper that tries to do
@@ -857,21 +856,24 @@ end
857
856
  # @param name [String]: The name of the cert. For IAM certs this can be any IAM name; for ACM, it's usually the domain name. If multiple matches are found, or no matches, an exception is raised.
858
857
  # @param id [String]: The ARN of a known certificate. We just validate that it exists. This is ignored if a name parameter is supplied.
859
858
  # @return [String]: The ARN of a matching certificate that is known to exist. If it is an ACM certificate, we also know that it is not expired.
860
- def self.findSSLCertificate(name: nil, id: nil, region: myRegion)
861
- if name.nil? and name.empty? and id.nil? and id.empty?
859
+ def self.findSSLCertificate(name: nil, id: nil, region: myRegion, credentials: nil, raise_on_missing: true)
860
+ if (name.nil? or name.empty?) and (id.nil? or id.empty?)
862
861
  raise MuError, "Can't call findSSLCertificate without specifying either a name or an id"
863
862
  end
863
+ if id and @@certificates[id]
864
+ return [id, @@certificates[id]]
865
+ end
864
866
 
865
867
  if !name.nil? and !name.empty?
866
868
  matches = []
867
- acmcerts = MU::Cloud::AWS.acm(region: region).list_certificates(
869
+ acmcerts = MU::Cloud::AWS.acm(region: region, credentials: credentials).list_certificates(
868
870
  certificate_statuses: ["ISSUED"]
869
871
  )
870
872
  acmcerts.certificate_summary_list.each { |cert|
871
873
  matches << cert.certificate_arn if cert.domain_name == name
872
874
  }
873
875
  begin
874
- iamcert = MU::Cloud::AWS.iam.get_server_certificate(
876
+ iamcert = MU::Cloud::AWS.iam(credentials: credentials).get_server_certificate(
875
877
  server_certificate_name: name
876
878
  )
877
879
  rescue Aws::IAM::Errors::ValidationError, Aws::IAM::Errors::NoSuchEntity
@@ -881,32 +883,45 @@ end
881
883
  matches << iamcert.server_certificate.server_certificate_metadata.arn
882
884
  end
883
885
  if matches.size == 1
884
- return matches.first
886
+ id = matches.first
885
887
  elsif matches.size == 0
886
- raise MuError, "No IAM or ACM certificate named #{name} was found in #{region}"
888
+ if raise_on_missing
889
+ raise MuError, "No IAM or ACM certificate named #{name} was found in #{region}"
890
+ else
891
+ return nil
892
+ end
887
893
  elsif matches.size > 1
888
894
  raise MuError, "Multiple certificates named #{name} were found in #{region}. Remove extras or use ssl_certificate_id to supply the exact ARN of the one you want to use."
889
895
  end
890
896
  end
891
897
 
898
+ domains = []
899
+
892
900
  if id.match(/^arn:aws(?:-us-gov)?:acm/)
893
- resp = MU::Cloud::AWS.acm(region: region).get_certificate(
901
+ resp = MU::Cloud::AWS.acm(region: region).describe_certificate(
894
902
  certificate_arn: id
895
903
  )
896
- if resp.nil?
904
+
905
+ if resp.nil? or resp.certificate.nil?
897
906
  raise MuError, "No such ACM certificate '#{id}'"
898
907
  end
908
+ domains << resp.certificate.domain_name
909
+ if resp.certificate.subject_alternative_names
910
+ domains.concat(resp.certificate.subject_alternative_names)
911
+ end
899
912
  elsif id.match(/^arn:aws(?:-us-gov)?:iam/)
900
913
  resp = MU::Cloud::AWS.iam.list_server_certificates
901
914
  if resp.nil?
902
915
  raise MuError, "No such IAM certificate '#{id}'"
903
916
  end
904
917
  resp.server_certificate_metadata_list.each { |cert|
918
+
905
919
  if cert.arn == id
906
920
  if cert.expiration < Time.now
907
921
  MU.log "IAM SSL certificate #{cert.server_certificate_name} (#{id}) is EXPIRED", MU::WARN
908
922
  end
909
- return id
923
+ @@certificates[id] = [cert.server_certificate_name]
924
+ return [id, [cert.server_certificate_name]]
910
925
  end
911
926
  }
912
927
  raise MuError, "No such IAM certificate '#{id}'"
@@ -914,7 +929,56 @@ end
914
929
  raise MuError, "The format of '#{id}' doesn't look like an ARN for either Amazon Certificate Manager or IAM"
915
930
  end
916
931
 
917
- id
932
+ @@certificates[id] = domains.uniq
933
+ [id, domains.uniq]
934
+ end
935
+
936
+ # Given a domain name and an ACM or IAM certificate identifier, sort out
937
+ # whether the domain name is "covered" by the certificate
938
+ # @param name [String]
939
+ # @param cert_id [String]
940
+ # @return [Boolean]
941
+ def self.nameMatchesCertificate(name, cert_id)
942
+ _id, domains = findSSLCertificate(id: cert_id)
943
+ return false if !domains
944
+ domains.each { |dom|
945
+ if dom == name or
946
+ (dom =~ /^\*/ and name =~ /.*#{Regexp.quote(dom[1..-1])}/)
947
+ return true
948
+ end
949
+ }
950
+ false
951
+ end
952
+
953
+ # Given a {MU::Config::Ref} block for an IAM or ACM SSL certificate,
954
+ # look up and validate the specified certificate. This is intended to be
955
+ # invoked from resource implementations' +validateConfig+ methods.
956
+ # @param certblock [Hash,MU::Config::Ref]:
957
+ # @param region [String]: Default region to use when looking up the certificate, if its configuration block does not specify any
958
+ # @param credentials [String]: Default credentials to use when looking up the certificate, if its configuration block does not specify any
959
+ # @return [Boolean]
960
+ def self.resolveSSLCertificate(certblock, region: nil, credentials: nil)
961
+ return false if !certblock
962
+ ok = true
963
+
964
+ certblock['region'] ||= region if !certblock['id']
965
+ certblock['credentials'] ||= credentials
966
+ cert_arn, cert_domains = MU::Cloud::AWS.findSSLCertificate(
967
+ name: certblock["name"],
968
+ id: certblock["id"],
969
+ region: certblock['region'],
970
+ credentials: certblock['credentials']
971
+ )
972
+
973
+ if cert_arn
974
+ certblock['id'] ||= cert_arn
975
+ end
976
+
977
+ ['region', 'credentials'].each { |field|
978
+ certblock.delete(field) if certblock[field].nil?
979
+ }
980
+
981
+ [cert_arn, cert_domains]
918
982
  end
919
983
 
920
984
  # Amazon Certificate Manager API
@@ -1034,6 +1098,14 @@ end
1034
1098
  @@cloudwatchlogs_api[credentials][region]
1035
1099
  end
1036
1100
 
1101
+ # Amazon's CloudWatchEvents API
1102
+ def self.cloudwatchevents(region: MU.curRegion, credentials: nil)
1103
+ region ||= myRegion
1104
+ @@cloudwatchevents_api[credentials] ||= {}
1105
+ @@cloudwatchevents_api[credentials][region] ||= MU::Cloud::AWS::AmazonEndpoint.new(api: "CloudWatchEvents", region: region, credentials: credentials)
1106
+ @@cloudwatchevents_api[credentials][region]
1107
+ end
1108
+
1037
1109
  # Amazon's CloudFront API
1038
1110
  def self.cloudfront(region: MU.curRegion, credentials: nil)
1039
1111
  region ||= myRegion
@@ -1122,6 +1194,14 @@ end
1122
1194
  @@dynamo_api[credentials][region]
1123
1195
  end
1124
1196
 
1197
+ # Amazon's DynamoStream API
1198
+ def self.dynamostream(region: MU.curRegion, credentials: nil)
1199
+ region ||= myRegion
1200
+ @@dynamostream_api[credentials] ||= {}
1201
+ @@dynamostream_api[credentials][region] ||= MU::Cloud::AWS::AmazonEndpoint.new(api: "DynamoDBStreams", region: region, credentials: credentials)
1202
+ @@dynamostream_api[credentials][region]
1203
+ end
1204
+
1125
1205
  # Amazon's Pricing API
1126
1206
  def self.pricing(region: MU.curRegion, credentials: nil)
1127
1207
  region ||= myRegion
@@ -1170,6 +1250,14 @@ end
1170
1250
  @@kms_api[credentials][region]
1171
1251
  end
1172
1252
 
1253
+ # Amazon's CloudFront API
1254
+ def self.cloudfront(region: MU.curRegion, credentials: nil)
1255
+ region ||= myRegion
1256
+ @@cloudfront_api[credentials] ||= {}
1257
+ @@cloudfront_api[credentials][region] ||= MU::Cloud::AWS::AmazonEndpoint.new(api: "CloudFront", region: region, credentials: credentials)
1258
+ @@cloudfront_api[credentials][region]
1259
+ end
1260
+
1173
1261
  # Amazon's Organizations API
1174
1262
  def self.orgs(credentials: nil)
1175
1263
  @@organizations_api ||= {}
@@ -1251,7 +1339,7 @@ end
1251
1339
  # Mu Master, if we're in AWS.
1252
1340
  # @return [void]
1253
1341
  def self.openFirewallForClients
1254
- MU::Cloud.loadCloudType("AWS", :FirewallRule)
1342
+ MU::Cloud.resourceClass("AWS", :FirewallRule)
1255
1343
  begin
1256
1344
  if File.exist?(Etc.getpwuid(Process.uid).dir+"/.chef/knife.rb")
1257
1345
  ::Chef::Config.from_file(Etc.getpwuid(Process.uid).dir+"/.chef/knife.rb")
@@ -1430,6 +1518,7 @@ end
1430
1518
  def initialize(region: nil, api: "EC2", credentials: nil)
1431
1519
  @cred_obj = MU::Cloud::AWS.loadCredentials(credentials)
1432
1520
  @credentials = MU::Cloud::AWS.credConfig(credentials, name_only: true)
1521
+ @api_name = api
1433
1522
 
1434
1523
  if !@cred_obj
1435
1524
  raise MuError, "Unable to locate valid AWS credentials for #{api} API. #{credentials ? "Credentials requested were '#{credentials}'": ""}"
@@ -1447,6 +1536,8 @@ end
1447
1536
  params[:credentials] = @cred_obj
1448
1537
 
1449
1538
  MU.log "Initializing #{api} object with credentials #{credentials}", MU::DEBUG, details: params
1539
+ require "aws-sdk-#{api.downcase}"
1540
+
1450
1541
  @api = Object.const_get("Aws::#{api}::Client").new(params)
1451
1542
  end
1452
1543
 
@@ -1454,21 +1545,113 @@ end
1454
1545
  # Catch-all for AWS client methods. Essentially a pass-through with some
1455
1546
  # rescues for known silly endpoint behavior.
1456
1547
  def method_missing(method_sym, *arguments)
1457
- require "aws-sdk-core"
1548
+ # make sure error symbols are loaded for our exception handling later
1549
+ require "aws-sdk-lambda"
1550
+ require "aws-sdk-rds"
1551
+ require "aws-sdk-ec2"
1552
+ require "aws-sdk-route53"
1553
+ require "aws-sdk-iam"
1554
+ require "aws-sdk-efs"
1555
+ require "aws-sdk-pricing"
1556
+ require "aws-sdk-apigateway"
1557
+ require "aws-sdk-ecs"
1558
+ require "aws-sdk-eks"
1559
+ require "aws-sdk-cloudwatchlogs"
1560
+ require "aws-sdk-cloudwatchevents"
1561
+ require "aws-sdk-elasticloadbalancing"
1562
+ require "aws-sdk-elasticloadbalancingv2"
1563
+ require "aws-sdk-autoscaling"
1564
+
1565
+ known_concats = {
1566
+ "Pricing" => {
1567
+ :get_products => :price_list
1568
+ }
1569
+ }
1458
1570
 
1459
1571
  retries = 0
1460
1572
  begin
1461
- MU.log "Calling #{method_sym} in #{@region}", MU::DEBUG, details: arguments
1462
- retval = nil
1463
- if !arguments.nil? and arguments.size == 1
1464
- retval = @api.method(method_sym).call(arguments[0])
1465
- elsif !arguments.nil? and arguments.size > 0
1466
- retval = @api.method(method_sym).call(*arguments)
1467
- else
1468
- retval = @api.method(method_sym).call
1573
+ MU.log "Calling #{@api_name}.#{method_sym} in #{@region}", MU::DEBUG, details: arguments
1574
+
1575
+ retval = if !arguments.nil? and arguments.size == 1
1576
+ @api.method(method_sym).call(arguments[0])
1577
+ elsif !arguments.nil? and arguments.size > 0
1578
+ @api.method(method_sym).call(*arguments)
1579
+ else
1580
+ @api.method(method_sym).call
1581
+ end
1582
+
1583
+ if !retval.nil?
1584
+ begin
1585
+ page_markers = {
1586
+ :marker => :marker,
1587
+ :next_token => :next_token,
1588
+ :next_marker => :marker
1589
+ }
1590
+ paginator = nil
1591
+ new_page = nil
1592
+ page_markers.each_key { |m|
1593
+ if !retval.nil? and retval.respond_to?(m)
1594
+ paginator = m
1595
+ new_page = retval.send(m)
1596
+ break
1597
+ end
1598
+ }
1599
+
1600
+ if paginator and new_page and !new_page.empty?
1601
+ resp = retval.respond_to?(:__getobj__) ? retval.__getobj__ : retval
1602
+ concat_to = MU.structToHash(resp).keys.reject { |m|
1603
+ m.to_s.match(/=$/) or m == paginator or resp.send(m).nil? or !resp.send(m).is_a?(Array)
1604
+ }
1605
+
1606
+ if concat_to.empty? and known_concats[@api_name] and
1607
+ known_concats[@api_name][method_sym]
1608
+ concat_to << known_concats[@api_name][method_sym]
1609
+ end
1610
+
1611
+ if concat_to.empty? and method_sym.to_s.match(/^(?:describe|list)_(.*)/)
1612
+ my_attr = Regexp.last_match[1].to_sym
1613
+ concat_to << my_attr if resp.respond_to?(my_attr)
1614
+ end
1615
+
1616
+ if concat_to.size != 1
1617
+ raise MuError.new "Tried to figure out where I might append paginated results for a #{@api_name}.#{method_sym}, but failed", details: MU.structToHash(resp).keys
1618
+ else
1619
+ concat_to = concat_to.first
1620
+ new_args = arguments ? arguments.dup : [{}]
1621
+ begin
1622
+ if new_args.is_a?(Array)
1623
+ new_args << {} if new_args.empty?
1624
+ if new_args.size == 1 and new_args.first.is_a?(Hash)
1625
+ new_args[0][page_markers[paginator]] = new_page
1626
+ else
1627
+ MU.log "I don't know how to insert a #{paginator} into these arguments for #{method_sym}", MU::WARN, details: new_args
1628
+ end
1629
+ elsif new_args.is_a?(Hash)
1630
+ new_args[page_markers[paginator]] = new_page
1631
+ end
1632
+
1633
+ MU.log "Attempting magic pagination for #{method_sym}", MU::DEBUG, details: new_args
1634
+
1635
+ # resp = if !arguments.nil? and arguments.size == 1
1636
+ # @api.method(method_sym).call(new_args[0])
1637
+ # elsif !arguments.nil? and arguments.size > 0
1638
+ resp = @api.method(method_sym).call(*new_args)
1639
+ # end
1640
+ break if resp.nil?
1641
+ resp = resp.__getobj__ if resp.respond_to?(:__getobj__)
1642
+ retval.send(concat_to).concat(resp.send(concat_to))
1643
+ new_page = resp.send(paginator) if !resp.nil?
1644
+ end while !resp.nil? and !new_page.nil? and !new_page.empty?
1645
+ end
1646
+ end
1647
+ rescue StandardError => e
1648
+ MU.log "Made a good-faith effort to auto-paginate API call to #{method_sym} and failed with #{e.message}", MU::DEBUG, details: arguments
1649
+ raise e
1650
+ end
1469
1651
  end
1652
+
1470
1653
  return retval
1471
- rescue Aws::EC2::Errors::InternalError, Aws::EC2::Errors::RequestLimitExceeded, Aws::EC2::Errors::Unavailable, Aws::Route53::Errors::Throttling, Aws::ElasticLoadBalancing::Errors::HttpFailureException, Aws::EC2::Errors::Http503Error, Aws::AutoScaling::Errors::Http503Error, Aws::AutoScaling::Errors::InternalFailure, Aws::AutoScaling::Errors::ServiceUnavailable, Aws::Route53::Errors::ServiceUnavailable, Aws::ElasticLoadBalancing::Errors::Throttling, Aws::RDS::Errors::ClientUnavailable, Aws::Waiters::Errors::UnexpectedError, Aws::ElasticLoadBalancing::Errors::ServiceUnavailable, Aws::ElasticLoadBalancingV2::Errors::Throttling, Seahorse::Client::NetworkingError, Aws::IAM::Errors::Throttling, Aws::EFS::Errors::ThrottlingException, Aws::Pricing::Errors::ThrottlingException, Aws::APIGateway::Errors::TooManyRequestsException, Aws::ECS::Errors::ThrottlingException, Net::ReadTimeout, Faraday::TimeoutError, Aws::CloudWatchLogs::Errors::ThrottlingException => e
1654
+ rescue Aws::Lambda::Errors::TooManyRequestsException, Aws::RDS::Errors::Throttling, Aws::EC2::Errors::InternalError, Aws::EC2::Errors::RequestLimitExceeded, Aws::EC2::Errors::Unavailable, Aws::Route53::Errors::Throttling, Aws::ElasticLoadBalancing::Errors::HttpFailureException, Aws::EC2::Errors::Http503Error, Aws::AutoScaling::Errors::Http503Error, Aws::AutoScaling::Errors::InternalFailure, Aws::AutoScaling::Errors::ServiceUnavailable, Aws::Route53::Errors::ServiceUnavailable, Aws::ElasticLoadBalancing::Errors::Throttling, Aws::RDS::Errors::ClientUnavailable, Aws::Waiters::Errors::UnexpectedError, Aws::ElasticLoadBalancing::Errors::ServiceUnavailable, Aws::ElasticLoadBalancingV2::Errors::Throttling, Seahorse::Client::NetworkingError, Aws::IAM::Errors::Throttling, Aws::EFS::Errors::ThrottlingException, Aws::Pricing::Errors::ThrottlingException, Aws::APIGateway::Errors::TooManyRequestsException, Aws::ECS::Errors::ThrottlingException, Net::ReadTimeout, Faraday::TimeoutError, Aws::CloudWatchLogs::Errors::ThrottlingException => e
1472
1655
  if e.class.name == "Seahorse::Client::NetworkingError" and e.message.match(/Name or service not known/)
1473
1656
  MU.log e.inspect, MU::ERR
1474
1657
  raise e
@@ -1510,6 +1693,7 @@ end
1510
1693
  @@wafglobal = {}
1511
1694
  @@waf = {}
1512
1695
  @@cloudwatchlogs_api = {}
1696
+ @@cloudwatchevents_api = {}
1513
1697
  @@cloudfront_api = {}
1514
1698
  @@elasticache_api = {}
1515
1699
  @@sns_api = {}
@@ -1528,6 +1712,8 @@ end
1528
1712
  @@kms_api ={}
1529
1713
  @@organization_api ={}
1530
1714
  @@dynamo_api ={}
1715
+ @@dynamostream_api ={}
1716
+ @@cloudfront_api ={}
1531
1717
  end
1532
1718
  end
1533
1719
  end