cloud-mu 3.1.6 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
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)