cloud-mu 3.1.6 → 3.2.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 (154) hide show
  1. checksums.yaml +4 -4
  2. data/bin/mu-adopt +4 -12
  3. data/bin/mu-azure-tests +57 -0
  4. data/bin/mu-cleanup +2 -4
  5. data/bin/mu-configure +37 -1
  6. data/bin/mu-deploy +3 -3
  7. data/bin/mu-findstray-tests +25 -0
  8. data/bin/mu-gen-docs +2 -4
  9. data/bin/mu-run-tests +23 -10
  10. data/cloud-mu.gemspec +2 -2
  11. data/cookbooks/mu-tools/libraries/helper.rb +1 -1
  12. data/cookbooks/mu-tools/recipes/apply_security.rb +14 -14
  13. data/cookbooks/mu-tools/recipes/aws_api.rb +9 -0
  14. data/extras/generate-stock-images +1 -0
  15. data/modules/mu.rb +82 -95
  16. data/modules/mu/adoption.rb +356 -56
  17. data/modules/mu/cleanup.rb +21 -20
  18. data/modules/mu/cloud.rb +79 -1753
  19. data/modules/mu/cloud/database.rb +49 -0
  20. data/modules/mu/cloud/dnszone.rb +46 -0
  21. data/modules/mu/cloud/machine_images.rb +212 -0
  22. data/modules/mu/cloud/providers.rb +81 -0
  23. data/modules/mu/cloud/resource_base.rb +920 -0
  24. data/modules/mu/cloud/server.rb +40 -0
  25. data/modules/mu/cloud/server_pool.rb +1 -0
  26. data/modules/mu/cloud/ssh_sessions.rb +228 -0
  27. data/modules/mu/cloud/winrm_sessions.rb +237 -0
  28. data/modules/mu/cloud/wrappers.rb +165 -0
  29. data/modules/mu/config.rb +122 -80
  30. data/modules/mu/config/alarm.rb +2 -6
  31. data/modules/mu/config/bucket.rb +1 -1
  32. data/modules/mu/config/cache_cluster.rb +1 -1
  33. data/modules/mu/config/collection.rb +1 -1
  34. data/modules/mu/config/container_cluster.rb +2 -2
  35. data/modules/mu/config/database.rb +83 -104
  36. data/modules/mu/config/database.yml +1 -2
  37. data/modules/mu/config/dnszone.rb +1 -1
  38. data/modules/mu/config/doc_helpers.rb +4 -5
  39. data/modules/mu/config/endpoint.rb +1 -1
  40. data/modules/mu/config/firewall_rule.rb +3 -19
  41. data/modules/mu/config/folder.rb +1 -1
  42. data/modules/mu/config/function.rb +1 -1
  43. data/modules/mu/config/group.rb +1 -1
  44. data/modules/mu/config/habitat.rb +1 -1
  45. data/modules/mu/config/loadbalancer.rb +57 -11
  46. data/modules/mu/config/log.rb +1 -1
  47. data/modules/mu/config/msg_queue.rb +1 -1
  48. data/modules/mu/config/nosqldb.rb +1 -1
  49. data/modules/mu/config/notifier.rb +1 -1
  50. data/modules/mu/config/ref.rb +30 -4
  51. data/modules/mu/config/role.rb +1 -1
  52. data/modules/mu/config/schema_helpers.rb +30 -34
  53. data/modules/mu/config/search_domain.rb +1 -1
  54. data/modules/mu/config/server.rb +4 -12
  55. data/modules/mu/config/server_pool.rb +3 -7
  56. data/modules/mu/config/storage_pool.rb +1 -1
  57. data/modules/mu/config/tail.rb +10 -0
  58. data/modules/mu/config/user.rb +1 -1
  59. data/modules/mu/config/vpc.rb +12 -17
  60. data/modules/mu/defaults/AWS.yaml +32 -32
  61. data/modules/mu/defaults/Azure.yaml +1 -0
  62. data/modules/mu/defaults/Google.yaml +1 -0
  63. data/modules/mu/deploy.rb +16 -15
  64. data/modules/mu/groomer.rb +15 -0
  65. data/modules/mu/groomers/chef.rb +3 -0
  66. data/modules/mu/logger.rb +120 -144
  67. data/modules/mu/master.rb +1 -1
  68. data/modules/mu/mommacat.rb +54 -25
  69. data/modules/mu/mommacat/daemon.rb +10 -7
  70. data/modules/mu/mommacat/naming.rb +82 -3
  71. data/modules/mu/mommacat/search.rb +47 -15
  72. data/modules/mu/mommacat/storage.rb +72 -41
  73. data/modules/mu/{clouds → providers}/README.md +1 -1
  74. data/modules/mu/{clouds → providers}/aws.rb +114 -47
  75. data/modules/mu/{clouds → providers}/aws/alarm.rb +1 -1
  76. data/modules/mu/{clouds → providers}/aws/bucket.rb +2 -2
  77. data/modules/mu/{clouds → providers}/aws/cache_cluster.rb +10 -46
  78. data/modules/mu/{clouds → providers}/aws/collection.rb +3 -3
  79. data/modules/mu/{clouds → providers}/aws/container_cluster.rb +15 -33
  80. data/modules/mu/providers/aws/database.rb +1744 -0
  81. data/modules/mu/{clouds → providers}/aws/dnszone.rb +2 -5
  82. data/modules/mu/{clouds → providers}/aws/endpoint.rb +2 -11
  83. data/modules/mu/{clouds → providers}/aws/firewall_rule.rb +33 -29
  84. data/modules/mu/{clouds → providers}/aws/folder.rb +0 -0
  85. data/modules/mu/{clouds → providers}/aws/function.rb +2 -10
  86. data/modules/mu/{clouds → providers}/aws/group.rb +9 -13
  87. data/modules/mu/{clouds → providers}/aws/habitat.rb +1 -1
  88. data/modules/mu/{clouds → providers}/aws/loadbalancer.rb +41 -33
  89. data/modules/mu/{clouds → providers}/aws/log.rb +2 -2
  90. data/modules/mu/{clouds → providers}/aws/msg_queue.rb +2 -8
  91. data/modules/mu/{clouds → providers}/aws/nosqldb.rb +0 -0
  92. data/modules/mu/{clouds → providers}/aws/notifier.rb +0 -0
  93. data/modules/mu/{clouds → providers}/aws/role.rb +7 -7
  94. data/modules/mu/{clouds → providers}/aws/search_domain.rb +8 -13
  95. data/modules/mu/{clouds → providers}/aws/server.rb +55 -90
  96. data/modules/mu/{clouds → providers}/aws/server_pool.rb +10 -33
  97. data/modules/mu/{clouds → providers}/aws/storage_pool.rb +19 -36
  98. data/modules/mu/{clouds → providers}/aws/user.rb +8 -12
  99. data/modules/mu/{clouds → providers}/aws/userdata/README.md +0 -0
  100. data/modules/mu/{clouds → providers}/aws/userdata/linux.erb +0 -0
  101. data/modules/mu/{clouds → providers}/aws/userdata/windows.erb +0 -0
  102. data/modules/mu/{clouds → providers}/aws/vpc.rb +135 -70
  103. data/modules/mu/{clouds → providers}/aws/vpc_subnet.rb +0 -0
  104. data/modules/mu/{clouds → providers}/azure.rb +4 -1
  105. data/modules/mu/{clouds → providers}/azure/container_cluster.rb +1 -5
  106. data/modules/mu/{clouds → providers}/azure/firewall_rule.rb +8 -1
  107. data/modules/mu/{clouds → providers}/azure/habitat.rb +0 -0
  108. data/modules/mu/{clouds → providers}/azure/loadbalancer.rb +0 -0
  109. data/modules/mu/{clouds → providers}/azure/role.rb +0 -0
  110. data/modules/mu/{clouds → providers}/azure/server.rb +30 -23
  111. data/modules/mu/{clouds → providers}/azure/user.rb +1 -1
  112. data/modules/mu/{clouds → providers}/azure/userdata/README.md +0 -0
  113. data/modules/mu/{clouds → providers}/azure/userdata/linux.erb +0 -0
  114. data/modules/mu/{clouds → providers}/azure/userdata/windows.erb +0 -0
  115. data/modules/mu/{clouds → providers}/azure/vpc.rb +4 -6
  116. data/modules/mu/{clouds → providers}/cloudformation.rb +1 -1
  117. data/modules/mu/{clouds → providers}/cloudformation/alarm.rb +3 -3
  118. data/modules/mu/{clouds → providers}/cloudformation/cache_cluster.rb +3 -3
  119. data/modules/mu/{clouds → providers}/cloudformation/collection.rb +3 -3
  120. data/modules/mu/{clouds → providers}/cloudformation/database.rb +6 -17
  121. data/modules/mu/{clouds → providers}/cloudformation/dnszone.rb +3 -3
  122. data/modules/mu/{clouds → providers}/cloudformation/firewall_rule.rb +3 -3
  123. data/modules/mu/{clouds → providers}/cloudformation/loadbalancer.rb +3 -3
  124. data/modules/mu/{clouds → providers}/cloudformation/log.rb +3 -3
  125. data/modules/mu/{clouds → providers}/cloudformation/server.rb +7 -7
  126. data/modules/mu/{clouds → providers}/cloudformation/server_pool.rb +5 -5
  127. data/modules/mu/{clouds → providers}/cloudformation/vpc.rb +3 -3
  128. data/modules/mu/{clouds → providers}/docker.rb +0 -0
  129. data/modules/mu/{clouds → providers}/google.rb +14 -6
  130. data/modules/mu/{clouds → providers}/google/bucket.rb +1 -1
  131. data/modules/mu/{clouds → providers}/google/container_cluster.rb +28 -13
  132. data/modules/mu/{clouds → providers}/google/database.rb +1 -8
  133. data/modules/mu/{clouds → providers}/google/firewall_rule.rb +2 -2
  134. data/modules/mu/{clouds → providers}/google/folder.rb +4 -8
  135. data/modules/mu/{clouds → providers}/google/function.rb +3 -3
  136. data/modules/mu/{clouds → providers}/google/group.rb +8 -16
  137. data/modules/mu/{clouds → providers}/google/habitat.rb +3 -7
  138. data/modules/mu/{clouds → providers}/google/loadbalancer.rb +1 -1
  139. data/modules/mu/{clouds → providers}/google/role.rb +42 -34
  140. data/modules/mu/{clouds → providers}/google/server.rb +25 -10
  141. data/modules/mu/{clouds → providers}/google/server_pool.rb +10 -10
  142. data/modules/mu/{clouds → providers}/google/user.rb +31 -21
  143. data/modules/mu/{clouds → providers}/google/userdata/README.md +0 -0
  144. data/modules/mu/{clouds → providers}/google/userdata/linux.erb +0 -0
  145. data/modules/mu/{clouds → providers}/google/userdata/windows.erb +0 -0
  146. data/modules/mu/{clouds → providers}/google/vpc.rb +37 -2
  147. data/modules/tests/centos6.yaml +11 -0
  148. data/modules/tests/centos7.yaml +11 -0
  149. data/modules/tests/centos8.yaml +12 -0
  150. data/modules/tests/rds.yaml +108 -0
  151. data/modules/tests/regrooms/rds.yaml +123 -0
  152. data/spec/mu/clouds/azure_spec.rb +2 -2
  153. metadata +108 -89
  154. data/modules/mu/clouds/aws/database.rb +0 -1974
@@ -88,16 +88,28 @@ module MU
88
88
  }
89
89
  end
90
90
 
91
+
91
92
  # Overwrite this deployment's configuration with a new version. Save the
92
93
  # previous version as well.
93
94
  # @param new_conf [Hash]: A new configuration, fully resolved by {MU::Config}
94
- def updateBasketofKittens(new_conf)
95
+ def updateBasketofKittens(new_conf, skip_validation: false, new_metadata: nil, save_now: false)
95
96
  loadDeploy
96
97
  if new_conf == @original_config
97
- MU.log "#{@deploy_id}", MU::WARN
98
98
  return
99
99
  end
100
100
 
101
+ scrub_with = nil
102
+
103
+ # Make sure the new config that we were just handed resolves and makes
104
+ # sense
105
+ if !skip_validation
106
+ f = Tempfile.new(@deploy_id)
107
+ f.write JSON.parse(JSON.generate(new_conf)).to_yaml
108
+ conf_engine = MU::Config.new(f.path) # will throw an exception if it's bad, adoption should catch this and cope reasonably
109
+ scrub_with = conf_engine.config
110
+ f.close
111
+ end
112
+
101
113
  backup = "#{deploy_dir}/basket_of_kittens.json.#{Time.now.to_i.to_s}"
102
114
  MU.log "Saving previous config of #{@deploy_id} to #{backup}"
103
115
  config = File.new(backup, File::CREAT|File::TRUNC|File::RDWR, 0600)
@@ -106,9 +118,26 @@ module MU
106
118
  config.flock(File::LOCK_UN)
107
119
  config.close
108
120
 
109
- @original_config = new_conf
110
- # save! # XXX this will happen later, more sensibly
111
- MU.log "New config saved to #{deploy_dir}/basket_of_kittens.json"
121
+ @original_config = new_conf.clone
122
+
123
+ MU::Cloud.resource_types.each_pair { |res_type, attrs|
124
+ next if !@deployment.has_key?(attrs[:cfg_plural])
125
+ deletia = []
126
+ @deployment[attrs[:cfg_plural]].each_pair { |res_name, data|
127
+ orig_cfg = findResourceConfig(attrs[:cfg_plural], res_name, (scrub_with || @original_config))
128
+
129
+ if orig_cfg.nil?
130
+ MU.log "#{res_type} #{res_name} no longer configured, will remove deployment metadata", MU::NOTICE
131
+ deletia << res_name
132
+ end
133
+ }
134
+ @deployment[attrs[:cfg_plural]].reject! { |k, v| deletia.include?(k) }
135
+ }
136
+
137
+ if save_now
138
+ save!
139
+ MU.log "New config saved to #{deploy_dir}/basket_of_kittens.json"
140
+ end
112
141
  end
113
142
 
114
143
  @lock_semaphore = Mutex.new
@@ -147,11 +176,11 @@ module MU
147
176
  # @param id [String]: The lock identifier to release.
148
177
  # @param nonblock [Boolean]: Whether to block while waiting for the lock. In non-blocking mode, we simply return false if the lock is not available.
149
178
  # return [false, nil]
150
- def self.lock(id, nonblock = false, global = false)
179
+ def self.lock(id, nonblock = false, global = false, deploy_id: MU.deploy_id)
151
180
  raise MuError, "Can't pass a nil id to MU::MommaCat.lock" if id.nil?
152
181
 
153
182
  if !global
154
- lockdir = "#{deploy_dir(MU.deploy_id)}/locks"
183
+ lockdir = "#{deploy_dir(deploy_id)}/locks"
155
184
  else
156
185
  lockdir = File.expand_path(MU.dataDir+"/locks")
157
186
  end
@@ -186,11 +215,11 @@ module MU
186
215
 
187
216
  # Release a flock() lock.
188
217
  # @param id [String]: The lock identifier to release.
189
- def self.unlock(id, global = false)
218
+ def self.unlock(id, global = false, deploy_id: MU.deploy_id)
190
219
  raise MuError, "Can't pass a nil id to MU::MommaCat.unlock" if id.nil?
191
220
  lockdir = nil
192
221
  if !global
193
- lockdir = "#{deploy_dir(MU.deploy_id)}/locks"
222
+ lockdir = "#{deploy_dir(deploy_id)}/locks"
194
223
  else
195
224
  lockdir = File.expand_path(MU.dataDir+"/locks")
196
225
  end
@@ -512,48 +541,30 @@ module MU
512
541
  if !@original_config['scrub_mu_isms'] and !@no_artifacts
513
542
  credsets.each_pair { |cloud, creds|
514
543
  creds.uniq!
515
- cloudclass = Object.const_get("MU").const_get("Cloud").const_get(cloud)
516
544
  creds.each { |credentials|
517
- cloudclass.writeDeploySecret(@deploy_id, @deploy_secret, credentials: credentials)
545
+ MU::Cloud.cloudClass(cloud).writeDeploySecret(@deploy_id, @deploy_secret, credentials: credentials)
518
546
  }
519
547
  }
520
548
  end
521
549
  end
522
550
 
523
551
  def loadObjects(delay_descriptor_load)
552
+ # Load up MU::Cloud objects for all our kittens in this deploy
553
+
524
554
  MU::Cloud.resource_types.each_pair { |res_type, attrs|
525
555
  type = attrs[:cfg_plural]
526
556
  next if !@deployment.has_key?(type)
527
557
 
558
+ deletia = {}
528
559
  @deployment[type].each_pair { |res_name, data|
529
- orig_cfg = nil
530
- if @original_config.has_key?(type)
531
- @original_config[type].each { |resource|
532
- if resource["name"] == res_name
533
- orig_cfg = resource
534
- break
535
- end
536
- }
537
- end
538
-
539
- # Some Server objects originated from ServerPools, get their
540
- # configs from there
541
- if type == "servers" and orig_cfg.nil? and
542
- @original_config.has_key?("server_pools")
543
- @original_config["server_pools"].each { |resource|
544
- if resource["name"] == res_name
545
- orig_cfg = resource
546
- break
547
- end
548
- }
549
- end
560
+ orig_cfg = findResourceConfig(type, res_name)
550
561
 
551
562
  if orig_cfg.nil?
552
563
  MU.log "Failed to locate original config for #{attrs[:cfg_name]} #{res_name} in #{@deploy_id}", MU::WARN if !["firewall_rules", "databases", "storage_pools", "cache_clusters", "alarms"].include?(type) # XXX shaddap
553
564
  next
554
565
  end
555
566
 
556
- if orig_cfg['vpc'] and orig_cfg['vpc'].is_a?(Hash)
567
+ if orig_cfg['vpc']
557
568
  ref = if orig_cfg['vpc']['id'] and orig_cfg['vpc']['id'].is_a?(Hash)
558
569
  orig_cfg['vpc']['id']['mommacat'] = self
559
570
  MU::Config::Ref.get(orig_cfg['vpc']['id'])
@@ -566,18 +577,12 @@ module MU
566
577
  end
567
578
 
568
579
  begin
569
- # Load up MU::Cloud objects for all our kittens in this deploy
570
- orig_cfg['environment'] = @environment # not always set in old deploys
571
580
  if attrs[:has_multiples]
572
581
  data.keys.each { |mu_name|
573
- attrs[:interface].new(mommacat: self, kitten_cfg: orig_cfg, mu_name: mu_name, delay_descriptor_load: delay_descriptor_load)
582
+ addKitten(type, res_name, attrs[:interface].new(mommacat: self, kitten_cfg: orig_cfg, mu_name: mu_name, delay_descriptor_load: delay_descriptor_load))
574
583
  }
575
584
  else
576
- # XXX hack for old deployments, this can go away some day
577
- if data['mu_name'].nil?
578
- raise MuError, "Unable to find or guess a Mu name for #{res_type}: #{res_name} in #{@deploy_id}"
579
- end
580
- attrs[:interface].new(mommacat: self, kitten_cfg: orig_cfg, mu_name: data['mu_name'], cloud_id: data['cloud_id'])
585
+ addKitten(type, res_name, attrs[:interface].new(mommacat: self, kitten_cfg: orig_cfg, mu_name: data['mu_name'], cloud_id: data['cloud_id']))
581
586
  end
582
587
  rescue StandardError => e
583
588
  if e.class != MU::Cloud::MuCloudResourceNotImplemented
@@ -585,6 +590,7 @@ module MU
585
590
  end
586
591
  end
587
592
  }
593
+
588
594
  }
589
595
  end
590
596
 
@@ -687,5 +693,30 @@ module MU
687
693
  }
688
694
  end
689
695
 
696
+ def findResourceConfig(type, name, config = @original_config)
697
+ orig_cfg = nil
698
+ if config.has_key?(type)
699
+ config[type].each { |resource|
700
+ if resource["name"] == name
701
+ orig_cfg = resource
702
+ break
703
+ end
704
+ }
705
+ end
706
+
707
+ # Some Server objects originated from ServerPools, get their
708
+ # configs from there
709
+ if type == "servers" and orig_cfg.nil? and config.has_key?("server_pools")
710
+ config["server_pools"].each { |resource|
711
+ if resource["name"] == name
712
+ orig_cfg = resource
713
+ break
714
+ end
715
+ }
716
+ end
717
+
718
+ orig_cfg
719
+ end
720
+
690
721
  end #class
691
722
  end #module
@@ -89,7 +89,7 @@ Looking elsewhere in `cloud.rb` let's see what all we have to do:
89
89
  generic_instance_methods = [:create, :notify, :mu_name, :cloud_id, :config]
90
90
  ```
91
91
 
92
- Just the basics, for now. Here's what that will look like in the AWS layer, in the file `modules/mu/clouds/aws/function.rb`:
92
+ Just the basics, for now. Here's what that will look like in the AWS layer, in the file `modules/mu/providers/aws/function.rb`:
93
93
 
94
94
  ```
95
95
  module MU
@@ -39,7 +39,7 @@ module MU
39
39
  end
40
40
 
41
41
  # List all AWS projects available to our credentials
42
- def self.listHabitats(credentials = nil)
42
+ def self.listHabitats(credentials = nil, use_cache: true)
43
43
  cfg = credConfig(credentials)
44
44
  return [] if !cfg or !cfg['account_number']
45
45
  [cfg['account_number']]
@@ -805,46 +805,41 @@ end
805
805
 
806
806
  @@instance_types ||= {}
807
807
  @@instance_types[region] ||= {}
808
- next_token = nil
809
808
 
810
- begin
811
- # Pricing API isn't widely available, so ask a region we know supports
812
- # it
813
- resp = MU::Cloud::AWS.pricing(region: "us-east-1").get_products(
814
- service_code: "AmazonEC2",
815
- filters: [
816
- {
817
- field: "productFamily",
818
- value: "Compute Instance",
819
- type: "TERM_MATCH"
820
- },
821
- {
822
- field: "tenancy",
823
- value: "Shared",
824
- type: "TERM_MATCH"
825
- },
826
- {
827
- field: "location",
828
- value: human_region,
829
- type: "TERM_MATCH"
830
- }
831
- ],
832
- next_token: next_token
833
- )
834
- resp.price_list.each { |pricing|
835
- data = JSON.parse(pricing)
836
- type = data["product"]["attributes"]["instanceType"]
837
- next if @@instance_types[region].has_key?(type)
838
- @@instance_types[region][type] = {}
839
- ["ecu", "vcpu", "memory", "storage"].each { |a|
840
- @@instance_types[region][type][a] = data["product"]["attributes"][a]
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"
841
828
  }
842
- @@instance_types[region][type]["memory"].sub!(/ GiB/, "")
843
- @@instance_types[region][type]["memory"] = @@instance_types[region][type]["memory"].to_f
844
- @@instance_types[region][type]["vcpu"] = @@instance_types[region][type]["vcpu"].to_f
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]
845
838
  }
846
- next_token = resp.next_token
847
- 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
+ }
848
843
 
849
844
  @@instance_types
850
845
  end
@@ -1251,7 +1246,7 @@ end
1251
1246
  # Mu Master, if we're in AWS.
1252
1247
  # @return [void]
1253
1248
  def self.openFirewallForClients
1254
- MU::Cloud.loadCloudType("AWS", :FirewallRule)
1249
+ MU::Cloud.resourceClass("AWS", :FirewallRule)
1255
1250
  begin
1256
1251
  if File.exist?(Etc.getpwuid(Process.uid).dir+"/.chef/knife.rb")
1257
1252
  ::Chef::Config.from_file(Etc.getpwuid(Process.uid).dir+"/.chef/knife.rb")
@@ -1454,21 +1449,93 @@ end
1454
1449
  # Catch-all for AWS client methods. Essentially a pass-through with some
1455
1450
  # rescues for known silly endpoint behavior.
1456
1451
  def method_missing(method_sym, *arguments)
1452
+ # make sure error symbols are loaded for our exception handling later
1457
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"
1458
1469
 
1459
1470
  retries = 0
1460
1471
  begin
1461
1472
  MU.log "Calling #{method_sym} in #{@region}", MU::DEBUG, details: arguments
1462
- retval = nil
1463
- if !arguments.nil? and arguments.size == 1
1464
- retval = @api.method(method_sym).call(arguments[0])
1465
- elsif !arguments.nil? and arguments.size > 0
1466
- retval = @api.method(method_sym).call(*arguments)
1467
- else
1468
- retval = @api.method(method_sym).call
1473
+
1474
+ retval = if !arguments.nil? and arguments.size == 1
1475
+ @api.method(method_sym).call(arguments[0])
1476
+ elsif !arguments.nil? and arguments.size > 0
1477
+ @api.method(method_sym).call(*arguments)
1478
+ else
1479
+ @api.method(method_sym).call
1480
+ end
1481
+
1482
+ if !retval.nil?
1483
+ begin
1484
+ page_markers = [:marker, :next_token]
1485
+ paginator = nil
1486
+ new_page = nil
1487
+ [:next_token, :marker].each { |m|
1488
+ if !retval.nil? and retval.respond_to?(m)
1489
+ paginator = m
1490
+ new_page = retval.send(paginator)
1491
+ break
1492
+ end
1493
+ }
1494
+
1495
+ if paginator and new_page and !new_page.empty?
1496
+ resp = retval.respond_to?(:__getobj__) ? retval.__getobj__ : retval
1497
+ concat_to = resp.class.instance_methods(false).reject { |m|
1498
+ m.to_s.match(/=$/) or m == paginator or resp.send(m).nil? or !resp.send(m).is_a?(Array)
1499
+ }
1500
+ 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
1502
+ else
1503
+ concat_to = concat_to.first
1504
+ new_args = arguments ? arguments.dup : [{}]
1505
+ begin
1506
+ if new_args.is_a?(Array)
1507
+ new_args << {} if new_args.empty?
1508
+ if new_args.size == 1 and new_args.first.is_a?(Hash)
1509
+ new_args[0][paginator] = new_page
1510
+ else
1511
+ MU.log "I don't know how to insert a #{paginator} into these arguments for #{method_sym}", MU::WARN, details: new_args
1512
+ end
1513
+ elsif new_args.is_a?(Hash)
1514
+ new_args[paginator] = new_page
1515
+ end
1516
+
1517
+ MU.log "Attempting magic pagination for #{method_sym}", MU::DEBUG, details: new_args
1518
+
1519
+ # resp = if !arguments.nil? and arguments.size == 1
1520
+ # @api.method(method_sym).call(new_args[0])
1521
+ # elsif !arguments.nil? and arguments.size > 0
1522
+ resp = @api.method(method_sym).call(*new_args)
1523
+ # end
1524
+ break if resp.nil?
1525
+ resp = resp.__getobj__ if resp.respond_to?(:__getobj__)
1526
+ retval.send(concat_to).concat(resp.send(concat_to))
1527
+ new_page = resp.send(paginator) if !resp.nil?
1528
+ end while !resp.nil? and !new_page.nil? and !new_page.empty?
1529
+ end
1530
+ end
1531
+ rescue StandardError => e
1532
+ MU.log "Made a good-faith effort to auto-paginate API call to #{method_sym} and failed with #{e.message}", MU::DEBUG, details: arguments
1533
+ raise e
1534
+ end
1469
1535
  end
1536
+
1470
1537
  return retval
1471
- rescue Aws::EC2::Errors::InternalError, Aws::EC2::Errors::RequestLimitExceeded, Aws::EC2::Errors::Unavailable, Aws::Route53::Errors::Throttling, Aws::ElasticLoadBalancing::Errors::HttpFailureException, Aws::EC2::Errors::Http503Error, Aws::AutoScaling::Errors::Http503Error, Aws::AutoScaling::Errors::InternalFailure, Aws::AutoScaling::Errors::ServiceUnavailable, Aws::Route53::Errors::ServiceUnavailable, Aws::ElasticLoadBalancing::Errors::Throttling, Aws::RDS::Errors::ClientUnavailable, Aws::Waiters::Errors::UnexpectedError, Aws::ElasticLoadBalancing::Errors::ServiceUnavailable, Aws::ElasticLoadBalancingV2::Errors::Throttling, Seahorse::Client::NetworkingError, Aws::IAM::Errors::Throttling, Aws::EFS::Errors::ThrottlingException, Aws::Pricing::Errors::ThrottlingException, Aws::APIGateway::Errors::TooManyRequestsException, Aws::ECS::Errors::ThrottlingException, Net::ReadTimeout, Faraday::TimeoutError, Aws::CloudWatchLogs::Errors::ThrottlingException => e
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
1472
1539
  if e.class.name == "Seahorse::Client::NetworkingError" and e.message.match(/Name or service not known/)
1473
1540
  MU.log e.inspect, MU::ERR
1474
1541
  raise e
@@ -321,7 +321,7 @@ module MU
321
321
  if !depclass.nil?
322
322
  dimension["depclass"] = depclass
323
323
  if !dimension["name"].nil? and !dimension["name"].empty?
324
- alarm["dependencies"] << { "name" => dimension["name"], "type" => depclass }
324
+ MU::Config.addDependency(alarm, dimension["name"], depclass)
325
325
  end
326
326
  end
327
327
  }
@@ -97,7 +97,7 @@ module MU
97
97
  ]
98
98
  }
99
99
 
100
- policy_docs = MU::Cloud::AWS::Role.genPolicyDocument(@config['policies'], deploy_obj: @deploy, bucket_style: true)
100
+ policy_docs = MU::Cloud.resourceClass("AWS", "Role").genPolicyDocument(@config['policies'], deploy_obj: @deploy, bucket_style: true)
101
101
  policy_docs.each { |doc|
102
102
  MU.log "Applying S3 bucket policy #{doc.keys.first} to bucket #{@cloud_id}", MU::NOTICE, details: JSON.pretty_generate(doc.values.first)
103
103
  MU::Cloud::AWS.s3(credentials: @config['credentials'], region: @config['region']).put_bucket_policy(
@@ -309,7 +309,7 @@ module MU
309
309
  def self.schema(_config)
310
310
  toplevel_required = []
311
311
  schema = {
312
- "policies" => MU::Cloud::AWS::Role.condition_schema,
312
+ "policies" => MU::Cloud.resourceClass("AWS", "Role").condition_schema,
313
313
  "acl" => {
314
314
  "type" => "string",
315
315
  "enum" => ["private", "public-read", "public-read-write", "authenticated-read"],
@@ -199,7 +199,7 @@ module MU
199
199
  addStandardTags(member, "cluster", region: @config['region'])
200
200
  }
201
201
 
202
- MU::Cloud::AWS::DNSZone.genericMuDNSEntry(
202
+ MU::Cloud.resourceClass("AWS", "DNSZone").genericMuDNSEntry(
203
203
  name: resp.replication_group_id,
204
204
  target: "#{resp.node_groups.first.primary_endpoint.address}.",
205
205
  cloudclass: MU::Cloud::CacheCluster,
@@ -207,7 +207,7 @@ module MU
207
207
  )
208
208
 
209
209
  resp.node_groups.first.node_group_members.each { |member|
210
- MU::Cloud::AWS::DNSZone.genericMuDNSEntry(
210
+ MU::Cloud.resourceClass("AWS", "DNSZone").genericMuDNSEntry(
211
211
  name: member.cache_cluster_id,
212
212
  target: "#{member.read_endpoint.address}.",
213
213
  cloudclass: MU::Cloud::CacheCluster,
@@ -270,7 +270,7 @@ module MU
270
270
  def createSubnetGroup
271
271
  subnet_ids = []
272
272
  if @config["vpc"] && !@config["vpc"].empty?
273
- raise MuError, "Didn't find the VPC specified in #{@config["vpc"]}" unless @vpc
273
+ raise MuError.new "Didn't find the VPC specified for #{@mu_name}", details: @config["vpc"].to_h unless @vpc
274
274
 
275
275
  vpc_id = @vpc.cloud_id
276
276
 
@@ -283,7 +283,7 @@ module MU
283
283
  else
284
284
  @config["vpc"]["subnets"].each { |subnet|
285
285
  subnet_obj = @vpc.getSubnet(cloud_id: subnet["subnet_id"].to_s, name: subnet["subnet_name"].to_s)
286
- raise MuError, "Couldn't find a live subnet matching #{subnet} in #{@vpc} (#{@vpc.subnets})" if subnet_obj.nil?
286
+ raise MuError.new "Couldn't find a live subnet matching #{subnet} in #{@vpc}", details: @vpc.subnets if subnet_obj.nil?
287
287
  subnet_ids << subnet_obj.cloud_id
288
288
  }
289
289
  end
@@ -333,24 +333,7 @@ module MU
333
333
  subnet_ids: subnet_ids
334
334
  )
335
335
 
336
- # Find NAT and create holes in security groups.
337
- # Adding just for consistency, but do we really need this for cache clusters? I guess Nagios and such..
338
- if @config["vpc"]["nat_host_name"] || @config["vpc"]["nat_host_id"] || @config["vpc"]["nat_host_tag"] || @config["vpc"]["nat_host_ip"]
339
- nat = @nat
340
- if nat.is_a?(Struct) && nat.nat_gateway_id && nat.nat_gateway_id.start_with?("nat-")
341
- MU.log "Using NAT Gateway, not modifying security groups"
342
- else
343
- _nat_name, _nat_conf, nat_deploydata = @nat.describe
344
- @deploy.kittens['firewall_rules'].values.each { |acl|
345
- # XXX if a user doesn't set up dependencies correctly, this can die horribly on a NAT that's still in mid-creation. Fix this... possibly in the config parser.
346
- if acl.config["admin"]
347
- acl.addRule([nat_deploydata["private_ip_address"]], proto: "tcp")
348
- acl.addRule([nat_deploydata["private_ip_address"]], proto: "udp")
349
- break
350
- end
351
- }
352
- end
353
- end
336
+ allowBastionAccess
354
337
 
355
338
  if @dependencies.has_key?('firewall_rule')
356
339
  @config["security_group_ids"] = []
@@ -430,7 +413,7 @@ module MU
430
413
  }
431
414
  end
432
415
  # XXX this should be a call to @deploy.nameKitten
433
- MU::Cloud::AWS::DNSZone.createRecordsFromConfig(@config['dns_records'], target: repl_group.node_groups.first.primary_endpoint.address)
416
+ MU::Cloud.resourceClass("AWS", "DNSZone").createRecordsFromConfig(@config['dns_records'], target: repl_group.node_groups.first.primary_endpoint.address)
434
417
 
435
418
  deploy_struct = {
436
419
  "identifier" => repl_group.replication_group_id,
@@ -703,26 +686,7 @@ module MU
703
686
  "type" => "boolean",
704
687
  "description" => "Create a replication group; will be set automatically if +engine+ is +redis+ and +node_count+ is greated than one."
705
688
  },
706
- "ingress_rules" => {
707
- "items" => {
708
- "properties" => {
709
- "sgs" => {
710
- "type" => "array",
711
- "items" => {
712
- "description" => "Other AWS Security Groups; resources that are associated with this group will have this rule applied to their traffic",
713
- "type" => "string"
714
- }
715
- },
716
- "lbs" => {
717
- "type" => "array",
718
- "items" => {
719
- "description" => "AWS Load Balancers which will have this rule applied to their traffic",
720
- "type" => "string"
721
- }
722
- }
723
- }
724
- }
725
- }
689
+ "ingress_rules" => MU::Cloud.resourceClass("AWS", "FirewallRule").ingressRuleAddtlSchema
726
690
  }
727
691
  [toplevel_required, schema]
728
692
  end
@@ -823,7 +787,7 @@ module MU
823
787
  end
824
788
 
825
789
  # The API is broken, cluster.cache_nodes is returnning an empty array, and the only URL we can get is the config one with cluster.configuration_endpoint.address.
826
- # MU::Cloud::AWS::DNSZone.genericMuDNSEntry(name: cluster_id, target: , cloudclass: MU::Cloud::CacheCluster, delete: true)
790
+ # MU::Cloud.resourceClass("AWS", "DNSZone").genericMuDNSEntry(name: cluster_id, target: , cloudclass: MU::Cloud::CacheCluster, delete: true)
827
791
 
828
792
  if %w{deleting deleted}.include?(cluster.cache_cluster_status)
829
793
  MU.log "#{cluster_id} has already been terminated", MU::WARN
@@ -925,10 +889,10 @@ module MU
925
889
  end
926
890
 
927
891
  # What's the likelihood of having more than one node group? maybe iterate over node_groups instead of assuming there is only one?
928
- MU::Cloud::AWS::DNSZone.genericMuDNSEntry(name: repl_group_id, target: repl_group.node_groups.first.primary_endpoint.address, cloudclass: MU::Cloud::CacheCluster, delete: true)
892
+ MU::Cloud.resourceClass("AWS", "DNSZone").genericMuDNSEntry(name: repl_group_id, target: repl_group.node_groups.first.primary_endpoint.address, cloudclass: MU::Cloud::CacheCluster, delete: true)
929
893
  # Assuming we also created DNS records for each of our cluster's read endpoint.
930
894
  repl_group.node_groups.first.node_group_members.each { |member|
931
- MU::Cloud::AWS::DNSZone.genericMuDNSEntry(name: member.cache_cluster_id, target: member.read_endpoint.address, cloudclass: MU::Cloud::CacheCluster, delete: true)
895
+ MU::Cloud.resourceClass("AWS", "DNSZone").genericMuDNSEntry(name: member.cache_cluster_id, target: member.read_endpoint.address, cloudclass: MU::Cloud::CacheCluster, delete: true)
932
896
  }
933
897
 
934
898
  if %w{deleting deleted}.include?(repl_group.status)