cloud-mu 3.1.3 → 3.3.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 (212) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +15 -3
  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 +21 -13
  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 +4 -4
  21. data/bin/mu-node-manage +15 -16
  22. data/bin/mu-run-tests +147 -37
  23. data/cloud-mu.gemspec +22 -20
  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 +3 -2
  27. data/cookbooks/mu-tools/libraries/monkey.rb +35 -0
  28. data/cookbooks/mu-tools/recipes/apply_security.rb +14 -14
  29. data/cookbooks/mu-tools/recipes/aws_api.rb +9 -0
  30. data/cookbooks/mu-tools/recipes/eks.rb +2 -2
  31. data/cookbooks/mu-tools/recipes/google_api.rb +2 -2
  32. data/cookbooks/mu-tools/recipes/selinux.rb +2 -1
  33. data/cookbooks/mu-tools/recipes/windows-client.rb +163 -164
  34. data/cookbooks/mu-tools/resources/disk.rb +1 -1
  35. data/cookbooks/mu-tools/resources/windows_users.rb +44 -43
  36. data/extras/clean-stock-amis +25 -19
  37. data/extras/generate-stock-images +1 -0
  38. data/extras/image-generators/AWS/win2k12.yaml +18 -13
  39. data/extras/image-generators/AWS/win2k16.yaml +18 -13
  40. data/extras/image-generators/AWS/win2k19.yaml +21 -0
  41. data/extras/image-generators/Google/centos6.yaml +1 -0
  42. data/extras/image-generators/Google/centos7.yaml +1 -1
  43. data/modules/mommacat.ru +6 -16
  44. data/modules/mu.rb +158 -111
  45. data/modules/mu/adoption.rb +404 -71
  46. data/modules/mu/cleanup.rb +221 -306
  47. data/modules/mu/cloud.rb +129 -1633
  48. data/modules/mu/cloud/database.rb +49 -0
  49. data/modules/mu/cloud/dnszone.rb +44 -0
  50. data/modules/mu/cloud/machine_images.rb +212 -0
  51. data/modules/mu/cloud/providers.rb +81 -0
  52. data/modules/mu/cloud/resource_base.rb +926 -0
  53. data/modules/mu/cloud/server.rb +40 -0
  54. data/modules/mu/cloud/server_pool.rb +1 -0
  55. data/modules/mu/cloud/ssh_sessions.rb +228 -0
  56. data/modules/mu/cloud/winrm_sessions.rb +237 -0
  57. data/modules/mu/cloud/wrappers.rb +169 -0
  58. data/modules/mu/config.rb +171 -1767
  59. data/modules/mu/config/alarm.rb +2 -6
  60. data/modules/mu/config/bucket.rb +32 -3
  61. data/modules/mu/config/cache_cluster.rb +2 -2
  62. data/modules/mu/config/cdn.rb +100 -0
  63. data/modules/mu/config/collection.rb +4 -4
  64. data/modules/mu/config/container_cluster.rb +9 -4
  65. data/modules/mu/config/database.rb +84 -105
  66. data/modules/mu/config/database.yml +1 -2
  67. data/modules/mu/config/dnszone.rb +10 -9
  68. data/modules/mu/config/doc_helpers.rb +516 -0
  69. data/modules/mu/config/endpoint.rb +5 -4
  70. data/modules/mu/config/firewall_rule.rb +103 -4
  71. data/modules/mu/config/folder.rb +4 -4
  72. data/modules/mu/config/function.rb +19 -10
  73. data/modules/mu/config/group.rb +4 -4
  74. data/modules/mu/config/habitat.rb +4 -4
  75. data/modules/mu/config/job.rb +89 -0
  76. data/modules/mu/config/loadbalancer.rb +60 -14
  77. data/modules/mu/config/log.rb +4 -4
  78. data/modules/mu/config/msg_queue.rb +4 -4
  79. data/modules/mu/config/nosqldb.rb +4 -4
  80. data/modules/mu/config/notifier.rb +10 -21
  81. data/modules/mu/config/ref.rb +411 -0
  82. data/modules/mu/config/role.rb +4 -4
  83. data/modules/mu/config/schema_helpers.rb +509 -0
  84. data/modules/mu/config/search_domain.rb +4 -4
  85. data/modules/mu/config/server.rb +98 -71
  86. data/modules/mu/config/server.yml +1 -0
  87. data/modules/mu/config/server_pool.rb +5 -9
  88. data/modules/mu/config/storage_pool.rb +1 -1
  89. data/modules/mu/config/tail.rb +200 -0
  90. data/modules/mu/config/user.rb +4 -4
  91. data/modules/mu/config/vpc.rb +71 -27
  92. data/modules/mu/config/vpc.yml +0 -1
  93. data/modules/mu/defaults/AWS.yaml +91 -68
  94. data/modules/mu/defaults/Azure.yaml +1 -0
  95. data/modules/mu/defaults/Google.yaml +3 -2
  96. data/modules/mu/deploy.rb +43 -26
  97. data/modules/mu/groomer.rb +17 -2
  98. data/modules/mu/groomers/ansible.rb +188 -41
  99. data/modules/mu/groomers/chef.rb +116 -55
  100. data/modules/mu/logger.rb +127 -148
  101. data/modules/mu/master.rb +410 -2
  102. data/modules/mu/master/chef.rb +3 -4
  103. data/modules/mu/master/ldap.rb +3 -3
  104. data/modules/mu/master/ssl.rb +12 -3
  105. data/modules/mu/mommacat.rb +218 -2612
  106. data/modules/mu/mommacat/daemon.rb +403 -0
  107. data/modules/mu/mommacat/naming.rb +473 -0
  108. data/modules/mu/mommacat/search.rb +495 -0
  109. data/modules/mu/mommacat/storage.rb +722 -0
  110. data/modules/mu/{clouds → providers}/README.md +1 -1
  111. data/modules/mu/{clouds → providers}/aws.rb +380 -122
  112. data/modules/mu/{clouds → providers}/aws/alarm.rb +7 -5
  113. data/modules/mu/{clouds → providers}/aws/bucket.rb +297 -59
  114. data/modules/mu/{clouds → providers}/aws/cache_cluster.rb +37 -71
  115. data/modules/mu/providers/aws/cdn.rb +782 -0
  116. data/modules/mu/{clouds → providers}/aws/collection.rb +26 -25
  117. data/modules/mu/{clouds → providers}/aws/container_cluster.rb +724 -744
  118. data/modules/mu/providers/aws/database.rb +1744 -0
  119. data/modules/mu/{clouds → providers}/aws/dnszone.rb +88 -70
  120. data/modules/mu/providers/aws/endpoint.rb +1072 -0
  121. data/modules/mu/{clouds → providers}/aws/firewall_rule.rb +220 -247
  122. data/modules/mu/{clouds → providers}/aws/folder.rb +8 -8
  123. data/modules/mu/{clouds → providers}/aws/function.rb +300 -142
  124. data/modules/mu/{clouds → providers}/aws/group.rb +31 -29
  125. data/modules/mu/{clouds → providers}/aws/habitat.rb +18 -15
  126. data/modules/mu/providers/aws/job.rb +466 -0
  127. data/modules/mu/{clouds → providers}/aws/loadbalancer.rb +66 -56
  128. data/modules/mu/{clouds → providers}/aws/log.rb +17 -14
  129. data/modules/mu/{clouds → providers}/aws/msg_queue.rb +29 -19
  130. data/modules/mu/{clouds → providers}/aws/nosqldb.rb +114 -16
  131. data/modules/mu/{clouds → providers}/aws/notifier.rb +142 -65
  132. data/modules/mu/{clouds → providers}/aws/role.rb +158 -118
  133. data/modules/mu/{clouds → providers}/aws/search_domain.rb +201 -59
  134. data/modules/mu/{clouds → providers}/aws/server.rb +844 -1139
  135. data/modules/mu/{clouds → providers}/aws/server_pool.rb +74 -65
  136. data/modules/mu/{clouds → providers}/aws/storage_pool.rb +26 -44
  137. data/modules/mu/{clouds → providers}/aws/user.rb +24 -25
  138. data/modules/mu/{clouds → providers}/aws/userdata/README.md +0 -0
  139. data/modules/mu/{clouds → providers}/aws/userdata/linux.erb +5 -4
  140. data/modules/mu/{clouds → providers}/aws/userdata/windows.erb +2 -1
  141. data/modules/mu/{clouds → providers}/aws/vpc.rb +525 -931
  142. data/modules/mu/providers/aws/vpc_subnet.rb +286 -0
  143. data/modules/mu/{clouds → providers}/azure.rb +29 -9
  144. data/modules/mu/{clouds → providers}/azure/container_cluster.rb +3 -8
  145. data/modules/mu/{clouds → providers}/azure/firewall_rule.rb +18 -11
  146. data/modules/mu/{clouds → providers}/azure/habitat.rb +8 -6
  147. data/modules/mu/{clouds → providers}/azure/loadbalancer.rb +5 -5
  148. data/modules/mu/{clouds → providers}/azure/role.rb +8 -10
  149. data/modules/mu/{clouds → providers}/azure/server.rb +97 -49
  150. data/modules/mu/{clouds → providers}/azure/user.rb +6 -8
  151. data/modules/mu/{clouds → providers}/azure/userdata/README.md +0 -0
  152. data/modules/mu/{clouds → providers}/azure/userdata/linux.erb +0 -0
  153. data/modules/mu/{clouds → providers}/azure/userdata/windows.erb +0 -0
  154. data/modules/mu/{clouds → providers}/azure/vpc.rb +16 -21
  155. data/modules/mu/{clouds → providers}/cloudformation.rb +18 -7
  156. data/modules/mu/{clouds → providers}/cloudformation/alarm.rb +3 -3
  157. data/modules/mu/{clouds → providers}/cloudformation/cache_cluster.rb +3 -3
  158. data/modules/mu/{clouds → providers}/cloudformation/collection.rb +3 -3
  159. data/modules/mu/{clouds → providers}/cloudformation/database.rb +6 -17
  160. data/modules/mu/{clouds → providers}/cloudformation/dnszone.rb +3 -3
  161. data/modules/mu/{clouds → providers}/cloudformation/firewall_rule.rb +3 -3
  162. data/modules/mu/{clouds → providers}/cloudformation/loadbalancer.rb +3 -3
  163. data/modules/mu/{clouds → providers}/cloudformation/log.rb +3 -3
  164. data/modules/mu/{clouds → providers}/cloudformation/server.rb +7 -7
  165. data/modules/mu/{clouds → providers}/cloudformation/server_pool.rb +5 -5
  166. data/modules/mu/{clouds → providers}/cloudformation/vpc.rb +5 -7
  167. data/modules/mu/{clouds → providers}/docker.rb +0 -0
  168. data/modules/mu/{clouds → providers}/google.rb +68 -30
  169. data/modules/mu/{clouds → providers}/google/bucket.rb +13 -15
  170. data/modules/mu/{clouds → providers}/google/container_cluster.rb +85 -78
  171. data/modules/mu/{clouds → providers}/google/database.rb +11 -21
  172. data/modules/mu/{clouds → providers}/google/firewall_rule.rb +15 -14
  173. data/modules/mu/{clouds → providers}/google/folder.rb +20 -17
  174. data/modules/mu/{clouds → providers}/google/function.rb +140 -168
  175. data/modules/mu/{clouds → providers}/google/group.rb +29 -34
  176. data/modules/mu/{clouds → providers}/google/habitat.rb +21 -22
  177. data/modules/mu/{clouds → providers}/google/loadbalancer.rb +19 -21
  178. data/modules/mu/{clouds → providers}/google/role.rb +94 -58
  179. data/modules/mu/{clouds → providers}/google/server.rb +243 -156
  180. data/modules/mu/{clouds → providers}/google/server_pool.rb +26 -45
  181. data/modules/mu/{clouds → providers}/google/user.rb +95 -31
  182. data/modules/mu/{clouds → providers}/google/userdata/README.md +0 -0
  183. data/modules/mu/{clouds → providers}/google/userdata/linux.erb +0 -0
  184. data/modules/mu/{clouds → providers}/google/userdata/windows.erb +0 -0
  185. data/modules/mu/{clouds → providers}/google/vpc.rb +103 -79
  186. data/modules/tests/aws-jobs-functions.yaml +46 -0
  187. data/modules/tests/bucket.yml +4 -0
  188. data/modules/tests/centos6.yaml +15 -0
  189. data/modules/tests/centos7.yaml +15 -0
  190. data/modules/tests/centos8.yaml +12 -0
  191. data/modules/tests/ecs.yaml +23 -0
  192. data/modules/tests/eks.yaml +1 -1
  193. data/modules/tests/functions/node-function/lambda_function.js +10 -0
  194. data/modules/tests/functions/python-function/lambda_function.py +12 -0
  195. data/modules/tests/includes-and-params.yaml +2 -1
  196. data/modules/tests/microservice_app.yaml +288 -0
  197. data/modules/tests/rds.yaml +108 -0
  198. data/modules/tests/regrooms/aws-iam.yaml +201 -0
  199. data/modules/tests/regrooms/bucket.yml +19 -0
  200. data/modules/tests/regrooms/rds.yaml +123 -0
  201. data/modules/tests/server-with-scrub-muisms.yaml +2 -1
  202. data/modules/tests/super_complex_bok.yml +2 -2
  203. data/modules/tests/super_simple_bok.yml +3 -5
  204. data/modules/tests/win2k12.yaml +17 -5
  205. data/modules/tests/win2k16.yaml +25 -0
  206. data/modules/tests/win2k19.yaml +25 -0
  207. data/requirements.txt +1 -0
  208. data/spec/mu/clouds/azure_spec.rb +2 -2
  209. metadata +240 -154
  210. data/extras/image-generators/AWS/windows.yaml +0 -18
  211. data/modules/mu/clouds/aws/database.rb +0 -1985
  212. data/modules/mu/clouds/aws/endpoint.rb +0 -592
@@ -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,13 +33,25 @@ 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
39
51
  # resources) have always been done.
40
52
  # @param cloudobj [MU::Cloud]
41
- # @param deploy [MU::MommaCat]
42
- def self.resourceInitHook(cloudobj, deploy)
53
+ # @param _deploy [MU::MommaCat]
54
+ def self.resourceInitHook(cloudobj, _deploy)
43
55
  class << self
44
56
  attr_reader :cloudformation_data
45
57
  end
@@ -63,7 +75,6 @@ module MU
63
75
  return nil
64
76
  end
65
77
 
66
- loaded = false
67
78
  cred_obj = nil
68
79
  if cred_cfg['access_key'] and cred_cfg['access_secret'] and
69
80
  # access key and secret just sitting in mu.yaml
@@ -137,11 +148,22 @@ module MU
137
148
  # assume we've got an IAM profile and hope for the best
138
149
  ENV.delete('AWS_ACCESS_KEY_ID')
139
150
  ENV.delete('AWS_SECRET_ACCESS_KEY')
140
- cred_obj = Aws::InstanceProfileCredentials.new
151
+ retries = 0
152
+ begin
153
+ cred_obj = Aws::InstanceProfileCredentials.new
154
+ if cred_obj.nil?
155
+ retries += 1
156
+ MU.log "Failed to fetch AWS instance profile credentials, attempt #{retries.to_s}/10", MU::WARN
157
+ sleep 3
158
+ end
159
+ end while cred_obj.nil? and retries < 10
141
160
  # if name.nil?
142
161
  # Aws.config = {region: ENV['EC2_REGION']}
143
162
  # end
144
163
  end
164
+ if cred_obj.nil?
165
+ MU.log "cred_obj is nil and hosted? says #{hosted?.to_s}", MU::WARN, details: name
166
+ end
145
167
 
146
168
  if name.nil?
147
169
  @@creds_loaded["#default"] = cred_obj
@@ -172,18 +194,34 @@ module MU
172
194
  end
173
195
  end
174
196
 
175
- # Tag a resource with all of our standard identifying tags.
197
+ # Tag an EC2 resource
176
198
  #
177
199
  # @param resource [String]: The cloud provider identifier of the resource to tag
178
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
179
205
  # @return [void]
180
- 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)
181
207
  tags = []
182
208
  MU::MommaCat.listStandardTags.each_pair { |name, value|
183
- if !value.nil?
184
- tags << {key: name, value: value}
185
- end
209
+ tags << {key: name, value: value} if !value.nil?
186
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
+
187
225
  if MU::Cloud::CloudFormation.emitCloudFormation
188
226
  return tags
189
227
  end
@@ -205,6 +243,7 @@ module MU
205
243
  end
206
244
  end
207
245
  MU.log "Created standard tags for resource #{resource}", MU::DEBUG, details: caller
246
+
208
247
  end
209
248
 
210
249
  @@myVPCObj = nil
@@ -283,52 +322,13 @@ module MU
283
322
  )
284
323
  end
285
324
 
286
- # Tag EC2 resources.
287
- #
288
- # @param resources [Array<String>]: The cloud provider identifier of the resource to tag
289
- # @param key [String]: The name of the tag to create
290
- # @param value [String]: The value of the tag
291
- # @param region [String]: The cloud provider region
292
- # @return [void,<Hash>]
293
- def self.createTag(key, value, resources = [], region: myRegion, credentials: nil)
294
-
295
- if !MU::Cloud::CloudFormation.emitCloudFormation
296
- begin
297
- MU::Cloud::AWS.ec2(region: region, credentials: credentials).create_tags(
298
- resources: resources,
299
- tags: [
300
- {
301
- key: key,
302
- value: value
303
- }
304
- ]
305
- )
306
- rescue Aws::EC2::Errors::ServiceError => e
307
- MU.log "Got #{e.inspect} tagging #{resources.size.to_s} resources with #{key}=#{value}", MU::WARN, details: resources if attempts > 1
308
- if attempts < 5
309
- attempts = attempts + 1
310
- sleep 15
311
- retry
312
- else
313
- raise e
314
- end
315
- end
316
- MU.log "Created tag #{key} with value #{value}", MU::DEBUG, details: resources
317
- else
318
- return {
319
- "Key" => key,
320
- "Value" => value
321
- }
322
- end
323
- end
324
-
325
325
  @@azs = {}
326
326
  # List the Availability Zones associated with a given Amazon Web Services
327
327
  # region. If no region is given, search the one in which this MU master
328
328
  # server resides.
329
329
  # @param region [String]: The region to search.
330
330
  # @return [Array<String>]: The Availability Zones in this region.
331
- def self.listAZs(region: MU.curRegion, account: nil, credentials: nil)
331
+ def self.listAZs(region: MU.curRegion, credentials: nil)
332
332
  cfg = credConfig(credentials)
333
333
  return [] if !cfg
334
334
  if !region.nil? and @@azs[region]
@@ -356,6 +356,27 @@ module MU
356
356
  # etc)
357
357
  # @param deploy_id [MU::MommaCat]
358
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
359
380
  end
360
381
 
361
382
  # Plant a Mu deploy secret into a storage bucket somewhere for so our kittens can consume it
@@ -435,7 +456,7 @@ module MU
435
456
  end
436
457
 
437
458
  begin
438
- Timeout.timeout(2) do
459
+ Timeout.timeout(4) do
439
460
  instance_id = open("http://169.254.169.254/latest/meta-data/instance-id").read
440
461
  if !instance_id.nil? and instance_id.size > 0
441
462
  @@is_in_aws = true
@@ -491,7 +512,32 @@ module MU
491
512
  # @param cloudobj [MU::Cloud::AWS]: The resource from which to extract the habitat id
492
513
  # @return [String,nil]
493
514
  def self.habitat(cloudobj, nolookup: false, deploy: nil)
494
- cloudobj.respond_to?(:account_number) ? cloudobj.account_number : nil
515
+ @@habmap ||= {}
516
+ # XXX whaddabout config['habitat'] HNNNGH
517
+
518
+ if cloudobj.respond_to?(:account_number) and cloudobj.account_number and
519
+ !cloudobj.account_number.empty?
520
+ return cloudobj.account_number
521
+ elsif cloudobj.config and cloudobj.config['account']
522
+ if nolookup
523
+ return cloudobj.config['account']
524
+ end
525
+ if @@habmap[cloudobj.config['account']]
526
+ return @@habmap[cloudobj.config['account']]
527
+ end
528
+ deploy ||= cloudobj.deploy if cloudobj.respond_to?(:deploy)
529
+
530
+ MU.log "Incomplete implementation: MU::Cloud::AWS.habitat", MU::DEBUG, details: deploy
531
+
532
+ # accountobj = accountLookup(cloudobj.config['account'], deploy, raise_on_fail: false)
533
+
534
+ # if accountobj
535
+ # @@habmap[cloudobj.config['account']] = accountobj.cloud_id
536
+ # return accountobj.cloud_id
537
+ # end
538
+ end
539
+
540
+ nil
495
541
  end
496
542
 
497
543
 
@@ -506,7 +552,6 @@ module MU
506
552
 
507
553
  return creds['account_number'] if creds['account_number']
508
554
 
509
- user_list = MU::Cloud::AWS.iam(credentials: name).list_users.users
510
555
  acct_num = MU::Cloud::AWS.iam(credentials: name).list_users.users.first.arn.split(/:/)[4]
511
556
  acct_num.to_s
512
557
  end
@@ -543,8 +588,8 @@ module MU
543
588
  if !found
544
589
  MU.log "Attempting to create log bucket #{cfg['log_bucket_name']} for credentials #{credentials}", MU::WARN
545
590
  begin
546
- resp = MU::Cloud::AWS.s3(credentials: credentials).create_bucket(bucket: cfg['log_bucket_name'], acl: "private")
547
- rescue Aws::S3::Errors::BucketAlreadyExists => e
591
+ MU::Cloud::AWS.s3(credentials: credentials).create_bucket(bucket: cfg['log_bucket_name'], acl: "private")
592
+ rescue Aws::S3::Errors::BucketAlreadyExists
548
593
  raise MuError, "AWS credentials #{credentials} need a log bucket, and the name #{cfg['log_bucket_name']} is unavailable. Use mu-configure to edit credentials '#{credentials}' or 'hostname'"
549
594
  end
550
595
  end
@@ -620,9 +665,9 @@ module MU
620
665
  # Check each credential sets' resident account, then
621
666
  $MU_CFG['aws'].each_pair { |acctname, cfg|
622
667
  begin
623
- user_list = MU::Cloud::AWS.iam(credentials: acctname).list_users.users
668
+ MU::Cloud::AWS.iam(credentials: acctname).list_users.users
624
669
  # rescue ::Aws::IAM::Errors => e # XXX why does this NameError here?
625
- rescue Exception => e
670
+ rescue StandardError => e
626
671
  MU.log e.inspect, MU::WARN, details: cfg
627
672
  next
628
673
  end
@@ -653,7 +698,7 @@ module MU
653
698
  # begin
654
699
  # user_list = MU::Cloud::AWS.iam(region: credConfig['region']).list_users.users
655
700
  ## rescue ::Aws::IAM::Errors => e # XXX why does this NameError here?
656
- # rescue Exception => e
701
+ # rescue StandardError => e
657
702
  # MU.log "Got #{e.inspect} while trying to figure out our account number", MU::WARN, details: caller
658
703
  # end
659
704
  # if user_list.nil? or user_list.size == 0
@@ -682,7 +727,6 @@ module MU
682
727
  if @@regions.size == 0
683
728
  return [] if credConfig.nil?
684
729
  result = MU::Cloud::AWS.ec2(region: myRegion, credentials: credentials).describe_regions.regions
685
- regions = []
686
730
  @@regions_semaphore.synchronize {
687
731
  begin
688
732
  result.each { |r|
@@ -761,50 +805,47 @@ module MU
761
805
 
762
806
  @@instance_types ||= {}
763
807
  @@instance_types[region] ||= {}
764
- next_token = nil
765
808
 
766
- begin
767
- # Pricing API isn't widely available, so ask a region we know supports
768
- # it
769
- resp = MU::Cloud::AWS.pricing(region: "us-east-1").get_products(
770
- service_code: "AmazonEC2",
771
- filters: [
772
- {
773
- field: "productFamily",
774
- value: "Compute Instance",
775
- type: "TERM_MATCH"
776
- },
777
- {
778
- field: "tenancy",
779
- value: "Shared",
780
- type: "TERM_MATCH"
781
- },
782
- {
783
- field: "location",
784
- value: human_region,
785
- type: "TERM_MATCH"
786
- }
787
- ],
788
- next_token: next_token
789
- )
790
- resp.price_list.each { |pricing|
791
- data = JSON.parse(pricing)
792
- type = data["product"]["attributes"]["instanceType"]
793
- next if @@instance_types[region].has_key?(type)
794
- @@instance_types[region][type] = {}
795
- ["ecu", "vcpu", "memory", "storage"].each { |a|
796
- @@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"
797
828
  }
798
- @@instance_types[region][type]["memory"].sub!(/ GiB/, "")
799
- @@instance_types[region][type]["memory"] = @@instance_types[region][type]["memory"].to_f
800
- @@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]
801
838
  }
802
- next_token = resp.next_token
803
- 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
+ }
804
843
 
805
844
  @@instance_types
806
845
  end
807
846
 
847
+ @@certificates = {}
848
+
808
849
  # AWS can stash API-available certificates in Amazon Certificate Manager
809
850
  # or in IAM. Rather than make people crazy trying to get the syntax
810
851
  # correct in our Baskets of Kittens, let's have a helper that tries to do
@@ -813,21 +854,24 @@ module MU
813
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.
814
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.
815
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.
816
- def self.findSSLCertificate(name: nil, id: nil, region: myRegion)
817
- 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?)
818
859
  raise MuError, "Can't call findSSLCertificate without specifying either a name or an id"
819
860
  end
861
+ if id and @@certificates[id]
862
+ return [id, @@certificates[id]]
863
+ end
820
864
 
821
865
  if !name.nil? and !name.empty?
822
866
  matches = []
823
- acmcerts = MU::Cloud::AWS.acm(region: region).list_certificates(
867
+ acmcerts = MU::Cloud::AWS.acm(region: region, credentials: credentials).list_certificates(
824
868
  certificate_statuses: ["ISSUED"]
825
869
  )
826
870
  acmcerts.certificate_summary_list.each { |cert|
827
871
  matches << cert.certificate_arn if cert.domain_name == name
828
872
  }
829
873
  begin
830
- iamcert = MU::Cloud::AWS.iam.get_server_certificate(
874
+ iamcert = MU::Cloud::AWS.iam(credentials: credentials).get_server_certificate(
831
875
  server_certificate_name: name
832
876
  )
833
877
  rescue Aws::IAM::Errors::ValidationError, Aws::IAM::Errors::NoSuchEntity
@@ -837,32 +881,45 @@ module MU
837
881
  matches << iamcert.server_certificate.server_certificate_metadata.arn
838
882
  end
839
883
  if matches.size == 1
840
- return matches.first
884
+ id = matches.first
841
885
  elsif matches.size == 0
842
- 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
843
891
  elsif matches.size > 1
844
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."
845
893
  end
846
894
  end
847
895
 
896
+ domains = []
897
+
848
898
  if id.match(/^arn:aws(?:-us-gov)?:acm/)
849
- resp = MU::Cloud::AWS.acm(region: region).get_certificate(
899
+ resp = MU::Cloud::AWS.acm(region: region).describe_certificate(
850
900
  certificate_arn: id
851
901
  )
852
- if resp.nil?
902
+
903
+ if resp.nil? or resp.certificate.nil?
853
904
  raise MuError, "No such ACM certificate '#{id}'"
854
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
855
910
  elsif id.match(/^arn:aws(?:-us-gov)?:iam/)
856
911
  resp = MU::Cloud::AWS.iam.list_server_certificates
857
912
  if resp.nil?
858
913
  raise MuError, "No such IAM certificate '#{id}'"
859
914
  end
860
915
  resp.server_certificate_metadata_list.each { |cert|
916
+
861
917
  if cert.arn == id
862
918
  if cert.expiration < Time.now
863
919
  MU.log "IAM SSL certificate #{cert.server_certificate_name} (#{id}) is EXPIRED", MU::WARN
864
920
  end
865
- return id
921
+ @@certificates[id] = [cert.server_certificate_name]
922
+ return [id, [cert.server_certificate_name]]
866
923
  end
867
924
  }
868
925
  raise MuError, "No such IAM certificate '#{id}'"
@@ -870,7 +927,56 @@ module MU
870
927
  raise MuError, "The format of '#{id}' doesn't look like an ARN for either Amazon Certificate Manager or IAM"
871
928
  end
872
929
 
873
- 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]
874
980
  end
875
981
 
876
982
  # Amazon Certificate Manager API
@@ -990,6 +1096,14 @@ module MU
990
1096
  @@cloudwatchlogs_api[credentials][region]
991
1097
  end
992
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
+
993
1107
  # Amazon's CloudFront API
994
1108
  def self.cloudfront(region: MU.curRegion, credentials: nil)
995
1109
  region ||= myRegion
@@ -1078,6 +1192,14 @@ module MU
1078
1192
  @@dynamo_api[credentials][region]
1079
1193
  end
1080
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
+
1081
1203
  # Amazon's Pricing API
1082
1204
  def self.pricing(region: MU.curRegion, credentials: nil)
1083
1205
  region ||= myRegion
@@ -1126,6 +1248,14 @@ module MU
1126
1248
  @@kms_api[credentials][region]
1127
1249
  end
1128
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
+
1129
1259
  # Amazon's Organizations API
1130
1260
  def self.orgs(credentials: nil)
1131
1261
  @@organizations_api ||= {}
@@ -1154,12 +1284,60 @@ module MU
1154
1284
  end
1155
1285
  end
1156
1286
 
1287
+ # Tag a resource. Defaults to applying our MU deployment identifier, if no
1288
+ # arguments other than the resource identifier are given.
1289
+ #
1290
+ # @param resource [String]: The cloud provider identifier of the resource to tag
1291
+ # @param tag_name [String]: The name of the tag to create
1292
+ # @param tag_value [String]: The value of the tag
1293
+ # @param region [String]: The cloud provider region
1294
+ # @return [void]
1295
+ def self.createTag(resource = nil,
1296
+ tag_name="MU-ID",
1297
+ tag_value=MU.deploy_id,
1298
+ region: MU.curRegion,
1299
+ credentials: nil)
1300
+ attempts = 0
1301
+
1302
+ return nil if resource.nil?
1303
+ resource = [resource] if resource.is_a?(String)
1304
+
1305
+ if !MU::Cloud::CloudFormation.emitCloudFormation
1306
+ begin
1307
+ MU::Cloud::AWS.ec2(credentials: credentials, region: region).create_tags(
1308
+ resources: resource,
1309
+ tags: [
1310
+ {
1311
+ key: tag_name,
1312
+ value: tag_value
1313
+ }
1314
+ ]
1315
+ )
1316
+ rescue Aws::EC2::Errors::ServiceError => e
1317
+ MU.log "Got #{e.inspect} tagging #{resource} with #{tag_name}=#{tag_value}", MU::WARN if attempts > 1
1318
+ if attempts < 5
1319
+ attempts = attempts + 1
1320
+ sleep 15
1321
+ retry
1322
+ else
1323
+ raise e
1324
+ end
1325
+ end
1326
+ MU.log "Created tag #{tag_name} with value #{tag_value} for resource #{resource}", MU::DEBUG
1327
+ else
1328
+ return {
1329
+ "Key" => tag_name,
1330
+ "Value" => tag_value
1331
+ }
1332
+ end
1333
+ end
1334
+
1157
1335
  @syslog_port_semaphore = Mutex.new
1158
1336
  # Punch AWS security group holes for client nodes to talk back to us, the
1159
1337
  # Mu Master, if we're in AWS.
1160
1338
  # @return [void]
1161
1339
  def self.openFirewallForClients
1162
- MU::Cloud.loadCloudType("AWS", :FirewallRule)
1340
+ MU::Cloud.resourceClass("AWS", :FirewallRule)
1163
1341
  begin
1164
1342
  if File.exist?(Etc.getpwuid(Process.uid).dir+"/.chef/knife.rb")
1165
1343
  ::Chef::Config.from_file(Etc.getpwuid(Process.uid).dir+"/.chef/knife.rb")
@@ -1205,8 +1383,8 @@ module MU
1205
1383
  )
1206
1384
  sg_id = group.group_id
1207
1385
  my_sgs << sg_id
1208
- MU::MommaCat.createTag sg_id, "Name", my_client_sg_name
1209
- MU::MommaCat.createTag sg_id, "MU-MASTER-IP", MU.mu_public_ip
1386
+ MU::Cloud::AWS.createTag sg_id, "Name", my_client_sg_name
1387
+ MU::Cloud::AWS.createTag sg_id, "MU-MASTER-IP", MU.mu_public_ip
1210
1388
  MU::Cloud::AWS.ec2.modify_instance_attribute(
1211
1389
  instance_id: my_instance_id,
1212
1390
  groups: my_sgs
@@ -1237,7 +1415,7 @@ module MU
1237
1415
  end
1238
1416
 
1239
1417
  allow_ips = ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]
1240
- MU::MommaCat.listAllNodes.each_pair { |node, data|
1418
+ MU::MommaCat.listAllNodes.values.each { |data|
1241
1419
  next if data.nil? or !data.is_a?(Hash)
1242
1420
  ["public_ip_address"].each { |key|
1243
1421
  if data.has_key?(key) and !data[key].nil? and !data[key].empty?
@@ -1266,7 +1444,7 @@ module MU
1266
1444
  }
1267
1445
  ]
1268
1446
  )
1269
- rescue Aws::EC2::Errors::InvalidPermissionNotFound => e
1447
+ rescue Aws::EC2::Errors::InvalidPermissionNotFound
1270
1448
  MU.log "Permission disappeared from #{sg_id} (port #{port.to_s}) before I could remove it", MU::WARN, details: MU.structToHash(rule.ip_ranges)
1271
1449
  end
1272
1450
  end
@@ -1300,8 +1478,6 @@ module MU
1300
1478
  }
1301
1479
  end
1302
1480
 
1303
- private
1304
-
1305
1481
  # XXX we shouldn't have to do this, but AWS does not provide a way to look
1306
1482
  # it up, and the pricing API only returns the human-readable strings.
1307
1483
  @@regionLookup = {
@@ -1337,7 +1513,7 @@ module MU
1337
1513
  # Create an AWS API client
1338
1514
  # @param region [String]: Amazon region so we know what endpoint to use
1339
1515
  # @param api [String]: Which API are we wrapping?
1340
- def initialize(region: MU.curRegion, api: "EC2", credentials: nil)
1516
+ def initialize(region: nil, api: "EC2", credentials: nil)
1341
1517
  @cred_obj = MU::Cloud::AWS.loadCredentials(credentials)
1342
1518
  @credentials = MU::Cloud::AWS.credConfig(credentials, name_only: true)
1343
1519
 
@@ -1346,6 +1522,8 @@ module MU
1346
1522
  end
1347
1523
 
1348
1524
  params = {}
1525
+ region ||= MU::Cloud::AWS.credConfig(credentials)['region']
1526
+ region ||= MU.myRegion
1349
1527
 
1350
1528
  if region
1351
1529
  @region = region
@@ -1362,21 +1540,98 @@ module MU
1362
1540
  # Catch-all for AWS client methods. Essentially a pass-through with some
1363
1541
  # rescues for known silly endpoint behavior.
1364
1542
  def method_missing(method_sym, *arguments)
1543
+ # make sure error symbols are loaded for our exception handling later
1365
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"
1366
1561
 
1367
1562
  retries = 0
1368
1563
  begin
1369
1564
  MU.log "Calling #{method_sym} in #{@region}", MU::DEBUG, details: arguments
1370
- retval = nil
1371
- if !arguments.nil? and arguments.size == 1
1372
- retval = @api.method(method_sym).call(arguments[0])
1373
- elsif !arguments.nil? and arguments.size > 0
1374
- retval = @api.method(method_sym).call(*arguments)
1375
- else
1376
- 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
1377
1631
  end
1632
+
1378
1633
  return retval
1379
- 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
1380
1635
  if e.class.name == "Seahorse::Client::NetworkingError" and e.message.match(/Name or service not known/)
1381
1636
  MU.log e.inspect, MU::ERR
1382
1637
  raise e
@@ -1397,7 +1652,7 @@ module MU
1397
1652
  MU.log "Got #{e.inspect} calling EC2's #{method_sym} in #{@region} with credentials #{@credentials}, waiting #{interval.to_s}s and retrying. Args were: #{arguments}", debuglevel, details: caller
1398
1653
  sleep interval
1399
1654
  retry
1400
- rescue Exception => e
1655
+ rescue StandardError => e
1401
1656
  MU.log "Got #{e.inspect} calling EC2's #{method_sym} in #{@region} with credentials #{@credentials}", MU::DEBUG, details: arguments
1402
1657
  raise e
1403
1658
  end
@@ -1418,6 +1673,7 @@ module MU
1418
1673
  @@wafglobal = {}
1419
1674
  @@waf = {}
1420
1675
  @@cloudwatchlogs_api = {}
1676
+ @@cloudwatchevents_api = {}
1421
1677
  @@cloudfront_api = {}
1422
1678
  @@elasticache_api = {}
1423
1679
  @@sns_api = {}
@@ -1436,6 +1692,8 @@ module MU
1436
1692
  @@kms_api ={}
1437
1693
  @@organization_api ={}
1438
1694
  @@dynamo_api ={}
1695
+ @@dynamostream_api ={}
1696
+ @@cloudfront_api ={}
1439
1697
  end
1440
1698
  end
1441
1699
  end