cloud-mu 3.2.0 → 3.5.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 (156) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/ansible/roles/mu-nat/tasks/main.yml +3 -0
  4. data/bin/mu-adopt +12 -1
  5. data/bin/mu-aws-setup +41 -7
  6. data/bin/mu-azure-setup +34 -0
  7. data/bin/mu-configure +214 -119
  8. data/bin/mu-gcp-setup +37 -2
  9. data/bin/mu-load-config.rb +2 -1
  10. data/bin/mu-node-manage +3 -0
  11. data/bin/mu-refresh-ssl +67 -0
  12. data/bin/mu-run-tests +28 -6
  13. data/bin/mu-self-update +30 -10
  14. data/bin/mu-upload-chef-artifacts +30 -26
  15. data/cloud-mu.gemspec +10 -8
  16. data/cookbooks/mu-master/attributes/default.rb +5 -1
  17. data/cookbooks/mu-master/metadata.rb +2 -2
  18. data/cookbooks/mu-master/recipes/default.rb +81 -26
  19. data/cookbooks/mu-master/recipes/init.rb +197 -62
  20. data/cookbooks/mu-master/recipes/update_nagios_only.rb +1 -1
  21. data/cookbooks/mu-master/recipes/vault.rb +78 -77
  22. data/cookbooks/mu-master/templates/default/mods/rewrite.conf.erb +1 -0
  23. data/cookbooks/mu-master/templates/default/nagios.conf.erb +103 -0
  24. data/cookbooks/mu-master/templates/default/web_app.conf.erb +14 -30
  25. data/cookbooks/mu-tools/attributes/default.rb +12 -0
  26. data/cookbooks/mu-tools/files/centos-6/CentOS-Base.repo +47 -0
  27. data/cookbooks/mu-tools/libraries/helper.rb +98 -4
  28. data/cookbooks/mu-tools/libraries/monkey.rb +1 -1
  29. data/cookbooks/mu-tools/recipes/apply_security.rb +31 -9
  30. data/cookbooks/mu-tools/recipes/aws_api.rb +8 -2
  31. data/cookbooks/mu-tools/recipes/base_repositories.rb +1 -1
  32. data/cookbooks/mu-tools/recipes/gcloud.rb +2 -9
  33. data/cookbooks/mu-tools/recipes/google_api.rb +7 -0
  34. data/cookbooks/mu-tools/recipes/rsyslog.rb +8 -1
  35. data/cookbooks/mu-tools/resources/disk.rb +113 -42
  36. data/cookbooks/mu-tools/resources/mommacat_request.rb +1 -2
  37. data/cookbooks/mu-tools/templates/centos-8/sshd_config.erb +215 -0
  38. data/extras/Gemfile.lock.bootstrap +394 -0
  39. data/extras/bucketstubs/error.html +0 -0
  40. data/extras/bucketstubs/index.html +0 -0
  41. data/extras/clean-stock-amis +11 -3
  42. data/extras/generate-stock-images +6 -3
  43. data/extras/git_rpm/build.sh +20 -0
  44. data/extras/git_rpm/mugit.spec +53 -0
  45. data/extras/image-generators/AWS/centos7.yaml +19 -16
  46. data/extras/image-generators/AWS/{rhel7.yaml → rhel71.yaml} +0 -0
  47. data/extras/image-generators/AWS/{win2k12.yaml → win2k12r2.yaml} +0 -0
  48. data/extras/image-generators/VMWare/centos8.yaml +15 -0
  49. data/extras/openssl_rpm/build.sh +19 -0
  50. data/extras/openssl_rpm/mussl.spec +46 -0
  51. data/extras/python_rpm/muthon.spec +14 -4
  52. data/extras/ruby_rpm/muby.spec +9 -5
  53. data/extras/sqlite_rpm/build.sh +19 -0
  54. data/extras/sqlite_rpm/muqlite.spec +47 -0
  55. data/install/installer +7 -5
  56. data/modules/mommacat.ru +2 -2
  57. data/modules/mu.rb +14 -7
  58. data/modules/mu/adoption.rb +5 -5
  59. data/modules/mu/cleanup.rb +47 -25
  60. data/modules/mu/cloud.rb +29 -1
  61. data/modules/mu/cloud/dnszone.rb +0 -2
  62. data/modules/mu/cloud/machine_images.rb +1 -1
  63. data/modules/mu/cloud/providers.rb +6 -1
  64. data/modules/mu/cloud/resource_base.rb +16 -7
  65. data/modules/mu/cloud/ssh_sessions.rb +5 -1
  66. data/modules/mu/cloud/wrappers.rb +20 -7
  67. data/modules/mu/config.rb +28 -12
  68. data/modules/mu/config/bucket.rb +31 -2
  69. data/modules/mu/config/cache_cluster.rb +1 -1
  70. data/modules/mu/config/cdn.rb +100 -0
  71. data/modules/mu/config/container_cluster.rb +1 -1
  72. data/modules/mu/config/database.rb +3 -3
  73. data/modules/mu/config/dnszone.rb +4 -3
  74. data/modules/mu/config/endpoint.rb +1 -0
  75. data/modules/mu/config/firewall_rule.rb +1 -1
  76. data/modules/mu/config/function.rb +16 -7
  77. data/modules/mu/config/job.rb +89 -0
  78. data/modules/mu/config/notifier.rb +7 -18
  79. data/modules/mu/config/ref.rb +55 -9
  80. data/modules/mu/config/schema_helpers.rb +12 -3
  81. data/modules/mu/config/server.rb +11 -5
  82. data/modules/mu/config/server_pool.rb +2 -2
  83. data/modules/mu/config/vpc.rb +11 -10
  84. data/modules/mu/defaults/AWS.yaml +106 -106
  85. data/modules/mu/deploy.rb +40 -14
  86. data/modules/mu/groomers/chef.rb +2 -2
  87. data/modules/mu/master.rb +70 -3
  88. data/modules/mu/mommacat.rb +28 -9
  89. data/modules/mu/mommacat/daemon.rb +13 -7
  90. data/modules/mu/mommacat/naming.rb +2 -2
  91. data/modules/mu/mommacat/search.rb +16 -5
  92. data/modules/mu/mommacat/storage.rb +67 -32
  93. data/modules/mu/providers/aws.rb +298 -85
  94. data/modules/mu/providers/aws/alarm.rb +5 -5
  95. data/modules/mu/providers/aws/bucket.rb +284 -50
  96. data/modules/mu/providers/aws/cache_cluster.rb +26 -26
  97. data/modules/mu/providers/aws/cdn.rb +782 -0
  98. data/modules/mu/providers/aws/collection.rb +16 -16
  99. data/modules/mu/providers/aws/container_cluster.rb +84 -64
  100. data/modules/mu/providers/aws/database.rb +59 -55
  101. data/modules/mu/providers/aws/dnszone.rb +29 -12
  102. data/modules/mu/providers/aws/endpoint.rb +535 -50
  103. data/modules/mu/providers/aws/firewall_rule.rb +32 -26
  104. data/modules/mu/providers/aws/folder.rb +1 -1
  105. data/modules/mu/providers/aws/function.rb +300 -134
  106. data/modules/mu/providers/aws/group.rb +16 -14
  107. data/modules/mu/providers/aws/habitat.rb +4 -4
  108. data/modules/mu/providers/aws/job.rb +469 -0
  109. data/modules/mu/providers/aws/loadbalancer.rb +67 -45
  110. data/modules/mu/providers/aws/log.rb +17 -17
  111. data/modules/mu/providers/aws/msg_queue.rb +22 -13
  112. data/modules/mu/providers/aws/nosqldb.rb +99 -8
  113. data/modules/mu/providers/aws/notifier.rb +137 -65
  114. data/modules/mu/providers/aws/role.rb +119 -83
  115. data/modules/mu/providers/aws/search_domain.rb +166 -30
  116. data/modules/mu/providers/aws/server.rb +209 -118
  117. data/modules/mu/providers/aws/server_pool.rb +95 -130
  118. data/modules/mu/providers/aws/storage_pool.rb +19 -11
  119. data/modules/mu/providers/aws/user.rb +5 -5
  120. data/modules/mu/providers/aws/userdata/linux.erb +5 -4
  121. data/modules/mu/providers/aws/vpc.rb +109 -54
  122. data/modules/mu/providers/aws/vpc_subnet.rb +43 -39
  123. data/modules/mu/providers/azure.rb +78 -12
  124. data/modules/mu/providers/azure/server.rb +20 -4
  125. data/modules/mu/providers/cloudformation/server.rb +1 -1
  126. data/modules/mu/providers/google.rb +21 -5
  127. data/modules/mu/providers/google/bucket.rb +1 -1
  128. data/modules/mu/providers/google/container_cluster.rb +1 -1
  129. data/modules/mu/providers/google/database.rb +1 -1
  130. data/modules/mu/providers/google/firewall_rule.rb +1 -1
  131. data/modules/mu/providers/google/folder.rb +7 -3
  132. data/modules/mu/providers/google/function.rb +66 -31
  133. data/modules/mu/providers/google/group.rb +1 -1
  134. data/modules/mu/providers/google/habitat.rb +1 -1
  135. data/modules/mu/providers/google/loadbalancer.rb +1 -1
  136. data/modules/mu/providers/google/role.rb +6 -3
  137. data/modules/mu/providers/google/server.rb +1 -1
  138. data/modules/mu/providers/google/server_pool.rb +1 -1
  139. data/modules/mu/providers/google/user.rb +1 -1
  140. data/modules/mu/providers/google/vpc.rb +28 -3
  141. data/modules/tests/aws-jobs-functions.yaml +46 -0
  142. data/modules/tests/aws-servers-with-handrolled-iam.yaml +37 -0
  143. data/modules/tests/centos6.yaml +4 -0
  144. data/modules/tests/centos7.yaml +4 -0
  145. data/modules/tests/ecs.yaml +2 -2
  146. data/modules/tests/eks.yaml +1 -1
  147. data/modules/tests/functions/node-function/lambda_function.js +10 -0
  148. data/modules/tests/functions/python-function/lambda_function.py +12 -0
  149. data/modules/tests/k8s.yaml +1 -1
  150. data/modules/tests/microservice_app.yaml +288 -0
  151. data/modules/tests/rds.yaml +5 -5
  152. data/modules/tests/regrooms/rds.yaml +5 -5
  153. data/modules/tests/server-with-scrub-muisms.yaml +1 -1
  154. data/modules/tests/super_complex_bok.yml +2 -2
  155. data/modules/tests/super_simple_bok.yml +2 -2
  156. metadata +42 -17
@@ -16,7 +16,6 @@ require "net/http"
16
16
  require 'open-uri'
17
17
  require 'timeout'
18
18
  require 'inifile'
19
- gem 'aws-sdk-core'
20
19
  autoload :Aws, "aws-sdk-core"
21
20
 
22
21
 
@@ -54,14 +53,19 @@ module MU
54
53
  def self.resourceInitHook(cloudobj, _deploy)
55
54
  class << self
56
55
  attr_reader :cloudformation_data
56
+ attr_reader :region
57
57
  end
58
+ return if !cloudobj
58
59
  cloudobj.instance_variable_set(:@cloudformation_data, {})
60
+
61
+ cloudobj.instance_variable_set(:@region, cloudobj.config['region'])
59
62
  end
60
63
 
61
64
  # Load some credentials for using the AWS API
62
65
  # @param name [String]: The name of the mu.yaml AWS credential set to use. If not specified, will use the default credentials, and set the global Aws.config credentials to those.
63
66
  # @return [Aws::Credentials]
64
67
  def self.loadCredentials(name = nil)
68
+ gem 'aws-sdk-core'
65
69
  @@creds_loaded ||= {}
66
70
 
67
71
  if name.nil?
@@ -124,10 +128,14 @@ module MU
124
128
  # pull access key and secret from a vault
125
129
  begin
126
130
  vault, item = cred_cfg["credentials"].split(/:/)
127
- data = MU::Groomer::Chef.getSecret(vault: vault, item: item).to_h
128
- if data["access_key"] and data["access_secret"]
131
+ data = if !vault or !item
132
+ raise MuError.new "AWS #{name} credentials field value '#{cred_cfg["credentials"]}' malformed, should be vaultname:itemname", details: cred_cfg
133
+ else
134
+ MU::Groomer::Chef.getSecret(vault: vault, item: item).to_h
135
+ end
136
+ if data and data["access_key"] and data["access_secret"]
129
137
  cred_obj = Aws::Credentials.new(
130
- cred_cfg['access_key'], cred_cfg['access_secret']
138
+ data['access_key'], data['access_secret']
131
139
  )
132
140
  if name.nil?
133
141
  # Aws.config = {
@@ -137,10 +145,10 @@ module MU
137
145
  # }
138
146
  end
139
147
  else
140
- MU.log "AWS credentials vault:item #{cred_cfg["credentials"]} specified, but is missing access_key or access_secret elements", MU::WARN
148
+ raise MuError.new "AWS #{name} credentials vault:item #{cred_cfg["credentials"]} specified, but is missing access_key or access_secret elements", details: cred_cfg
141
149
  end
142
150
  rescue MU::Groomer::MuNoSuchSecret
143
- MU.log "AWS credentials vault:item #{cred_cfg["credentials"]} specified, but does not exist", MU::WARN
151
+ raise MuError.new "AWS #{name} credentials vault:item #{cred_cfg["credentials"]} specified, but does not exist", details: cred_cfg
144
152
  end
145
153
  end
146
154
 
@@ -186,6 +194,7 @@ end
186
194
  # @param r [String]
187
195
  # @return [String]
188
196
  def self.validate_region(r, credentials: nil)
197
+ require "aws-sdk-ec2"
189
198
  begin
190
199
  MU::Cloud::AWS.ec2(region: r, credentials: credentials).describe_availability_zones.availability_zones.first.region_name
191
200
  rescue ::Aws::EC2::Errors::UnauthorizedOperation => e
@@ -204,6 +213,7 @@ end
204
213
  # @param othertags [Array<Hash>]: Miscellaneous custom tags, in Basket of Kittens style
205
214
  # @return [void]
206
215
  def self.createStandardTags(resource = nil, region: MU.curRegion, credentials: nil, optional: true, nametag: nil, othertags: nil)
216
+ require "aws-sdk-ec2"
207
217
  tags = []
208
218
  MU::MommaCat.listStandardTags.each_pair { |name, value|
209
219
  tags << {key: name, value: value} if !value.nil?
@@ -261,20 +271,33 @@ end
261
271
  @@myVPCObj
262
272
  end
263
273
 
264
- # If we've configured AWS as a provider, or are simply hosted in AWS,
274
+ # If we've configured AWS as a provider, or are simply hosted in AWS,
265
275
  # decide what our default region is.
266
- def self.myRegion(credentials = nil)
267
- return @@myRegion_var if @@myRegion_var
276
+ def self.myRegion(credentials = nil, debug: false)
277
+ loglevel = debug ? MU::NOTICE : MU::DEBUG
278
+ if @@myRegion_var
279
+ MU.log "AWS.myRegion: returning #{@@myRegion_var} from cache", loglevel
280
+ return @@myRegion_var
281
+ end
268
282
 
283
+ MU.log "AWS.myRegion: credConfig", loglevel, details: credConfig
284
+ MU.log "AWS.myRegion: hosted?", loglevel, details: hosted?.to_s
285
+ MU.log "AWS.myRegion: ENV['EC2_REGION']", loglevel, details: ENV['EC2_REGION']
286
+ MU.log "AWS.myRegion: $MU_CFG['aws']", loglevel, details: $MU_CFG['aws']
269
287
  if credConfig.nil? and !hosted? and !ENV['EC2_REGION']
288
+ MU.log "AWS.myRegion: nothing of use set, returning", loglevel
270
289
  return nil
271
290
  end
272
291
 
273
292
  if $MU_CFG and $MU_CFG['aws']
274
293
  $MU_CFG['aws'].each_pair { |credset, cfg|
294
+ MU.log "AWS.myRegion: #{credset} != #{credentials} ?", loglevel, details: cfg
275
295
  next if credentials and credset != credentials
296
+ MU.log "AWS.myRegion: validating credset #{credset}", loglevel, details: cfg
276
297
  next if !cfg['region']
277
- if (cfg['default'] or !@@myRegion_var) and validate_region(cfg['region'], credentials: credset)
298
+ MU.log "AWS.myRegion: validation response", loglevel, details: validate_region(cfg['region'], credentials: credset)
299
+ if (cfg['default'] or !@@myRegion_var or $MU_CFG['aws'].size == 1) and validate_region(cfg['region'], credentials: credset)
300
+ MU.log "AWS.myRegion: liking this set", loglevel, details: cfg
278
301
  @@myRegion_var = cfg['region']
279
302
  break if cfg['default'] or credentials
280
303
  end
@@ -286,15 +309,21 @@ end
286
309
  (Aws.config['access_key'] and Aws.config['access_secret'])
287
310
  )
288
311
  # Make sure this string is valid by way of the API
312
+ MU.log "AWS.myRegion: using ENV", loglevel, details: ENV
289
313
  @@myRegion_var = ENV['EC2_REGION']
290
314
  end
291
315
 
292
316
  if hosted? and !@@myRegion_var
293
317
  # hacky, but useful in a pinch (and if we're hosted in AWS)
294
318
  az_str = MU::Cloud::AWS.getAWSMetaData("placement/availability-zone")
319
+ MU.log "AWS.myRegion: using hosted", loglevel, details: az_str
295
320
  @@myRegion_var = az_str.sub(/[a-z]$/i, "") if az_str
296
321
  end
297
322
 
323
+ if credConfig and credConfig["region"]
324
+ @@myRegion_var ||= credConfig["region"]
325
+ end
326
+
298
327
  @@myRegion_var
299
328
  end
300
329
 
@@ -382,8 +411,9 @@ end
382
411
  # Plant a Mu deploy secret into a storage bucket somewhere for so our kittens can consume it
383
412
  # @param deploy_id [String]: The deploy for which we're writing the secret
384
413
  # @param value [String]: The contents of the secret
385
- def self.writeDeploySecret(deploy_id, value, name = nil, credentials: nil)
386
- name ||= deploy_id+"-secret"
414
+ def self.writeDeploySecret(deploy, value, name = nil, credentials: nil)
415
+ require "aws-sdk-s3"
416
+ name ||= deploy.deploy_id+"-secret"
387
417
  begin
388
418
  MU.log "Writing #{name} to S3 bucket #{adminBucketName(credentials)}"
389
419
  MU::Cloud::AWS.s3(region: myRegion, credentials: credentials).put_object(
@@ -401,35 +431,35 @@ end
401
431
  def self.cloudtrailBucketPolicy(credentials = nil)
402
432
  cfg = credConfig(credentials)
403
433
  policy_json = '{
404
- "Version": "2012-10-17",
405
- "Statement": [
406
- {
407
- "Sid": "AWSCloudTrailAclCheck20131101",
408
- "Effect": "Allow",
434
+ "Version": "2012-10-17",
435
+ "Statement": [
436
+ {
437
+ "Sid": "AWSCloudTrailAclCheck20131101",
438
+ "Effect": "Allow",
409
439
  "Principal": {
410
440
  "AWS": "arn:'+(MU::Cloud::AWS.isGovCloud?(cfg['region']) ? "aws-us-gov" : "aws")+':iam::<%= MU.account_number %>:root",
411
441
  "Service": "cloudtrail.amazonaws.com"
412
442
  },
413
- "Action": "s3:GetBucketAcl",
414
- "Resource": "arn:'+(MU::Cloud::AWS.isGovCloud?(cfg['region']) ? "aws-us-gov" : "aws")+':s3:::'+MU::Cloud::AWS.adminBucketName(credentials)+'"
415
- },
416
- {
417
- "Sid": "AWSCloudTrailWrite20131101",
418
- "Effect": "Allow",
443
+ "Action": "s3:GetBucketAcl",
444
+ "Resource": "arn:'+(MU::Cloud::AWS.isGovCloud?(cfg['region']) ? "aws-us-gov" : "aws")+':s3:::'+MU::Cloud::AWS.adminBucketName(credentials)+'"
445
+ },
446
+ {
447
+ "Sid": "AWSCloudTrailWrite20131101",
448
+ "Effect": "Allow",
419
449
  "Principal": {
420
450
  "AWS": "arn:'+(MU::Cloud::AWS.isGovCloud?(cfg['region']) ? "aws-us-gov" : "aws")+':iam::'+credToAcct(credentials)+':root",
421
451
  "Service": "cloudtrail.amazonaws.com"
422
452
  },
423
- "Action": "s3:PutObject",
424
- "Resource": "arn:'+(MU::Cloud::AWS.isGovCloud?(cfg['region']) ? "aws-us-gov" : "aws")+':s3:::'+MU::Cloud::AWS.adminBucketName(credentials)+'/AWSLogs/'+credToAcct(credentials)+'/*",
425
- "Condition": {
426
- "StringEquals": {
427
- "s3:x-amz-acl": "bucket-owner-full-control"
428
- }
429
- }
430
- }
431
- ]
432
- }'
453
+ "Action": "s3:PutObject",
454
+ "Resource": "arn:'+(MU::Cloud::AWS.isGovCloud?(cfg['region']) ? "aws-us-gov" : "aws")+':s3:::'+MU::Cloud::AWS.adminBucketName(credentials)+'/AWSLogs/'+credToAcct(credentials)+'/*",
455
+ "Condition": {
456
+ "StringEquals": {
457
+ "s3:x-amz-acl": "bucket-owner-full-control"
458
+ }
459
+ }
460
+ }
461
+ ]
462
+ }'
433
463
  ERB.new(policy_json).result
434
464
  end
435
465
 
@@ -440,6 +470,53 @@ end
440
470
  MU::Cloud::AWS.hosted?
441
471
  end
442
472
 
473
+ # If we're in AWS and NVME-aware, return a mapping of AWS-side device
474
+ # names to actual NVME devices.
475
+ # @return [Hash]
476
+ def self.attachedNVMeDisks
477
+ if !hosted? or !File.executable?("/bin/lsblk") or !File.executable?("/sbin/nvme")
478
+ return {}
479
+ end
480
+ map = {}
481
+ devices = MU::Master.listBlockDevices
482
+ return {} if !devices
483
+ devices.each { |d|
484
+ if d =~ /^\/dev\/nvme/
485
+ %x{/sbin/nvme id-ctrl -v #{d}}.each_line { |desc|
486
+ if desc.match(/^0000: (?:[0-9a-f]{2} ){16}"(.+?)\./)
487
+ virt_dev = Regexp.last_match[1]
488
+ map[virt_dev] = d
489
+ break
490
+ end
491
+ }
492
+ end
493
+ }
494
+ map
495
+ end
496
+
497
+ # Map our own idea of what a block device is called back to whatever AWS
498
+ # and the operating system decided on amongst themselves. This currently
499
+ # exists to map generic "xvd[a-z]" style names back to real NVMe devices.
500
+ # @param dev [String]
501
+ def self.realDevicePath(dev)
502
+ return dev if !hosted?
503
+ value = nil
504
+ should_retry = Proc.new {
505
+ !value and MU::Master.nvme?
506
+ }
507
+ MU.retrier(loop_if: should_retry, wait: 5, max: 6) {
508
+ map = attachedNVMeDisks
509
+ value = if map[dev]
510
+ map[dev]
511
+ elsif map[dev.gsub(/.*?\//, '')]
512
+ map[dev.gsub(/.*?\//, '')]
513
+ else
514
+ dev # be nice to actually handle this too
515
+ end
516
+ }
517
+ value
518
+ end
519
+
443
520
  # Determine whether we (the Mu master, presumably) are hosted in this
444
521
  # cloud.
445
522
  # @return [Boolean]
@@ -457,7 +534,7 @@ end
457
534
 
458
535
  begin
459
536
  Timeout.timeout(4) do
460
- instance_id = open("http://169.254.169.254/latest/meta-data/instance-id").read
537
+ instance_id = URI.open("http://169.254.169.254/latest/meta-data/instance-id").read
461
538
  if !instance_id.nil? and instance_id.size > 0
462
539
  @@is_in_aws = true
463
540
  region = getAWSMetaData("placement/availability-zone").sub(/[a-z]$/i, "")
@@ -550,7 +627,9 @@ end
550
627
  def self.credToAcct(name = nil)
551
628
  creds = credConfig(name)
552
629
 
553
- return creds['account_number'] if creds['account_number']
630
+ if creds['account_number'] and !creds['account_number'].empty?
631
+ return creds['account_number']
632
+ end
554
633
 
555
634
  acct_num = MU::Cloud::AWS.iam(credentials: name).list_users.users.first.arn.split(/:/)[4]
556
635
  acct_num.to_s
@@ -564,17 +643,18 @@ end
564
643
  end
565
644
 
566
645
  $MU_CFG['aws'].keys
567
- end
646
+ end
568
647
 
569
648
  # Resolve the administrative S3 bucket for a given credential set, or
570
649
  # return a default.
571
650
  # @param credentials [String]
572
651
  # @return [String]
573
652
  def self.adminBucketName(credentials = nil)
653
+ require "aws-sdk-s3"
574
654
  cfg = credConfig(credentials)
575
655
  return nil if !cfg
576
656
  if !cfg['log_bucket_name']
577
- cfg['log_bucket_name'] = $MU_CFG['hostname']
657
+ cfg['log_bucket_name'] = $MU_CFG['hostname']
578
658
  MU.log "No AWS log bucket defined for credentials #{credentials}, attempting to use default of #{cfg['log_bucket_name']}", MU::WARN
579
659
  end
580
660
  resp = MU::Cloud::AWS.s3(credentials: credentials).list_buckets
@@ -608,7 +688,7 @@ end
608
688
 
609
689
  # Return the $MU_CFG data associated with a particular profile/name/set of
610
690
  # credentials. If no account name is specified, will return one flagged as
611
- # default. Returns nil if AWS is not configured. Throws an exception if
691
+ # default. Returns nil if AWS is not configured. Throws an exception if
612
692
  # an account name is specified which does not exist.
613
693
  # @param name [String]: The name of the key under 'aws' in mu.yaml to return
614
694
  # @return [Hash,nil]
@@ -623,10 +703,13 @@ end
623
703
 
624
704
  if hosted?
625
705
  begin
626
- iam_data = JSON.parse(getAWSMetaData("iam/info"))
627
- if iam_data["InstanceProfileArn"] and !iam_data["InstanceProfileArn"].empty?
628
- @@my_hosted_cfg = hosted_config
629
- return name_only ? "#default" : @@my_hosted_cfg
706
+ iam_blob = getAWSMetaData("iam/info")
707
+ if iam_blob
708
+ iam_data = JSON.parse(iam_blob)
709
+ if iam_data["InstanceProfileArn"] and !iam_data["InstanceProfileArn"].empty?
710
+ @@my_hosted_cfg = hosted_config
711
+ return name_only ? "#default" : @@my_hosted_cfg
712
+ end
630
713
  end
631
714
  rescue JSON::ParserError => e
632
715
  end
@@ -672,8 +755,8 @@ end
672
755
  next
673
756
  end
674
757
  acct_num = MU::Cloud::AWS.iam(credentials: acctname).list_users.users.first.arn.split(/:/)[4]
675
- if acct_num.to_s == name.to_s
676
- cfg['account_number'] = acct_num.to_s
758
+ cfg['account_number'] ||= acct_num.to_s
759
+ if acct_num.to_s == name.to_s
677
760
  @@acct_to_profile_map[name.to_s] = cfg
678
761
  return name_only ? name.to_s : cfg
679
762
  end
@@ -690,6 +773,7 @@ end
690
773
  # XXX this needs to be "myAccountNumber" or somesuch
691
774
  # XXX and maybe do the IAM thing for arbitrary, non-resident accounts
692
775
  def self.account_number
776
+ require "aws-sdk-ec2"
693
777
  return nil if credConfig.nil?
694
778
  return @@my_acct_num if @@my_acct_num
695
779
  loadCredentials
@@ -747,7 +831,7 @@ end
747
831
  @@regions.keys.uniq
748
832
  end
749
833
 
750
- # XXX GovCloud doesn't show up if you query a commercial endpoint... that's
834
+ # XXX GovCloud doesn't show up if you query a commercial endpoint... that's
751
835
  # *probably* ok for most purposes? We can't call listAZs on it from out here
752
836
  # apparently, so getting around it is nontrivial
753
837
  # if !@@regions.has_key?("us-gov-west-1")
@@ -772,6 +856,7 @@ end
772
856
  # @param public_key [String]: The public key
773
857
  # @return [Array<String>]: keypairname, ssh_private_key, ssh_public_key
774
858
  def self.createEc2SSHKey(keyname, public_key, credentials: nil)
859
+ require "aws-sdk-ec2"
775
860
  # We replicate this key in all regions
776
861
  if !MU::Cloud::CloudFormation.emitCloudFormation
777
862
  MU::Cloud::AWS.listRegions.each { |region|
@@ -800,8 +885,16 @@ end
800
885
  def self.listInstanceTypes(region = myRegion)
801
886
  return @@instance_types if @@instance_types and @@instance_types[region]
802
887
  return {} if credConfig.nil?
888
+ if region.nil?
889
+ region = myRegion(debug: true)
890
+ end
891
+ return {} if region.nil?
803
892
 
804
893
  human_region = @@regionLookup[region]
894
+ if human_region.nil?
895
+ MU.log "Failed to map a Pricing API region name from #{region}", MU::ERR
896
+ return {}
897
+ end
805
898
 
806
899
  @@instance_types ||= {}
807
900
  @@instance_types[region] ||= {}
@@ -844,6 +937,8 @@ end
844
937
  @@instance_types
845
938
  end
846
939
 
940
+ @@certificates = {}
941
+
847
942
  # AWS can stash API-available certificates in Amazon Certificate Manager
848
943
  # or in IAM. Rather than make people crazy trying to get the syntax
849
944
  # correct in our Baskets of Kittens, let's have a helper that tries to do
@@ -852,21 +947,25 @@ end
852
947
  # @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.
853
948
  # @param id [String]: The ARN of a known certificate. We just validate that it exists. This is ignored if a name parameter is supplied.
854
949
  # @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.
855
- def self.findSSLCertificate(name: nil, id: nil, region: myRegion)
856
- if name.nil? and name.empty? and id.nil? and id.empty?
950
+ def self.findSSLCertificate(name: nil, id: nil, region: myRegion, credentials: nil, raise_on_missing: true)
951
+ require "aws-sdk-iam"
952
+ if (name.nil? or name.empty?) and (id.nil? or id.empty?)
857
953
  raise MuError, "Can't call findSSLCertificate without specifying either a name or an id"
858
954
  end
955
+ if id and @@certificates[id]
956
+ return [id, @@certificates[id]]
957
+ end
859
958
 
860
959
  if !name.nil? and !name.empty?
861
960
  matches = []
862
- acmcerts = MU::Cloud::AWS.acm(region: region).list_certificates(
961
+ acmcerts = MU::Cloud::AWS.acm(region: region, credentials: credentials).list_certificates(
863
962
  certificate_statuses: ["ISSUED"]
864
963
  )
865
964
  acmcerts.certificate_summary_list.each { |cert|
866
965
  matches << cert.certificate_arn if cert.domain_name == name
867
966
  }
868
967
  begin
869
- iamcert = MU::Cloud::AWS.iam.get_server_certificate(
968
+ iamcert = MU::Cloud::AWS.iam(credentials: credentials).get_server_certificate(
870
969
  server_certificate_name: name
871
970
  )
872
971
  rescue Aws::IAM::Errors::ValidationError, Aws::IAM::Errors::NoSuchEntity
@@ -876,32 +975,45 @@ end
876
975
  matches << iamcert.server_certificate.server_certificate_metadata.arn
877
976
  end
878
977
  if matches.size == 1
879
- return matches.first
978
+ id = matches.first
880
979
  elsif matches.size == 0
881
- raise MuError, "No IAM or ACM certificate named #{name} was found in #{region}"
980
+ if raise_on_missing
981
+ raise MuError, "No IAM or ACM certificate named #{name} was found in #{region}"
982
+ else
983
+ return nil
984
+ end
882
985
  elsif matches.size > 1
883
- 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."
986
+ 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."
884
987
  end
885
988
  end
886
989
 
990
+ domains = []
991
+
887
992
  if id.match(/^arn:aws(?:-us-gov)?:acm/)
888
- resp = MU::Cloud::AWS.acm(region: region).get_certificate(
993
+ resp = MU::Cloud::AWS.acm(region: region).describe_certificate(
889
994
  certificate_arn: id
890
995
  )
891
- if resp.nil?
996
+
997
+ if resp.nil? or resp.certificate.nil?
892
998
  raise MuError, "No such ACM certificate '#{id}'"
893
999
  end
1000
+ domains << resp.certificate.domain_name
1001
+ if resp.certificate.subject_alternative_names
1002
+ domains.concat(resp.certificate.subject_alternative_names)
1003
+ end
894
1004
  elsif id.match(/^arn:aws(?:-us-gov)?:iam/)
895
1005
  resp = MU::Cloud::AWS.iam.list_server_certificates
896
1006
  if resp.nil?
897
1007
  raise MuError, "No such IAM certificate '#{id}'"
898
1008
  end
899
1009
  resp.server_certificate_metadata_list.each { |cert|
1010
+
900
1011
  if cert.arn == id
901
1012
  if cert.expiration < Time.now
902
1013
  MU.log "IAM SSL certificate #{cert.server_certificate_name} (#{id}) is EXPIRED", MU::WARN
903
1014
  end
904
- return id
1015
+ @@certificates[id] = [cert.server_certificate_name]
1016
+ return [id, [cert.server_certificate_name]]
905
1017
  end
906
1018
  }
907
1019
  raise MuError, "No such IAM certificate '#{id}'"
@@ -909,7 +1021,56 @@ end
909
1021
  raise MuError, "The format of '#{id}' doesn't look like an ARN for either Amazon Certificate Manager or IAM"
910
1022
  end
911
1023
 
912
- id
1024
+ @@certificates[id] = domains.uniq
1025
+ [id, domains.uniq]
1026
+ end
1027
+
1028
+ # Given a domain name and an ACM or IAM certificate identifier, sort out
1029
+ # whether the domain name is "covered" by the certificate
1030
+ # @param name [String]
1031
+ # @param cert_id [String]
1032
+ # @return [Boolean]
1033
+ def self.nameMatchesCertificate(name, cert_id)
1034
+ _id, domains = findSSLCertificate(id: cert_id)
1035
+ return false if !domains
1036
+ domains.each { |dom|
1037
+ if dom == name or
1038
+ (dom =~ /^\*/ and name =~ /.*#{Regexp.quote(dom[1..-1])}/)
1039
+ return true
1040
+ end
1041
+ }
1042
+ false
1043
+ end
1044
+
1045
+ # Given a {MU::Config::Ref} block for an IAM or ACM SSL certificate,
1046
+ # look up and validate the specified certificate. This is intended to be
1047
+ # invoked from resource implementations' +validateConfig+ methods.
1048
+ # @param certblock [Hash,MU::Config::Ref]:
1049
+ # @param region [String]: Default region to use when looking up the certificate, if its configuration block does not specify any
1050
+ # @param credentials [String]: Default credentials to use when looking up the certificate, if its configuration block does not specify any
1051
+ # @return [Boolean]
1052
+ def self.resolveSSLCertificate(certblock, region: nil, credentials: nil)
1053
+ return false if !certblock
1054
+ ok = true
1055
+
1056
+ certblock['region'] ||= region if !certblock['id']
1057
+ certblock['credentials'] ||= credentials
1058
+ cert_arn, cert_domains = MU::Cloud::AWS.findSSLCertificate(
1059
+ name: certblock["name"],
1060
+ id: certblock["id"],
1061
+ region: certblock['region'],
1062
+ credentials: certblock['credentials']
1063
+ )
1064
+
1065
+ if cert_arn
1066
+ certblock['id'] ||= cert_arn
1067
+ end
1068
+
1069
+ ['region', 'credentials'].each { |field|
1070
+ certblock.delete(field) if certblock[field].nil?
1071
+ }
1072
+
1073
+ [cert_arn, cert_domains]
913
1074
  end
914
1075
 
915
1076
  # Amazon Certificate Manager API
@@ -1029,6 +1190,14 @@ end
1029
1190
  @@cloudwatchlogs_api[credentials][region]
1030
1191
  end
1031
1192
 
1193
+ # Amazon's CloudWatchEvents API
1194
+ def self.cloudwatchevents(region: MU.curRegion, credentials: nil)
1195
+ region ||= myRegion
1196
+ @@cloudwatchevents_api[credentials] ||= {}
1197
+ @@cloudwatchevents_api[credentials][region] ||= MU::Cloud::AWS::AmazonEndpoint.new(api: "CloudWatchEvents", region: region, credentials: credentials)
1198
+ @@cloudwatchevents_api[credentials][region]
1199
+ end
1200
+
1032
1201
  # Amazon's CloudFront API
1033
1202
  def self.cloudfront(region: MU.curRegion, credentials: nil)
1034
1203
  region ||= myRegion
@@ -1044,7 +1213,7 @@ end
1044
1213
  @@elasticache_api[credentials][region] ||= MU::Cloud::AWS::AmazonEndpoint.new(api: "ElastiCache", region: region, credentials: credentials)
1045
1214
  @@elasticache_api[credentials][region]
1046
1215
  end
1047
-
1216
+
1048
1217
  # Amazon's SNS API
1049
1218
  def self.sns(region: MU.curRegion, credentials: nil)
1050
1219
  region ||= myRegion
@@ -1052,7 +1221,7 @@ end
1052
1221
  @@sns_api[credentials][region] ||= MU::Cloud::AWS::AmazonEndpoint.new(api: "SNS", region: region, credentials: credentials)
1053
1222
  @@sns_api[credentials][region]
1054
1223
  end
1055
-
1224
+
1056
1225
  # Amazon's SQS API
1057
1226
  def self.sqs(region: MU.curRegion, credentials: nil)
1058
1227
  region ||= myRegion
@@ -1084,7 +1253,7 @@ end
1084
1253
  @@apig_api[credentials][region] ||= MU::Cloud::AWS::AmazonEndpoint.new(api: "APIGateway", region: region, credentials: credentials)
1085
1254
  @@apig_api[credentials][region]
1086
1255
  end
1087
-
1256
+
1088
1257
  # Amazon's Cloudwatch Events API
1089
1258
  def self.cloudwatch_events(region = MU.cureRegion)
1090
1259
  region ||= myRegion
@@ -1117,6 +1286,14 @@ end
1117
1286
  @@dynamo_api[credentials][region]
1118
1287
  end
1119
1288
 
1289
+ # Amazon's DynamoStream API
1290
+ def self.dynamostream(region: MU.curRegion, credentials: nil)
1291
+ region ||= myRegion
1292
+ @@dynamostream_api[credentials] ||= {}
1293
+ @@dynamostream_api[credentials][region] ||= MU::Cloud::AWS::AmazonEndpoint.new(api: "DynamoDBStreams", region: region, credentials: credentials)
1294
+ @@dynamostream_api[credentials][region]
1295
+ end
1296
+
1120
1297
  # Amazon's Pricing API
1121
1298
  def self.pricing(region: MU.curRegion, credentials: nil)
1122
1299
  region ||= myRegion
@@ -1165,6 +1342,14 @@ end
1165
1342
  @@kms_api[credentials][region]
1166
1343
  end
1167
1344
 
1345
+ # Amazon's CloudFront API
1346
+ def self.cloudfront(region: MU.curRegion, credentials: nil)
1347
+ region ||= myRegion
1348
+ @@cloudfront_api[credentials] ||= {}
1349
+ @@cloudfront_api[credentials][region] ||= MU::Cloud::AWS::AmazonEndpoint.new(api: "CloudFront", region: region, credentials: credentials)
1350
+ @@cloudfront_api[credentials][region]
1351
+ end
1352
+
1168
1353
  # Amazon's Organizations API
1169
1354
  def self.orgs(credentials: nil)
1170
1355
  @@organizations_api ||= {}
@@ -1181,7 +1366,7 @@ end
1181
1366
  begin
1182
1367
  response = nil
1183
1368
  Timeout.timeout(1) do
1184
- response = open("#{base_url}/#{param}").read
1369
+ response = URI.open("#{base_url}/#{param}").read
1185
1370
  end
1186
1371
 
1187
1372
  response
@@ -1206,6 +1391,7 @@ end
1206
1391
  tag_value=MU.deploy_id,
1207
1392
  region: MU.curRegion,
1208
1393
  credentials: nil)
1394
+ require "aws-sdk-ec2"
1209
1395
  attempts = 0
1210
1396
 
1211
1397
  return nil if resource.nil?
@@ -1246,6 +1432,7 @@ end
1246
1432
  # Mu Master, if we're in AWS.
1247
1433
  # @return [void]
1248
1434
  def self.openFirewallForClients
1435
+ require "aws-sdk-ec2"
1249
1436
  MU::Cloud.resourceClass("AWS", :FirewallRule)
1250
1437
  begin
1251
1438
  if File.exist?(Etc.getpwuid(Process.uid).dir+"/.chef/knife.rb")
@@ -1425,6 +1612,7 @@ end
1425
1612
  def initialize(region: nil, api: "EC2", credentials: nil)
1426
1613
  @cred_obj = MU::Cloud::AWS.loadCredentials(credentials)
1427
1614
  @credentials = MU::Cloud::AWS.credConfig(credentials, name_only: true)
1615
+ @api_name = api
1428
1616
 
1429
1617
  if !@cred_obj
1430
1618
  raise MuError, "Unable to locate valid AWS credentials for #{api} API. #{credentials ? "Credentials requested were '#{credentials}'": ""}"
@@ -1442,6 +1630,8 @@ end
1442
1630
  params[:credentials] = @cred_obj
1443
1631
 
1444
1632
  MU.log "Initializing #{api} object with credentials #{credentials}", MU::DEBUG, details: params
1633
+ require "aws-sdk-#{api.downcase}"
1634
+
1445
1635
  @api = Object.const_get("Aws::#{api}::Client").new(params)
1446
1636
  end
1447
1637
 
@@ -1450,26 +1640,31 @@ end
1450
1640
  # rescues for known silly endpoint behavior.
1451
1641
  def method_missing(method_sym, *arguments)
1452
1642
  # make sure error symbols are loaded for our exception handling later
1453
- require "aws-sdk-core"
1454
- require "aws-sdk-core/rds"
1455
- require "aws-sdk-core/ec2"
1456
- require "aws-sdk-core/route53"
1457
- require "aws-sdk-core/iam"
1458
- require "aws-sdk-core/efs"
1459
- require "aws-sdk-core/pricing"
1460
- require "aws-sdk-core/apigateway"
1461
- require "aws-sdk-core/ecs"
1462
- require "aws-sdk-core/eks"
1463
- require "aws-sdk-core/cloudwatchlogs"
1464
- require "aws-sdk-core/elasticloadbalancing"
1465
- require "aws-sdk-core/elasticloadbalancingv2"
1466
- require "aws-sdk-core/autoscaling"
1467
- require "aws-sdk-core/client_waiters"
1468
- require "aws-sdk-core/waiters/errors"
1643
+ require "aws-sdk-lambda"
1644
+ require "aws-sdk-rds"
1645
+ require "aws-sdk-ec2"
1646
+ require "aws-sdk-route53"
1647
+ require "aws-sdk-iam"
1648
+ require "aws-sdk-efs"
1649
+ require "aws-sdk-pricing"
1650
+ require "aws-sdk-apigateway"
1651
+ require "aws-sdk-ecs"
1652
+ require "aws-sdk-eks"
1653
+ require "aws-sdk-cloudwatchlogs"
1654
+ require "aws-sdk-cloudwatchevents"
1655
+ require "aws-sdk-elasticloadbalancing"
1656
+ require "aws-sdk-elasticloadbalancingv2"
1657
+ require "aws-sdk-autoscaling"
1658
+
1659
+ known_concats = {
1660
+ "Pricing" => {
1661
+ :get_products => :price_list
1662
+ }
1663
+ }
1469
1664
 
1470
1665
  retries = 0
1471
1666
  begin
1472
- MU.log "Calling #{method_sym} in #{@region}", MU::DEBUG, details: arguments
1667
+ MU.log "Calling #{@api_name}.#{method_sym} in #{@region}", MU::DEBUG, details: arguments
1473
1668
 
1474
1669
  retval = if !arguments.nil? and arguments.size == 1
1475
1670
  @api.method(method_sym).call(arguments[0])
@@ -1481,24 +1676,39 @@ end
1481
1676
 
1482
1677
  if !retval.nil?
1483
1678
  begin
1484
- page_markers = [:marker, :next_token]
1679
+ page_markers = {
1680
+ :marker => :marker,
1681
+ :next_token => :next_token,
1682
+ :next_marker => :marker
1683
+ }
1485
1684
  paginator = nil
1486
1685
  new_page = nil
1487
- [:next_token, :marker].each { |m|
1686
+ page_markers.each_key { |m|
1488
1687
  if !retval.nil? and retval.respond_to?(m)
1489
1688
  paginator = m
1490
- new_page = retval.send(paginator)
1689
+ new_page = retval.send(m)
1491
1690
  break
1492
1691
  end
1493
1692
  }
1494
1693
 
1495
1694
  if paginator and new_page and !new_page.empty?
1496
1695
  resp = retval.respond_to?(:__getobj__) ? retval.__getobj__ : retval
1497
- concat_to = resp.class.instance_methods(false).reject { |m|
1696
+ concat_to = MU.structToHash(resp).keys.reject { |m|
1498
1697
  m.to_s.match(/=$/) or m == paginator or resp.send(m).nil? or !resp.send(m).is_a?(Array)
1499
1698
  }
1699
+
1700
+ if concat_to.empty? and known_concats[@api_name] and
1701
+ known_concats[@api_name][method_sym]
1702
+ concat_to << known_concats[@api_name][method_sym]
1703
+ end
1704
+
1705
+ if concat_to.empty? and method_sym.to_s.match(/^(?:describe|list)_(.*)/)
1706
+ my_attr = Regexp.last_match[1].to_sym
1707
+ concat_to << my_attr if resp.respond_to?(my_attr)
1708
+ end
1709
+
1500
1710
  if concat_to.size != 1
1501
- MU.log "Tried to figure out where I might append paginated results for a #{resp.class.name}, but failed", MU::DEBUG, details: concat_to
1711
+ raise MuError.new "Tried to figure out where I might append paginated results for a #{@api_name}.#{method_sym}, but failed", details: MU.structToHash(resp).keys
1502
1712
  else
1503
1713
  concat_to = concat_to.first
1504
1714
  new_args = arguments ? arguments.dup : [{}]
@@ -1506,12 +1716,12 @@ end
1506
1716
  if new_args.is_a?(Array)
1507
1717
  new_args << {} if new_args.empty?
1508
1718
  if new_args.size == 1 and new_args.first.is_a?(Hash)
1509
- new_args[0][paginator] = new_page
1719
+ new_args[0][page_markers[paginator]] = new_page
1510
1720
  else
1511
1721
  MU.log "I don't know how to insert a #{paginator} into these arguments for #{method_sym}", MU::WARN, details: new_args
1512
1722
  end
1513
1723
  elsif new_args.is_a?(Hash)
1514
- new_args[paginator] = new_page
1724
+ new_args[page_markers[paginator]] = new_page
1515
1725
  end
1516
1726
 
1517
1727
  MU.log "Attempting magic pagination for #{method_sym}", MU::DEBUG, details: new_args
@@ -1535,7 +1745,7 @@ end
1535
1745
  end
1536
1746
 
1537
1747
  return retval
1538
- rescue 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
1748
+ 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
1539
1749
  if e.class.name == "Seahorse::Client::NetworkingError" and e.message.match(/Name or service not known/)
1540
1750
  MU.log e.inspect, MU::ERR
1541
1751
  raise e
@@ -1577,6 +1787,7 @@ end
1577
1787
  @@wafglobal = {}
1578
1788
  @@waf = {}
1579
1789
  @@cloudwatchlogs_api = {}
1790
+ @@cloudwatchevents_api = {}
1580
1791
  @@cloudfront_api = {}
1581
1792
  @@elasticache_api = {}
1582
1793
  @@sns_api = {}
@@ -1595,6 +1806,8 @@ end
1595
1806
  @@kms_api ={}
1596
1807
  @@organization_api ={}
1597
1808
  @@dynamo_api ={}
1809
+ @@dynamostream_api ={}
1810
+ @@cloudfront_api ={}
1598
1811
  end
1599
1812
  end
1600
1813
  end