cloud-mu 3.1.5 → 3.3.2

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 (185) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +5 -1
  3. data/ansible/roles/mu-windows/files/LaunchConfig.json +9 -0
  4. data/ansible/roles/mu-windows/files/config.xml +76 -0
  5. data/ansible/roles/mu-windows/tasks/main.yml +16 -0
  6. data/bin/mu-adopt +16 -12
  7. data/bin/mu-azure-tests +57 -0
  8. data/bin/mu-cleanup +2 -4
  9. data/bin/mu-configure +52 -0
  10. data/bin/mu-deploy +3 -3
  11. data/bin/mu-findstray-tests +25 -0
  12. data/bin/mu-gen-docs +2 -4
  13. data/bin/mu-load-config.rb +2 -1
  14. data/bin/mu-node-manage +15 -16
  15. data/bin/mu-run-tests +37 -12
  16. data/cloud-mu.gemspec +3 -3
  17. data/cookbooks/mu-activedirectory/resources/domain.rb +4 -4
  18. data/cookbooks/mu-activedirectory/resources/domain_controller.rb +4 -4
  19. data/cookbooks/mu-tools/libraries/helper.rb +1 -1
  20. data/cookbooks/mu-tools/recipes/apply_security.rb +14 -14
  21. data/cookbooks/mu-tools/recipes/aws_api.rb +9 -0
  22. data/cookbooks/mu-tools/recipes/eks.rb +2 -2
  23. data/cookbooks/mu-tools/recipes/windows-client.rb +25 -22
  24. data/extras/clean-stock-amis +25 -19
  25. data/extras/generate-stock-images +1 -0
  26. data/extras/image-generators/AWS/win2k12.yaml +2 -0
  27. data/extras/image-generators/AWS/win2k16.yaml +2 -0
  28. data/extras/image-generators/AWS/win2k19.yaml +2 -0
  29. data/modules/mommacat.ru +1 -1
  30. data/modules/mu.rb +86 -98
  31. data/modules/mu/adoption.rb +373 -58
  32. data/modules/mu/cleanup.rb +214 -303
  33. data/modules/mu/cloud.rb +128 -1733
  34. data/modules/mu/cloud/database.rb +49 -0
  35. data/modules/mu/cloud/dnszone.rb +44 -0
  36. data/modules/mu/cloud/machine_images.rb +212 -0
  37. data/modules/mu/cloud/providers.rb +81 -0
  38. data/modules/mu/cloud/resource_base.rb +929 -0
  39. data/modules/mu/cloud/server.rb +40 -0
  40. data/modules/mu/cloud/server_pool.rb +1 -0
  41. data/modules/mu/cloud/ssh_sessions.rb +228 -0
  42. data/modules/mu/cloud/winrm_sessions.rb +237 -0
  43. data/modules/mu/cloud/wrappers.rb +169 -0
  44. data/modules/mu/config.rb +123 -81
  45. data/modules/mu/config/alarm.rb +2 -6
  46. data/modules/mu/config/bucket.rb +32 -3
  47. data/modules/mu/config/cache_cluster.rb +2 -2
  48. data/modules/mu/config/cdn.rb +100 -0
  49. data/modules/mu/config/collection.rb +1 -1
  50. data/modules/mu/config/container_cluster.rb +7 -2
  51. data/modules/mu/config/database.rb +84 -105
  52. data/modules/mu/config/database.yml +1 -2
  53. data/modules/mu/config/dnszone.rb +5 -4
  54. data/modules/mu/config/doc_helpers.rb +5 -6
  55. data/modules/mu/config/endpoint.rb +2 -1
  56. data/modules/mu/config/firewall_rule.rb +3 -19
  57. data/modules/mu/config/folder.rb +1 -1
  58. data/modules/mu/config/function.rb +17 -8
  59. data/modules/mu/config/group.rb +1 -1
  60. data/modules/mu/config/habitat.rb +1 -1
  61. data/modules/mu/config/job.rb +89 -0
  62. data/modules/mu/config/loadbalancer.rb +57 -11
  63. data/modules/mu/config/log.rb +1 -1
  64. data/modules/mu/config/msg_queue.rb +1 -1
  65. data/modules/mu/config/nosqldb.rb +1 -1
  66. data/modules/mu/config/notifier.rb +8 -19
  67. data/modules/mu/config/ref.rb +92 -14
  68. data/modules/mu/config/role.rb +1 -1
  69. data/modules/mu/config/schema_helpers.rb +38 -37
  70. data/modules/mu/config/search_domain.rb +1 -1
  71. data/modules/mu/config/server.rb +12 -13
  72. data/modules/mu/config/server_pool.rb +3 -7
  73. data/modules/mu/config/storage_pool.rb +1 -1
  74. data/modules/mu/config/tail.rb +11 -0
  75. data/modules/mu/config/user.rb +1 -1
  76. data/modules/mu/config/vpc.rb +27 -23
  77. data/modules/mu/config/vpc.yml +0 -1
  78. data/modules/mu/defaults/AWS.yaml +90 -90
  79. data/modules/mu/defaults/Azure.yaml +1 -0
  80. data/modules/mu/defaults/Google.yaml +1 -0
  81. data/modules/mu/deploy.rb +34 -20
  82. data/modules/mu/groomer.rb +16 -1
  83. data/modules/mu/groomers/ansible.rb +69 -4
  84. data/modules/mu/groomers/chef.rb +51 -4
  85. data/modules/mu/logger.rb +120 -144
  86. data/modules/mu/master.rb +97 -4
  87. data/modules/mu/mommacat.rb +160 -874
  88. data/modules/mu/mommacat/daemon.rb +23 -14
  89. data/modules/mu/mommacat/naming.rb +110 -3
  90. data/modules/mu/mommacat/search.rb +497 -0
  91. data/modules/mu/mommacat/storage.rb +252 -194
  92. data/modules/mu/{clouds → providers}/README.md +1 -1
  93. data/modules/mu/{clouds → providers}/aws.rb +258 -57
  94. data/modules/mu/{clouds → providers}/aws/alarm.rb +3 -3
  95. data/modules/mu/{clouds → providers}/aws/bucket.rb +275 -41
  96. data/modules/mu/{clouds → providers}/aws/cache_cluster.rb +14 -50
  97. data/modules/mu/providers/aws/cdn.rb +782 -0
  98. data/modules/mu/{clouds → providers}/aws/collection.rb +5 -5
  99. data/modules/mu/{clouds → providers}/aws/container_cluster.rb +95 -84
  100. data/modules/mu/providers/aws/database.rb +1744 -0
  101. data/modules/mu/{clouds → providers}/aws/dnszone.rb +26 -12
  102. data/modules/mu/providers/aws/endpoint.rb +1072 -0
  103. data/modules/mu/{clouds → providers}/aws/firewall_rule.rb +39 -32
  104. data/modules/mu/{clouds → providers}/aws/folder.rb +1 -1
  105. data/modules/mu/{clouds → providers}/aws/function.rb +289 -134
  106. data/modules/mu/{clouds → providers}/aws/group.rb +18 -20
  107. data/modules/mu/{clouds → providers}/aws/habitat.rb +3 -3
  108. data/modules/mu/providers/aws/job.rb +466 -0
  109. data/modules/mu/{clouds → providers}/aws/loadbalancer.rb +77 -47
  110. data/modules/mu/{clouds → providers}/aws/log.rb +5 -5
  111. data/modules/mu/{clouds → providers}/aws/msg_queue.rb +14 -11
  112. data/modules/mu/{clouds → providers}/aws/nosqldb.rb +96 -5
  113. data/modules/mu/{clouds → providers}/aws/notifier.rb +135 -63
  114. data/modules/mu/{clouds → providers}/aws/role.rb +76 -48
  115. data/modules/mu/{clouds → providers}/aws/search_domain.rb +172 -41
  116. data/modules/mu/{clouds → providers}/aws/server.rb +66 -98
  117. data/modules/mu/{clouds → providers}/aws/server_pool.rb +42 -60
  118. data/modules/mu/{clouds → providers}/aws/storage_pool.rb +21 -38
  119. data/modules/mu/{clouds → providers}/aws/user.rb +12 -16
  120. data/modules/mu/{clouds → providers}/aws/userdata/README.md +0 -0
  121. data/modules/mu/{clouds → providers}/aws/userdata/linux.erb +5 -4
  122. data/modules/mu/{clouds → providers}/aws/userdata/windows.erb +0 -0
  123. data/modules/mu/{clouds → providers}/aws/vpc.rb +143 -74
  124. data/modules/mu/{clouds → providers}/aws/vpc_subnet.rb +0 -0
  125. data/modules/mu/{clouds → providers}/azure.rb +13 -0
  126. data/modules/mu/{clouds → providers}/azure/container_cluster.rb +1 -5
  127. data/modules/mu/{clouds → providers}/azure/firewall_rule.rb +8 -1
  128. data/modules/mu/{clouds → providers}/azure/habitat.rb +0 -0
  129. data/modules/mu/{clouds → providers}/azure/loadbalancer.rb +0 -0
  130. data/modules/mu/{clouds → providers}/azure/role.rb +0 -0
  131. data/modules/mu/{clouds → providers}/azure/server.rb +32 -24
  132. data/modules/mu/{clouds → providers}/azure/user.rb +1 -1
  133. data/modules/mu/{clouds → providers}/azure/userdata/README.md +0 -0
  134. data/modules/mu/{clouds → providers}/azure/userdata/linux.erb +0 -0
  135. data/modules/mu/{clouds → providers}/azure/userdata/windows.erb +0 -0
  136. data/modules/mu/{clouds → providers}/azure/vpc.rb +4 -6
  137. data/modules/mu/{clouds → providers}/cloudformation.rb +10 -0
  138. data/modules/mu/{clouds → providers}/cloudformation/alarm.rb +3 -3
  139. data/modules/mu/{clouds → providers}/cloudformation/cache_cluster.rb +3 -3
  140. data/modules/mu/{clouds → providers}/cloudformation/collection.rb +3 -3
  141. data/modules/mu/{clouds → providers}/cloudformation/database.rb +6 -17
  142. data/modules/mu/{clouds → providers}/cloudformation/dnszone.rb +3 -3
  143. data/modules/mu/{clouds → providers}/cloudformation/firewall_rule.rb +3 -3
  144. data/modules/mu/{clouds → providers}/cloudformation/loadbalancer.rb +3 -3
  145. data/modules/mu/{clouds → providers}/cloudformation/log.rb +3 -3
  146. data/modules/mu/{clouds → providers}/cloudformation/server.rb +7 -7
  147. data/modules/mu/{clouds → providers}/cloudformation/server_pool.rb +5 -5
  148. data/modules/mu/{clouds → providers}/cloudformation/vpc.rb +3 -3
  149. data/modules/mu/{clouds → providers}/docker.rb +0 -0
  150. data/modules/mu/{clouds → providers}/google.rb +29 -6
  151. data/modules/mu/{clouds → providers}/google/bucket.rb +4 -4
  152. data/modules/mu/{clouds → providers}/google/container_cluster.rb +38 -20
  153. data/modules/mu/{clouds → providers}/google/database.rb +5 -12
  154. data/modules/mu/{clouds → providers}/google/firewall_rule.rb +5 -5
  155. data/modules/mu/{clouds → providers}/google/folder.rb +5 -9
  156. data/modules/mu/{clouds → providers}/google/function.rb +6 -6
  157. data/modules/mu/{clouds → providers}/google/group.rb +9 -17
  158. data/modules/mu/{clouds → providers}/google/habitat.rb +4 -8
  159. data/modules/mu/{clouds → providers}/google/loadbalancer.rb +5 -5
  160. data/modules/mu/{clouds → providers}/google/role.rb +50 -31
  161. data/modules/mu/{clouds → providers}/google/server.rb +41 -24
  162. data/modules/mu/{clouds → providers}/google/server_pool.rb +14 -14
  163. data/modules/mu/{clouds → providers}/google/user.rb +34 -24
  164. data/modules/mu/{clouds → providers}/google/userdata/README.md +0 -0
  165. data/modules/mu/{clouds → providers}/google/userdata/linux.erb +0 -0
  166. data/modules/mu/{clouds → providers}/google/userdata/windows.erb +0 -0
  167. data/modules/mu/{clouds → providers}/google/vpc.rb +45 -14
  168. data/modules/tests/aws-jobs-functions.yaml +46 -0
  169. data/modules/tests/centos6.yaml +15 -0
  170. data/modules/tests/centos7.yaml +15 -0
  171. data/modules/tests/centos8.yaml +12 -0
  172. data/modules/tests/ecs.yaml +2 -2
  173. data/modules/tests/eks.yaml +1 -1
  174. data/modules/tests/functions/node-function/lambda_function.js +10 -0
  175. data/modules/tests/functions/python-function/lambda_function.py +12 -0
  176. data/modules/tests/microservice_app.yaml +288 -0
  177. data/modules/tests/rds.yaml +108 -0
  178. data/modules/tests/regrooms/rds.yaml +123 -0
  179. data/modules/tests/server-with-scrub-muisms.yaml +1 -1
  180. data/modules/tests/super_complex_bok.yml +2 -2
  181. data/modules/tests/super_simple_bok.yml +3 -5
  182. data/spec/mu/clouds/azure_spec.rb +2 -2
  183. metadata +122 -92
  184. data/modules/mu/clouds/aws/database.rb +0 -1974
  185. 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
@@ -33,6 +33,18 @@ module MU
33
33
  module AdditionalResourceMethods
34
34
  end
35
35
 
36
+ # Is this a "real" cloud provider, or a stub like CloudFormation?
37
+ def self.virtual?
38
+ false
39
+ end
40
+
41
+ # List all AWS projects available to our credentials
42
+ def self.listHabitats(credentials = nil, use_cache: true)
43
+ cfg = credConfig(credentials)
44
+ return [] if !cfg or !cfg['account_number']
45
+ [cfg['account_number']]
46
+ end
47
+
36
48
  # A hook that is always called just before any of the instance method of
37
49
  # our resource implementations gets invoked, so that we can ensure that
38
50
  # repetitive setup tasks (like resolving +:resource_group+ for Azure
@@ -344,6 +356,27 @@ end
344
356
  # etc)
345
357
  # @param deploy_id [MU::MommaCat]
346
358
  def self.cleanDeploy(deploy_id, credentials: nil, noop: false)
359
+
360
+ if !noop
361
+ MU.log "Deleting s3://#{adminBucketName(credentials)}/#{deploy_id}-secret"
362
+ MU::Cloud::AWS.s3(credentials: credentials).delete_object(
363
+ bucket: adminBucketName(credentials),
364
+ key: "#{deploy_id}-secret"
365
+ )
366
+ listRegions(credentials: credentials).each { |r|
367
+ resp = MU::Cloud::AWS.ec2(region: r, credentials: credentials).describe_key_pairs(
368
+ filters: [{name: "key-name", values: ["deploy-#{MU.deploy_id}"]}]
369
+ )
370
+ resp.data.key_pairs.each { |keypair|
371
+ MU.log "Deleting key pair #{keypair.key_name} from #{r}"
372
+ MU::Cloud::AWS.ec2(region: r, credentials: credentials).delete_key_pair(key_name: keypair.key_name) if !noop
373
+ }
374
+ }
375
+
376
+ end
377
+ if hosted?
378
+ MU::Cloud::AWS.openFirewallForClients
379
+ end
347
380
  end
348
381
 
349
382
  # Plant a Mu deploy secret into a storage bucket somewhere for so our kittens can consume it
@@ -772,50 +805,47 @@ end
772
805
 
773
806
  @@instance_types ||= {}
774
807
  @@instance_types[region] ||= {}
775
- next_token = nil
776
808
 
777
- begin
778
- # Pricing API isn't widely available, so ask a region we know supports
779
- # it
780
- resp = MU::Cloud::AWS.pricing(region: "us-east-1").get_products(
781
- service_code: "AmazonEC2",
782
- filters: [
783
- {
784
- field: "productFamily",
785
- value: "Compute Instance",
786
- type: "TERM_MATCH"
787
- },
788
- {
789
- field: "tenancy",
790
- value: "Shared",
791
- type: "TERM_MATCH"
792
- },
793
- {
794
- field: "location",
795
- value: human_region,
796
- type: "TERM_MATCH"
797
- }
798
- ],
799
- next_token: next_token
800
- )
801
- resp.price_list.each { |pricing|
802
- data = JSON.parse(pricing)
803
- type = data["product"]["attributes"]["instanceType"]
804
- next if @@instance_types[region].has_key?(type)
805
- @@instance_types[region][type] = {}
806
- ["ecu", "vcpu", "memory", "storage"].each { |a|
807
- @@instance_types[region][type][a] = data["product"]["attributes"][a]
809
+ # Pricing API isn't widely available, so ask a region we know supports
810
+ # it
811
+ resp = MU::Cloud::AWS.pricing(region: "us-east-1").get_products(
812
+ service_code: "AmazonEC2",
813
+ filters: [
814
+ {
815
+ field: "productFamily",
816
+ value: "Compute Instance",
817
+ type: "TERM_MATCH"
818
+ },
819
+ {
820
+ field: "tenancy",
821
+ value: "Shared",
822
+ type: "TERM_MATCH"
823
+ },
824
+ {
825
+ field: "location",
826
+ value: human_region,
827
+ type: "TERM_MATCH"
808
828
  }
809
- @@instance_types[region][type]["memory"].sub!(/ GiB/, "")
810
- @@instance_types[region][type]["memory"] = @@instance_types[region][type]["memory"].to_f
811
- @@instance_types[region][type]["vcpu"] = @@instance_types[region][type]["vcpu"].to_f
829
+ ]
830
+ )
831
+ resp.price_list.each { |pricing|
832
+ data = JSON.parse(pricing)
833
+ type = data["product"]["attributes"]["instanceType"]
834
+ next if @@instance_types[region].has_key?(type)
835
+ @@instance_types[region][type] = {}
836
+ ["ecu", "vcpu", "memory", "storage"].each { |a|
837
+ @@instance_types[region][type][a] = data["product"]["attributes"][a]
812
838
  }
813
- next_token = resp.next_token
814
- end while resp and next_token
839
+ @@instance_types[region][type]["memory"].sub!(/ GiB/, "")
840
+ @@instance_types[region][type]["memory"] = @@instance_types[region][type]["memory"].to_f
841
+ @@instance_types[region][type]["vcpu"] = @@instance_types[region][type]["vcpu"].to_f
842
+ }
815
843
 
816
844
  @@instance_types
817
845
  end
818
846
 
847
+ @@certificates = {}
848
+
819
849
  # AWS can stash API-available certificates in Amazon Certificate Manager
820
850
  # or in IAM. Rather than make people crazy trying to get the syntax
821
851
  # correct in our Baskets of Kittens, let's have a helper that tries to do
@@ -824,21 +854,24 @@ end
824
854
  # @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.
825
855
  # @param id [String]: The ARN of a known certificate. We just validate that it exists. This is ignored if a name parameter is supplied.
826
856
  # @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.
827
- def self.findSSLCertificate(name: nil, id: nil, region: myRegion)
828
- if name.nil? and name.empty? and id.nil? and id.empty?
857
+ def self.findSSLCertificate(name: nil, id: nil, region: myRegion, credentials: nil, raise_on_missing: true)
858
+ if (name.nil? or name.empty?) and (id.nil? or id.empty?)
829
859
  raise MuError, "Can't call findSSLCertificate without specifying either a name or an id"
830
860
  end
861
+ if id and @@certificates[id]
862
+ return [id, @@certificates[id]]
863
+ end
831
864
 
832
865
  if !name.nil? and !name.empty?
833
866
  matches = []
834
- acmcerts = MU::Cloud::AWS.acm(region: region).list_certificates(
867
+ acmcerts = MU::Cloud::AWS.acm(region: region, credentials: credentials).list_certificates(
835
868
  certificate_statuses: ["ISSUED"]
836
869
  )
837
870
  acmcerts.certificate_summary_list.each { |cert|
838
871
  matches << cert.certificate_arn if cert.domain_name == name
839
872
  }
840
873
  begin
841
- iamcert = MU::Cloud::AWS.iam.get_server_certificate(
874
+ iamcert = MU::Cloud::AWS.iam(credentials: credentials).get_server_certificate(
842
875
  server_certificate_name: name
843
876
  )
844
877
  rescue Aws::IAM::Errors::ValidationError, Aws::IAM::Errors::NoSuchEntity
@@ -848,32 +881,45 @@ end
848
881
  matches << iamcert.server_certificate.server_certificate_metadata.arn
849
882
  end
850
883
  if matches.size == 1
851
- return matches.first
884
+ id = matches.first
852
885
  elsif matches.size == 0
853
- raise MuError, "No IAM or ACM certificate named #{name} was found in #{region}"
886
+ if raise_on_missing
887
+ raise MuError, "No IAM or ACM certificate named #{name} was found in #{region}"
888
+ else
889
+ return nil
890
+ end
854
891
  elsif matches.size > 1
855
892
  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."
856
893
  end
857
894
  end
858
895
 
896
+ domains = []
897
+
859
898
  if id.match(/^arn:aws(?:-us-gov)?:acm/)
860
- resp = MU::Cloud::AWS.acm(region: region).get_certificate(
899
+ resp = MU::Cloud::AWS.acm(region: region).describe_certificate(
861
900
  certificate_arn: id
862
901
  )
863
- if resp.nil?
902
+
903
+ if resp.nil? or resp.certificate.nil?
864
904
  raise MuError, "No such ACM certificate '#{id}'"
865
905
  end
906
+ domains << resp.certificate.domain_name
907
+ if resp.certificate.subject_alternative_names
908
+ domains.concat(resp.certificate.subject_alternative_names)
909
+ end
866
910
  elsif id.match(/^arn:aws(?:-us-gov)?:iam/)
867
911
  resp = MU::Cloud::AWS.iam.list_server_certificates
868
912
  if resp.nil?
869
913
  raise MuError, "No such IAM certificate '#{id}'"
870
914
  end
871
915
  resp.server_certificate_metadata_list.each { |cert|
916
+
872
917
  if cert.arn == id
873
918
  if cert.expiration < Time.now
874
919
  MU.log "IAM SSL certificate #{cert.server_certificate_name} (#{id}) is EXPIRED", MU::WARN
875
920
  end
876
- return id
921
+ @@certificates[id] = [cert.server_certificate_name]
922
+ return [id, [cert.server_certificate_name]]
877
923
  end
878
924
  }
879
925
  raise MuError, "No such IAM certificate '#{id}'"
@@ -881,7 +927,56 @@ end
881
927
  raise MuError, "The format of '#{id}' doesn't look like an ARN for either Amazon Certificate Manager or IAM"
882
928
  end
883
929
 
884
- id
930
+ @@certificates[id] = domains.uniq
931
+ [id, domains.uniq]
932
+ end
933
+
934
+ # Given a domain name and an ACM or IAM certificate identifier, sort out
935
+ # whether the domain name is "covered" by the certificate
936
+ # @param name [String]
937
+ # @param cert_id [String]
938
+ # @return [Boolean]
939
+ def self.nameMatchesCertificate(name, cert_id)
940
+ _id, domains = findSSLCertificate(id: cert_id)
941
+ return false if !domains
942
+ domains.each { |dom|
943
+ if dom == name or
944
+ (dom =~ /^\*/ and name =~ /.*#{Regexp.quote(dom[1..-1])}/)
945
+ return true
946
+ end
947
+ }
948
+ false
949
+ end
950
+
951
+ # Given a {MU::Config::Ref} block for an IAM or ACM SSL certificate,
952
+ # look up and validate the specified certificate. This is intended to be
953
+ # invoked from resource implementations' +validateConfig+ methods.
954
+ # @param certblock [Hash,MU::Config::Ref]:
955
+ # @param region [String]: Default region to use when looking up the certificate, if its configuration block does not specify any
956
+ # @param credentials [String]: Default credentials to use when looking up the certificate, if its configuration block does not specify any
957
+ # @return [Boolean]
958
+ def self.resolveSSLCertificate(certblock, region: nil, credentials: nil)
959
+ return false if !certblock
960
+ ok = true
961
+
962
+ certblock['region'] ||= region if !certblock['id']
963
+ certblock['credentials'] ||= credentials
964
+ cert_arn, cert_domains = MU::Cloud::AWS.findSSLCertificate(
965
+ name: certblock["name"],
966
+ id: certblock["id"],
967
+ region: certblock['region'],
968
+ credentials: certblock['credentials']
969
+ )
970
+
971
+ if cert_arn
972
+ certblock['id'] ||= cert_arn
973
+ end
974
+
975
+ ['region', 'credentials'].each { |field|
976
+ certblock.delete(field) if certblock[field].nil?
977
+ }
978
+
979
+ [cert_arn, cert_domains]
885
980
  end
886
981
 
887
982
  # Amazon Certificate Manager API
@@ -1001,6 +1096,14 @@ end
1001
1096
  @@cloudwatchlogs_api[credentials][region]
1002
1097
  end
1003
1098
 
1099
+ # Amazon's CloudWatchEvents API
1100
+ def self.cloudwatchevents(region: MU.curRegion, credentials: nil)
1101
+ region ||= myRegion
1102
+ @@cloudwatchevents_api[credentials] ||= {}
1103
+ @@cloudwatchevents_api[credentials][region] ||= MU::Cloud::AWS::AmazonEndpoint.new(api: "CloudWatchEvents", region: region, credentials: credentials)
1104
+ @@cloudwatchevents_api[credentials][region]
1105
+ end
1106
+
1004
1107
  # Amazon's CloudFront API
1005
1108
  def self.cloudfront(region: MU.curRegion, credentials: nil)
1006
1109
  region ||= myRegion
@@ -1089,6 +1192,14 @@ end
1089
1192
  @@dynamo_api[credentials][region]
1090
1193
  end
1091
1194
 
1195
+ # Amazon's DynamoStream API
1196
+ def self.dynamostream(region: MU.curRegion, credentials: nil)
1197
+ region ||= myRegion
1198
+ @@dynamostream_api[credentials] ||= {}
1199
+ @@dynamostream_api[credentials][region] ||= MU::Cloud::AWS::AmazonEndpoint.new(api: "DynamoDBStreams", region: region, credentials: credentials)
1200
+ @@dynamostream_api[credentials][region]
1201
+ end
1202
+
1092
1203
  # Amazon's Pricing API
1093
1204
  def self.pricing(region: MU.curRegion, credentials: nil)
1094
1205
  region ||= myRegion
@@ -1137,6 +1248,14 @@ end
1137
1248
  @@kms_api[credentials][region]
1138
1249
  end
1139
1250
 
1251
+ # Amazon's CloudFront API
1252
+ def self.cloudfront(region: MU.curRegion, credentials: nil)
1253
+ region ||= myRegion
1254
+ @@cloudfront_api[credentials] ||= {}
1255
+ @@cloudfront_api[credentials][region] ||= MU::Cloud::AWS::AmazonEndpoint.new(api: "CloudFront", region: region, credentials: credentials)
1256
+ @@cloudfront_api[credentials][region]
1257
+ end
1258
+
1140
1259
  # Amazon's Organizations API
1141
1260
  def self.orgs(credentials: nil)
1142
1261
  @@organizations_api ||= {}
@@ -1218,7 +1337,7 @@ end
1218
1337
  # Mu Master, if we're in AWS.
1219
1338
  # @return [void]
1220
1339
  def self.openFirewallForClients
1221
- MU::Cloud.loadCloudType("AWS", :FirewallRule)
1340
+ MU::Cloud.resourceClass("AWS", :FirewallRule)
1222
1341
  begin
1223
1342
  if File.exist?(Etc.getpwuid(Process.uid).dir+"/.chef/knife.rb")
1224
1343
  ::Chef::Config.from_file(Etc.getpwuid(Process.uid).dir+"/.chef/knife.rb")
@@ -1394,7 +1513,7 @@ end
1394
1513
  # Create an AWS API client
1395
1514
  # @param region [String]: Amazon region so we know what endpoint to use
1396
1515
  # @param api [String]: Which API are we wrapping?
1397
- def initialize(region: MU.curRegion, api: "EC2", credentials: nil)
1516
+ def initialize(region: nil, api: "EC2", credentials: nil)
1398
1517
  @cred_obj = MU::Cloud::AWS.loadCredentials(credentials)
1399
1518
  @credentials = MU::Cloud::AWS.credConfig(credentials, name_only: true)
1400
1519
 
@@ -1403,6 +1522,8 @@ end
1403
1522
  end
1404
1523
 
1405
1524
  params = {}
1525
+ region ||= MU::Cloud::AWS.credConfig(credentials)['region']
1526
+ region ||= MU.myRegion
1406
1527
 
1407
1528
  if region
1408
1529
  @region = region
@@ -1419,21 +1540,98 @@ end
1419
1540
  # Catch-all for AWS client methods. Essentially a pass-through with some
1420
1541
  # rescues for known silly endpoint behavior.
1421
1542
  def method_missing(method_sym, *arguments)
1543
+ # make sure error symbols are loaded for our exception handling later
1422
1544
  require "aws-sdk-core"
1545
+ require "aws-sdk-core/rds"
1546
+ require "aws-sdk-core/ec2"
1547
+ require "aws-sdk-core/route53"
1548
+ require "aws-sdk-core/iam"
1549
+ require "aws-sdk-core/efs"
1550
+ require "aws-sdk-core/pricing"
1551
+ require "aws-sdk-core/apigateway"
1552
+ require "aws-sdk-core/ecs"
1553
+ require "aws-sdk-core/eks"
1554
+ require "aws-sdk-core/cloudwatchlogs"
1555
+ require "aws-sdk-core/cloudwatchevents"
1556
+ require "aws-sdk-core/elasticloadbalancing"
1557
+ require "aws-sdk-core/elasticloadbalancingv2"
1558
+ require "aws-sdk-core/autoscaling"
1559
+ require "aws-sdk-core/client_waiters"
1560
+ require "aws-sdk-core/waiters/errors"
1423
1561
 
1424
1562
  retries = 0
1425
1563
  begin
1426
1564
  MU.log "Calling #{method_sym} in #{@region}", MU::DEBUG, details: arguments
1427
- retval = nil
1428
- if !arguments.nil? and arguments.size == 1
1429
- retval = @api.method(method_sym).call(arguments[0])
1430
- elsif !arguments.nil? and arguments.size > 0
1431
- retval = @api.method(method_sym).call(*arguments)
1432
- else
1433
- retval = @api.method(method_sym).call
1565
+
1566
+ retval = if !arguments.nil? and arguments.size == 1
1567
+ @api.method(method_sym).call(arguments[0])
1568
+ elsif !arguments.nil? and arguments.size > 0
1569
+ @api.method(method_sym).call(*arguments)
1570
+ else
1571
+ @api.method(method_sym).call
1572
+ end
1573
+
1574
+ if !retval.nil?
1575
+ begin
1576
+ page_markers = {
1577
+ :marker => :marker,
1578
+ :next_token => :next_token,
1579
+ :next_marker => :marker
1580
+ }
1581
+ paginator = nil
1582
+ new_page = nil
1583
+ page_markers.each_key { |m|
1584
+ if !retval.nil? and retval.respond_to?(m)
1585
+ paginator = m
1586
+ new_page = retval.send(m)
1587
+ break
1588
+ end
1589
+ }
1590
+
1591
+ if paginator and new_page and !new_page.empty?
1592
+ resp = retval.respond_to?(:__getobj__) ? retval.__getobj__ : retval
1593
+ concat_to = resp.class.instance_methods(false).reject { |m|
1594
+ m.to_s.match(/=$/) or m == paginator or resp.send(m).nil? or !resp.send(m).is_a?(Array)
1595
+ }
1596
+ if concat_to.size != 1
1597
+ MU.log "Tried to figure out where I might append paginated results for a #{resp.class.name}, but failed", MU::DEBUG, details: concat_to
1598
+ else
1599
+ concat_to = concat_to.first
1600
+ new_args = arguments ? arguments.dup : [{}]
1601
+ begin
1602
+ if new_args.is_a?(Array)
1603
+ new_args << {} if new_args.empty?
1604
+ if new_args.size == 1 and new_args.first.is_a?(Hash)
1605
+ new_args[0][page_markers[paginator]] = new_page
1606
+ else
1607
+ MU.log "I don't know how to insert a #{paginator} into these arguments for #{method_sym}", MU::WARN, details: new_args
1608
+ end
1609
+ elsif new_args.is_a?(Hash)
1610
+ new_args[page_markers[paginator]] = new_page
1611
+ end
1612
+
1613
+ MU.log "Attempting magic pagination for #{method_sym}", MU::DEBUG, details: new_args
1614
+
1615
+ # resp = if !arguments.nil? and arguments.size == 1
1616
+ # @api.method(method_sym).call(new_args[0])
1617
+ # elsif !arguments.nil? and arguments.size > 0
1618
+ resp = @api.method(method_sym).call(*new_args)
1619
+ # end
1620
+ break if resp.nil?
1621
+ resp = resp.__getobj__ if resp.respond_to?(:__getobj__)
1622
+ retval.send(concat_to).concat(resp.send(concat_to))
1623
+ new_page = resp.send(paginator) if !resp.nil?
1624
+ end while !resp.nil? and !new_page.nil? and !new_page.empty?
1625
+ end
1626
+ end
1627
+ rescue StandardError => e
1628
+ MU.log "Made a good-faith effort to auto-paginate API call to #{method_sym} and failed with #{e.message}", MU::DEBUG, details: arguments
1629
+ raise e
1630
+ end
1434
1631
  end
1632
+
1435
1633
  return retval
1436
- 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
1634
+ 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
1437
1635
  if e.class.name == "Seahorse::Client::NetworkingError" and e.message.match(/Name or service not known/)
1438
1636
  MU.log e.inspect, MU::ERR
1439
1637
  raise e
@@ -1475,6 +1673,7 @@ end
1475
1673
  @@wafglobal = {}
1476
1674
  @@waf = {}
1477
1675
  @@cloudwatchlogs_api = {}
1676
+ @@cloudwatchevents_api = {}
1478
1677
  @@cloudfront_api = {}
1479
1678
  @@elasticache_api = {}
1480
1679
  @@sns_api = {}
@@ -1493,6 +1692,8 @@ end
1493
1692
  @@kms_api ={}
1494
1693
  @@organization_api ={}
1495
1694
  @@dynamo_api ={}
1695
+ @@dynamostream_api ={}
1696
+ @@cloudfront_api ={}
1496
1697
  end
1497
1698
  end
1498
1699
  end