cloud-mu 3.1.4 → 3.3.1

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 (203) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +5 -1
  3. data/ansible/roles/mu-windows/README.md +33 -0
  4. data/ansible/roles/mu-windows/defaults/main.yml +2 -0
  5. data/ansible/roles/mu-windows/files/LaunchConfig.json +9 -0
  6. data/ansible/roles/mu-windows/files/config.xml +76 -0
  7. data/ansible/roles/mu-windows/handlers/main.yml +2 -0
  8. data/ansible/roles/mu-windows/meta/main.yml +53 -0
  9. data/ansible/roles/mu-windows/tasks/main.yml +36 -0
  10. data/ansible/roles/mu-windows/tests/inventory +2 -0
  11. data/ansible/roles/mu-windows/tests/test.yml +5 -0
  12. data/ansible/roles/mu-windows/vars/main.yml +2 -0
  13. data/bin/mu-adopt +16 -12
  14. data/bin/mu-azure-tests +57 -0
  15. data/bin/mu-cleanup +2 -4
  16. data/bin/mu-configure +52 -0
  17. data/bin/mu-deploy +3 -3
  18. data/bin/mu-findstray-tests +25 -0
  19. data/bin/mu-gen-docs +2 -4
  20. data/bin/mu-load-config.rb +2 -1
  21. data/bin/mu-node-manage +15 -16
  22. data/bin/mu-run-tests +37 -12
  23. data/cloud-mu.gemspec +5 -3
  24. data/cookbooks/mu-activedirectory/resources/domain.rb +4 -4
  25. data/cookbooks/mu-activedirectory/resources/domain_controller.rb +4 -4
  26. data/cookbooks/mu-tools/libraries/helper.rb +1 -1
  27. data/cookbooks/mu-tools/recipes/apply_security.rb +14 -14
  28. data/cookbooks/mu-tools/recipes/aws_api.rb +9 -0
  29. data/cookbooks/mu-tools/recipes/eks.rb +2 -2
  30. data/cookbooks/mu-tools/recipes/selinux.rb +2 -1
  31. data/cookbooks/mu-tools/recipes/windows-client.rb +163 -164
  32. data/cookbooks/mu-tools/resources/windows_users.rb +44 -43
  33. data/extras/clean-stock-amis +25 -19
  34. data/extras/generate-stock-images +1 -0
  35. data/extras/image-generators/AWS/win2k12.yaml +18 -13
  36. data/extras/image-generators/AWS/win2k16.yaml +18 -13
  37. data/extras/image-generators/AWS/win2k19.yaml +21 -0
  38. data/modules/mommacat.ru +1 -1
  39. data/modules/mu.rb +158 -107
  40. data/modules/mu/adoption.rb +386 -59
  41. data/modules/mu/cleanup.rb +214 -303
  42. data/modules/mu/cloud.rb +128 -1632
  43. data/modules/mu/cloud/database.rb +49 -0
  44. data/modules/mu/cloud/dnszone.rb +44 -0
  45. data/modules/mu/cloud/machine_images.rb +212 -0
  46. data/modules/mu/cloud/providers.rb +81 -0
  47. data/modules/mu/cloud/resource_base.rb +926 -0
  48. data/modules/mu/cloud/server.rb +40 -0
  49. data/modules/mu/cloud/server_pool.rb +1 -0
  50. data/modules/mu/cloud/ssh_sessions.rb +228 -0
  51. data/modules/mu/cloud/winrm_sessions.rb +237 -0
  52. data/modules/mu/cloud/wrappers.rb +169 -0
  53. data/modules/mu/config.rb +135 -82
  54. data/modules/mu/config/alarm.rb +2 -6
  55. data/modules/mu/config/bucket.rb +32 -3
  56. data/modules/mu/config/cache_cluster.rb +2 -2
  57. data/modules/mu/config/cdn.rb +100 -0
  58. data/modules/mu/config/collection.rb +1 -1
  59. data/modules/mu/config/container_cluster.rb +7 -2
  60. data/modules/mu/config/database.rb +84 -105
  61. data/modules/mu/config/database.yml +1 -2
  62. data/modules/mu/config/dnszone.rb +5 -4
  63. data/modules/mu/config/doc_helpers.rb +5 -6
  64. data/modules/mu/config/endpoint.rb +2 -1
  65. data/modules/mu/config/firewall_rule.rb +3 -19
  66. data/modules/mu/config/folder.rb +1 -1
  67. data/modules/mu/config/function.rb +17 -8
  68. data/modules/mu/config/group.rb +1 -1
  69. data/modules/mu/config/habitat.rb +1 -1
  70. data/modules/mu/config/job.rb +89 -0
  71. data/modules/mu/config/loadbalancer.rb +57 -11
  72. data/modules/mu/config/log.rb +1 -1
  73. data/modules/mu/config/msg_queue.rb +1 -1
  74. data/modules/mu/config/nosqldb.rb +1 -1
  75. data/modules/mu/config/notifier.rb +8 -19
  76. data/modules/mu/config/ref.rb +92 -14
  77. data/modules/mu/config/role.rb +1 -1
  78. data/modules/mu/config/schema_helpers.rb +38 -37
  79. data/modules/mu/config/search_domain.rb +1 -1
  80. data/modules/mu/config/server.rb +12 -13
  81. data/modules/mu/config/server.yml +1 -0
  82. data/modules/mu/config/server_pool.rb +3 -7
  83. data/modules/mu/config/storage_pool.rb +1 -1
  84. data/modules/mu/config/tail.rb +11 -0
  85. data/modules/mu/config/user.rb +1 -1
  86. data/modules/mu/config/vpc.rb +27 -23
  87. data/modules/mu/config/vpc.yml +0 -1
  88. data/modules/mu/defaults/AWS.yaml +91 -68
  89. data/modules/mu/defaults/Azure.yaml +1 -0
  90. data/modules/mu/defaults/Google.yaml +1 -0
  91. data/modules/mu/deploy.rb +33 -19
  92. data/modules/mu/groomer.rb +16 -1
  93. data/modules/mu/groomers/ansible.rb +123 -21
  94. data/modules/mu/groomers/chef.rb +64 -11
  95. data/modules/mu/logger.rb +120 -144
  96. data/modules/mu/master.rb +97 -4
  97. data/modules/mu/master/ssl.rb +0 -1
  98. data/modules/mu/mommacat.rb +154 -867
  99. data/modules/mu/mommacat/daemon.rb +23 -14
  100. data/modules/mu/mommacat/naming.rb +110 -3
  101. data/modules/mu/mommacat/search.rb +495 -0
  102. data/modules/mu/mommacat/storage.rb +225 -192
  103. data/modules/mu/{clouds → providers}/README.md +1 -1
  104. data/modules/mu/{clouds → providers}/aws.rb +281 -64
  105. data/modules/mu/{clouds → providers}/aws/alarm.rb +3 -3
  106. data/modules/mu/{clouds → providers}/aws/bucket.rb +275 -41
  107. data/modules/mu/{clouds → providers}/aws/cache_cluster.rb +14 -50
  108. data/modules/mu/providers/aws/cdn.rb +782 -0
  109. data/modules/mu/{clouds → providers}/aws/collection.rb +5 -5
  110. data/modules/mu/{clouds → providers}/aws/container_cluster.rb +708 -749
  111. data/modules/mu/providers/aws/database.rb +1744 -0
  112. data/modules/mu/{clouds → providers}/aws/dnszone.rb +75 -57
  113. data/modules/mu/providers/aws/endpoint.rb +1072 -0
  114. data/modules/mu/{clouds → providers}/aws/firewall_rule.rb +212 -242
  115. data/modules/mu/{clouds → providers}/aws/folder.rb +1 -1
  116. data/modules/mu/{clouds → providers}/aws/function.rb +289 -134
  117. data/modules/mu/{clouds → providers}/aws/group.rb +18 -20
  118. data/modules/mu/{clouds → providers}/aws/habitat.rb +3 -3
  119. data/modules/mu/providers/aws/job.rb +466 -0
  120. data/modules/mu/{clouds → providers}/aws/loadbalancer.rb +50 -41
  121. data/modules/mu/{clouds → providers}/aws/log.rb +5 -5
  122. data/modules/mu/{clouds → providers}/aws/msg_queue.rb +14 -11
  123. data/modules/mu/{clouds → providers}/aws/nosqldb.rb +96 -5
  124. data/modules/mu/{clouds → providers}/aws/notifier.rb +135 -63
  125. data/modules/mu/{clouds → providers}/aws/role.rb +94 -57
  126. data/modules/mu/{clouds → providers}/aws/search_domain.rb +173 -42
  127. data/modules/mu/{clouds → providers}/aws/server.rb +782 -1107
  128. data/modules/mu/{clouds → providers}/aws/server_pool.rb +36 -46
  129. data/modules/mu/{clouds → providers}/aws/storage_pool.rb +21 -38
  130. data/modules/mu/{clouds → providers}/aws/user.rb +12 -16
  131. data/modules/mu/{clouds → providers}/aws/userdata/README.md +0 -0
  132. data/modules/mu/{clouds → providers}/aws/userdata/linux.erb +5 -4
  133. data/modules/mu/{clouds → providers}/aws/userdata/windows.erb +2 -1
  134. data/modules/mu/{clouds → providers}/aws/vpc.rb +429 -849
  135. data/modules/mu/providers/aws/vpc_subnet.rb +286 -0
  136. data/modules/mu/{clouds → providers}/azure.rb +13 -0
  137. data/modules/mu/{clouds → providers}/azure/container_cluster.rb +1 -5
  138. data/modules/mu/{clouds → providers}/azure/firewall_rule.rb +8 -1
  139. data/modules/mu/{clouds → providers}/azure/habitat.rb +0 -0
  140. data/modules/mu/{clouds → providers}/azure/loadbalancer.rb +0 -0
  141. data/modules/mu/{clouds → providers}/azure/role.rb +0 -0
  142. data/modules/mu/{clouds → providers}/azure/server.rb +32 -24
  143. data/modules/mu/{clouds → providers}/azure/user.rb +1 -1
  144. data/modules/mu/{clouds → providers}/azure/userdata/README.md +0 -0
  145. data/modules/mu/{clouds → providers}/azure/userdata/linux.erb +0 -0
  146. data/modules/mu/{clouds → providers}/azure/userdata/windows.erb +0 -0
  147. data/modules/mu/{clouds → providers}/azure/vpc.rb +4 -6
  148. data/modules/mu/{clouds → providers}/cloudformation.rb +10 -0
  149. data/modules/mu/{clouds → providers}/cloudformation/alarm.rb +3 -3
  150. data/modules/mu/{clouds → providers}/cloudformation/cache_cluster.rb +3 -3
  151. data/modules/mu/{clouds → providers}/cloudformation/collection.rb +3 -3
  152. data/modules/mu/{clouds → providers}/cloudformation/database.rb +6 -17
  153. data/modules/mu/{clouds → providers}/cloudformation/dnszone.rb +3 -3
  154. data/modules/mu/{clouds → providers}/cloudformation/firewall_rule.rb +3 -3
  155. data/modules/mu/{clouds → providers}/cloudformation/loadbalancer.rb +3 -3
  156. data/modules/mu/{clouds → providers}/cloudformation/log.rb +3 -3
  157. data/modules/mu/{clouds → providers}/cloudformation/server.rb +7 -7
  158. data/modules/mu/{clouds → providers}/cloudformation/server_pool.rb +5 -5
  159. data/modules/mu/{clouds → providers}/cloudformation/vpc.rb +3 -3
  160. data/modules/mu/{clouds → providers}/docker.rb +0 -0
  161. data/modules/mu/{clouds → providers}/google.rb +29 -6
  162. data/modules/mu/{clouds → providers}/google/bucket.rb +5 -5
  163. data/modules/mu/{clouds → providers}/google/container_cluster.rb +59 -37
  164. data/modules/mu/{clouds → providers}/google/database.rb +5 -12
  165. data/modules/mu/{clouds → providers}/google/firewall_rule.rb +5 -5
  166. data/modules/mu/{clouds → providers}/google/folder.rb +5 -9
  167. data/modules/mu/{clouds → providers}/google/function.rb +14 -8
  168. data/modules/mu/{clouds → providers}/google/group.rb +9 -17
  169. data/modules/mu/{clouds → providers}/google/habitat.rb +4 -8
  170. data/modules/mu/{clouds → providers}/google/loadbalancer.rb +5 -5
  171. data/modules/mu/{clouds → providers}/google/role.rb +50 -31
  172. data/modules/mu/{clouds → providers}/google/server.rb +142 -55
  173. data/modules/mu/{clouds → providers}/google/server_pool.rb +14 -14
  174. data/modules/mu/{clouds → providers}/google/user.rb +34 -24
  175. data/modules/mu/{clouds → providers}/google/userdata/README.md +0 -0
  176. data/modules/mu/{clouds → providers}/google/userdata/linux.erb +0 -0
  177. data/modules/mu/{clouds → providers}/google/userdata/windows.erb +0 -0
  178. data/modules/mu/{clouds → providers}/google/vpc.rb +46 -15
  179. data/modules/tests/aws-jobs-functions.yaml +46 -0
  180. data/modules/tests/centos6.yaml +15 -0
  181. data/modules/tests/centos7.yaml +15 -0
  182. data/modules/tests/centos8.yaml +12 -0
  183. data/modules/tests/ecs.yaml +23 -0
  184. data/modules/tests/eks.yaml +1 -1
  185. data/modules/tests/functions/node-function/lambda_function.js +10 -0
  186. data/modules/tests/functions/python-function/lambda_function.py +12 -0
  187. data/modules/tests/includes-and-params.yaml +2 -1
  188. data/modules/tests/microservice_app.yaml +288 -0
  189. data/modules/tests/rds.yaml +108 -0
  190. data/modules/tests/regrooms/rds.yaml +123 -0
  191. data/modules/tests/server-with-scrub-muisms.yaml +2 -1
  192. data/modules/tests/super_complex_bok.yml +2 -2
  193. data/modules/tests/super_simple_bok.yml +3 -5
  194. data/modules/tests/win2k12.yaml +25 -0
  195. data/modules/tests/win2k16.yaml +25 -0
  196. data/modules/tests/win2k19.yaml +25 -0
  197. data/requirements.txt +1 -0
  198. data/spec/mu/clouds/azure_spec.rb +2 -2
  199. metadata +169 -93
  200. data/extras/image-generators/AWS/windows.yaml +0 -18
  201. data/modules/mu/clouds/aws/database.rb +0 -1974
  202. data/modules/mu/clouds/aws/endpoint.rb +0 -596
  203. data/modules/tests/needwork/win2k12.yaml +0 -13
@@ -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
@@ -182,18 +194,34 @@ end
182
194
  end
183
195
  end
184
196
 
185
- # Tag a resource with all of our standard identifying tags.
197
+ # Tag an EC2 resource
186
198
  #
187
199
  # @param resource [String]: The cloud provider identifier of the resource to tag
188
200
  # @param region [String]: The cloud provider region
201
+ # @param credentials [String]: Credentials to authorize API requests
202
+ # @param optional [Boolean]: Whether to apply our optional generic tags
203
+ # @param nametag [String]: A +Name+ tag to apply
204
+ # @param othertags [Array<Hash>]: Miscellaneous custom tags, in Basket of Kittens style
189
205
  # @return [void]
190
- def self.createStandardTags(resource = nil, region: MU.curRegion, credentials: nil)
206
+ def self.createStandardTags(resource = nil, region: MU.curRegion, credentials: nil, optional: true, nametag: nil, othertags: nil)
191
207
  tags = []
192
208
  MU::MommaCat.listStandardTags.each_pair { |name, value|
193
- if !value.nil?
194
- tags << {key: name, value: value}
195
- end
209
+ tags << {key: name, value: value} if !value.nil?
196
210
  }
211
+ if optional
212
+ MU::MommaCat.listOptionalTags.each { |key, value|
213
+ tags << {key: name, value: value} if !value.nil?
214
+ }
215
+ end
216
+ if nametag
217
+ tags << { key: "Name", value: nametag }
218
+ end
219
+ if othertags
220
+ othertags.each { |tag|
221
+ tags << { key: tag['key'], value: tag['value'] }
222
+ }
223
+ end
224
+
197
225
  if MU::Cloud::CloudFormation.emitCloudFormation
198
226
  return tags
199
227
  end
@@ -215,6 +243,7 @@ end
215
243
  end
216
244
  end
217
245
  MU.log "Created standard tags for resource #{resource}", MU::DEBUG, details: caller
246
+
218
247
  end
219
248
 
220
249
  @@myVPCObj = nil
@@ -327,6 +356,27 @@ end
327
356
  # etc)
328
357
  # @param deploy_id [MU::MommaCat]
329
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
330
380
  end
331
381
 
332
382
  # Plant a Mu deploy secret into a storage bucket somewhere for so our kittens can consume it
@@ -406,7 +456,7 @@ end
406
456
  end
407
457
 
408
458
  begin
409
- Timeout.timeout(2) do
459
+ Timeout.timeout(4) do
410
460
  instance_id = open("http://169.254.169.254/latest/meta-data/instance-id").read
411
461
  if !instance_id.nil? and instance_id.size > 0
412
462
  @@is_in_aws = true
@@ -755,50 +805,47 @@ end
755
805
 
756
806
  @@instance_types ||= {}
757
807
  @@instance_types[region] ||= {}
758
- next_token = nil
759
808
 
760
- begin
761
- # Pricing API isn't widely available, so ask a region we know supports
762
- # it
763
- resp = MU::Cloud::AWS.pricing(region: "us-east-1").get_products(
764
- service_code: "AmazonEC2",
765
- filters: [
766
- {
767
- field: "productFamily",
768
- value: "Compute Instance",
769
- type: "TERM_MATCH"
770
- },
771
- {
772
- field: "tenancy",
773
- value: "Shared",
774
- type: "TERM_MATCH"
775
- },
776
- {
777
- field: "location",
778
- value: human_region,
779
- type: "TERM_MATCH"
780
- }
781
- ],
782
- next_token: next_token
783
- )
784
- resp.price_list.each { |pricing|
785
- data = JSON.parse(pricing)
786
- type = data["product"]["attributes"]["instanceType"]
787
- next if @@instance_types[region].has_key?(type)
788
- @@instance_types[region][type] = {}
789
- ["ecu", "vcpu", "memory", "storage"].each { |a|
790
- @@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"
791
828
  }
792
- @@instance_types[region][type]["memory"].sub!(/ GiB/, "")
793
- @@instance_types[region][type]["memory"] = @@instance_types[region][type]["memory"].to_f
794
- @@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]
795
838
  }
796
- next_token = resp.next_token
797
- 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
+ }
798
843
 
799
844
  @@instance_types
800
845
  end
801
846
 
847
+ @@certificates = {}
848
+
802
849
  # AWS can stash API-available certificates in Amazon Certificate Manager
803
850
  # or in IAM. Rather than make people crazy trying to get the syntax
804
851
  # correct in our Baskets of Kittens, let's have a helper that tries to do
@@ -807,21 +854,24 @@ end
807
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.
808
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.
809
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.
810
- def self.findSSLCertificate(name: nil, id: nil, region: myRegion)
811
- 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?)
812
859
  raise MuError, "Can't call findSSLCertificate without specifying either a name or an id"
813
860
  end
861
+ if id and @@certificates[id]
862
+ return [id, @@certificates[id]]
863
+ end
814
864
 
815
865
  if !name.nil? and !name.empty?
816
866
  matches = []
817
- acmcerts = MU::Cloud::AWS.acm(region: region).list_certificates(
867
+ acmcerts = MU::Cloud::AWS.acm(region: region, credentials: credentials).list_certificates(
818
868
  certificate_statuses: ["ISSUED"]
819
869
  )
820
870
  acmcerts.certificate_summary_list.each { |cert|
821
871
  matches << cert.certificate_arn if cert.domain_name == name
822
872
  }
823
873
  begin
824
- iamcert = MU::Cloud::AWS.iam.get_server_certificate(
874
+ iamcert = MU::Cloud::AWS.iam(credentials: credentials).get_server_certificate(
825
875
  server_certificate_name: name
826
876
  )
827
877
  rescue Aws::IAM::Errors::ValidationError, Aws::IAM::Errors::NoSuchEntity
@@ -831,32 +881,45 @@ end
831
881
  matches << iamcert.server_certificate.server_certificate_metadata.arn
832
882
  end
833
883
  if matches.size == 1
834
- return matches.first
884
+ id = matches.first
835
885
  elsif matches.size == 0
836
- 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
837
891
  elsif matches.size > 1
838
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."
839
893
  end
840
894
  end
841
895
 
896
+ domains = []
897
+
842
898
  if id.match(/^arn:aws(?:-us-gov)?:acm/)
843
- resp = MU::Cloud::AWS.acm(region: region).get_certificate(
899
+ resp = MU::Cloud::AWS.acm(region: region).describe_certificate(
844
900
  certificate_arn: id
845
901
  )
846
- if resp.nil?
902
+
903
+ if resp.nil? or resp.certificate.nil?
847
904
  raise MuError, "No such ACM certificate '#{id}'"
848
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
849
910
  elsif id.match(/^arn:aws(?:-us-gov)?:iam/)
850
911
  resp = MU::Cloud::AWS.iam.list_server_certificates
851
912
  if resp.nil?
852
913
  raise MuError, "No such IAM certificate '#{id}'"
853
914
  end
854
915
  resp.server_certificate_metadata_list.each { |cert|
916
+
855
917
  if cert.arn == id
856
918
  if cert.expiration < Time.now
857
919
  MU.log "IAM SSL certificate #{cert.server_certificate_name} (#{id}) is EXPIRED", MU::WARN
858
920
  end
859
- return id
921
+ @@certificates[id] = [cert.server_certificate_name]
922
+ return [id, [cert.server_certificate_name]]
860
923
  end
861
924
  }
862
925
  raise MuError, "No such IAM certificate '#{id}'"
@@ -864,7 +927,56 @@ end
864
927
  raise MuError, "The format of '#{id}' doesn't look like an ARN for either Amazon Certificate Manager or IAM"
865
928
  end
866
929
 
867
- 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]
868
980
  end
869
981
 
870
982
  # Amazon Certificate Manager API
@@ -984,6 +1096,14 @@ end
984
1096
  @@cloudwatchlogs_api[credentials][region]
985
1097
  end
986
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
+
987
1107
  # Amazon's CloudFront API
988
1108
  def self.cloudfront(region: MU.curRegion, credentials: nil)
989
1109
  region ||= myRegion
@@ -1072,6 +1192,14 @@ end
1072
1192
  @@dynamo_api[credentials][region]
1073
1193
  end
1074
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
+
1075
1203
  # Amazon's Pricing API
1076
1204
  def self.pricing(region: MU.curRegion, credentials: nil)
1077
1205
  region ||= myRegion
@@ -1120,6 +1248,14 @@ end
1120
1248
  @@kms_api[credentials][region]
1121
1249
  end
1122
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
+
1123
1259
  # Amazon's Organizations API
1124
1260
  def self.orgs(credentials: nil)
1125
1261
  @@organizations_api ||= {}
@@ -1150,7 +1286,6 @@ end
1150
1286
 
1151
1287
  # Tag a resource. Defaults to applying our MU deployment identifier, if no
1152
1288
  # arguments other than the resource identifier are given.
1153
- # XXX this belongs in the cloud layer(s)
1154
1289
  #
1155
1290
  # @param resource [String]: The cloud provider identifier of the resource to tag
1156
1291
  # @param tag_name [String]: The name of the tag to create
@@ -1202,7 +1337,7 @@ end
1202
1337
  # Mu Master, if we're in AWS.
1203
1338
  # @return [void]
1204
1339
  def self.openFirewallForClients
1205
- MU::Cloud.loadCloudType("AWS", :FirewallRule)
1340
+ MU::Cloud.resourceClass("AWS", :FirewallRule)
1206
1341
  begin
1207
1342
  if File.exist?(Etc.getpwuid(Process.uid).dir+"/.chef/knife.rb")
1208
1343
  ::Chef::Config.from_file(Etc.getpwuid(Process.uid).dir+"/.chef/knife.rb")
@@ -1378,7 +1513,7 @@ end
1378
1513
  # Create an AWS API client
1379
1514
  # @param region [String]: Amazon region so we know what endpoint to use
1380
1515
  # @param api [String]: Which API are we wrapping?
1381
- def initialize(region: MU.curRegion, api: "EC2", credentials: nil)
1516
+ def initialize(region: nil, api: "EC2", credentials: nil)
1382
1517
  @cred_obj = MU::Cloud::AWS.loadCredentials(credentials)
1383
1518
  @credentials = MU::Cloud::AWS.credConfig(credentials, name_only: true)
1384
1519
 
@@ -1387,6 +1522,8 @@ end
1387
1522
  end
1388
1523
 
1389
1524
  params = {}
1525
+ region ||= MU::Cloud::AWS.credConfig(credentials)['region']
1526
+ region ||= MU.myRegion
1390
1527
 
1391
1528
  if region
1392
1529
  @region = region
@@ -1403,21 +1540,98 @@ end
1403
1540
  # Catch-all for AWS client methods. Essentially a pass-through with some
1404
1541
  # rescues for known silly endpoint behavior.
1405
1542
  def method_missing(method_sym, *arguments)
1543
+ # make sure error symbols are loaded for our exception handling later
1406
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"
1407
1561
 
1408
1562
  retries = 0
1409
1563
  begin
1410
1564
  MU.log "Calling #{method_sym} in #{@region}", MU::DEBUG, details: arguments
1411
- retval = nil
1412
- if !arguments.nil? and arguments.size == 1
1413
- retval = @api.method(method_sym).call(arguments[0])
1414
- elsif !arguments.nil? and arguments.size > 0
1415
- retval = @api.method(method_sym).call(*arguments)
1416
- else
1417
- 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
1418
1631
  end
1632
+
1419
1633
  return retval
1420
- 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
1421
1635
  if e.class.name == "Seahorse::Client::NetworkingError" and e.message.match(/Name or service not known/)
1422
1636
  MU.log e.inspect, MU::ERR
1423
1637
  raise e
@@ -1459,6 +1673,7 @@ end
1459
1673
  @@wafglobal = {}
1460
1674
  @@waf = {}
1461
1675
  @@cloudwatchlogs_api = {}
1676
+ @@cloudwatchevents_api = {}
1462
1677
  @@cloudfront_api = {}
1463
1678
  @@elasticache_api = {}
1464
1679
  @@sns_api = {}
@@ -1477,6 +1692,8 @@ end
1477
1692
  @@kms_api ={}
1478
1693
  @@organization_api ={}
1479
1694
  @@dynamo_api ={}
1695
+ @@dynamostream_api ={}
1696
+ @@cloudfront_api ={}
1480
1697
  end
1481
1698
  end
1482
1699
  end