cloud-mu 3.1.5 → 3.3.2

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 (185) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +5 -1
  3. data/ansible/roles/mu-windows/files/LaunchConfig.json +9 -0
  4. data/ansible/roles/mu-windows/files/config.xml +76 -0
  5. data/ansible/roles/mu-windows/tasks/main.yml +16 -0
  6. data/bin/mu-adopt +16 -12
  7. data/bin/mu-azure-tests +57 -0
  8. data/bin/mu-cleanup +2 -4
  9. data/bin/mu-configure +52 -0
  10. data/bin/mu-deploy +3 -3
  11. data/bin/mu-findstray-tests +25 -0
  12. data/bin/mu-gen-docs +2 -4
  13. data/bin/mu-load-config.rb +2 -1
  14. data/bin/mu-node-manage +15 -16
  15. data/bin/mu-run-tests +37 -12
  16. data/cloud-mu.gemspec +3 -3
  17. data/cookbooks/mu-activedirectory/resources/domain.rb +4 -4
  18. data/cookbooks/mu-activedirectory/resources/domain_controller.rb +4 -4
  19. data/cookbooks/mu-tools/libraries/helper.rb +1 -1
  20. data/cookbooks/mu-tools/recipes/apply_security.rb +14 -14
  21. data/cookbooks/mu-tools/recipes/aws_api.rb +9 -0
  22. data/cookbooks/mu-tools/recipes/eks.rb +2 -2
  23. data/cookbooks/mu-tools/recipes/windows-client.rb +25 -22
  24. data/extras/clean-stock-amis +25 -19
  25. data/extras/generate-stock-images +1 -0
  26. data/extras/image-generators/AWS/win2k12.yaml +2 -0
  27. data/extras/image-generators/AWS/win2k16.yaml +2 -0
  28. data/extras/image-generators/AWS/win2k19.yaml +2 -0
  29. data/modules/mommacat.ru +1 -1
  30. data/modules/mu.rb +86 -98
  31. data/modules/mu/adoption.rb +373 -58
  32. data/modules/mu/cleanup.rb +214 -303
  33. data/modules/mu/cloud.rb +128 -1733
  34. data/modules/mu/cloud/database.rb +49 -0
  35. data/modules/mu/cloud/dnszone.rb +44 -0
  36. data/modules/mu/cloud/machine_images.rb +212 -0
  37. data/modules/mu/cloud/providers.rb +81 -0
  38. data/modules/mu/cloud/resource_base.rb +929 -0
  39. data/modules/mu/cloud/server.rb +40 -0
  40. data/modules/mu/cloud/server_pool.rb +1 -0
  41. data/modules/mu/cloud/ssh_sessions.rb +228 -0
  42. data/modules/mu/cloud/winrm_sessions.rb +237 -0
  43. data/modules/mu/cloud/wrappers.rb +169 -0
  44. data/modules/mu/config.rb +123 -81
  45. data/modules/mu/config/alarm.rb +2 -6
  46. data/modules/mu/config/bucket.rb +32 -3
  47. data/modules/mu/config/cache_cluster.rb +2 -2
  48. data/modules/mu/config/cdn.rb +100 -0
  49. data/modules/mu/config/collection.rb +1 -1
  50. data/modules/mu/config/container_cluster.rb +7 -2
  51. data/modules/mu/config/database.rb +84 -105
  52. data/modules/mu/config/database.yml +1 -2
  53. data/modules/mu/config/dnszone.rb +5 -4
  54. data/modules/mu/config/doc_helpers.rb +5 -6
  55. data/modules/mu/config/endpoint.rb +2 -1
  56. data/modules/mu/config/firewall_rule.rb +3 -19
  57. data/modules/mu/config/folder.rb +1 -1
  58. data/modules/mu/config/function.rb +17 -8
  59. data/modules/mu/config/group.rb +1 -1
  60. data/modules/mu/config/habitat.rb +1 -1
  61. data/modules/mu/config/job.rb +89 -0
  62. data/modules/mu/config/loadbalancer.rb +57 -11
  63. data/modules/mu/config/log.rb +1 -1
  64. data/modules/mu/config/msg_queue.rb +1 -1
  65. data/modules/mu/config/nosqldb.rb +1 -1
  66. data/modules/mu/config/notifier.rb +8 -19
  67. data/modules/mu/config/ref.rb +92 -14
  68. data/modules/mu/config/role.rb +1 -1
  69. data/modules/mu/config/schema_helpers.rb +38 -37
  70. data/modules/mu/config/search_domain.rb +1 -1
  71. data/modules/mu/config/server.rb +12 -13
  72. data/modules/mu/config/server_pool.rb +3 -7
  73. data/modules/mu/config/storage_pool.rb +1 -1
  74. data/modules/mu/config/tail.rb +11 -0
  75. data/modules/mu/config/user.rb +1 -1
  76. data/modules/mu/config/vpc.rb +27 -23
  77. data/modules/mu/config/vpc.yml +0 -1
  78. data/modules/mu/defaults/AWS.yaml +90 -90
  79. data/modules/mu/defaults/Azure.yaml +1 -0
  80. data/modules/mu/defaults/Google.yaml +1 -0
  81. data/modules/mu/deploy.rb +34 -20
  82. data/modules/mu/groomer.rb +16 -1
  83. data/modules/mu/groomers/ansible.rb +69 -4
  84. data/modules/mu/groomers/chef.rb +51 -4
  85. data/modules/mu/logger.rb +120 -144
  86. data/modules/mu/master.rb +97 -4
  87. data/modules/mu/mommacat.rb +160 -874
  88. data/modules/mu/mommacat/daemon.rb +23 -14
  89. data/modules/mu/mommacat/naming.rb +110 -3
  90. data/modules/mu/mommacat/search.rb +497 -0
  91. data/modules/mu/mommacat/storage.rb +252 -194
  92. data/modules/mu/{clouds → providers}/README.md +1 -1
  93. data/modules/mu/{clouds → providers}/aws.rb +258 -57
  94. data/modules/mu/{clouds → providers}/aws/alarm.rb +3 -3
  95. data/modules/mu/{clouds → providers}/aws/bucket.rb +275 -41
  96. data/modules/mu/{clouds → providers}/aws/cache_cluster.rb +14 -50
  97. data/modules/mu/providers/aws/cdn.rb +782 -0
  98. data/modules/mu/{clouds → providers}/aws/collection.rb +5 -5
  99. data/modules/mu/{clouds → providers}/aws/container_cluster.rb +95 -84
  100. data/modules/mu/providers/aws/database.rb +1744 -0
  101. data/modules/mu/{clouds → providers}/aws/dnszone.rb +26 -12
  102. data/modules/mu/providers/aws/endpoint.rb +1072 -0
  103. data/modules/mu/{clouds → providers}/aws/firewall_rule.rb +39 -32
  104. data/modules/mu/{clouds → providers}/aws/folder.rb +1 -1
  105. data/modules/mu/{clouds → providers}/aws/function.rb +289 -134
  106. data/modules/mu/{clouds → providers}/aws/group.rb +18 -20
  107. data/modules/mu/{clouds → providers}/aws/habitat.rb +3 -3
  108. data/modules/mu/providers/aws/job.rb +466 -0
  109. data/modules/mu/{clouds → providers}/aws/loadbalancer.rb +77 -47
  110. data/modules/mu/{clouds → providers}/aws/log.rb +5 -5
  111. data/modules/mu/{clouds → providers}/aws/msg_queue.rb +14 -11
  112. data/modules/mu/{clouds → providers}/aws/nosqldb.rb +96 -5
  113. data/modules/mu/{clouds → providers}/aws/notifier.rb +135 -63
  114. data/modules/mu/{clouds → providers}/aws/role.rb +76 -48
  115. data/modules/mu/{clouds → providers}/aws/search_domain.rb +172 -41
  116. data/modules/mu/{clouds → providers}/aws/server.rb +66 -98
  117. data/modules/mu/{clouds → providers}/aws/server_pool.rb +42 -60
  118. data/modules/mu/{clouds → providers}/aws/storage_pool.rb +21 -38
  119. data/modules/mu/{clouds → providers}/aws/user.rb +12 -16
  120. data/modules/mu/{clouds → providers}/aws/userdata/README.md +0 -0
  121. data/modules/mu/{clouds → providers}/aws/userdata/linux.erb +5 -4
  122. data/modules/mu/{clouds → providers}/aws/userdata/windows.erb +0 -0
  123. data/modules/mu/{clouds → providers}/aws/vpc.rb +143 -74
  124. data/modules/mu/{clouds → providers}/aws/vpc_subnet.rb +0 -0
  125. data/modules/mu/{clouds → providers}/azure.rb +13 -0
  126. data/modules/mu/{clouds → providers}/azure/container_cluster.rb +1 -5
  127. data/modules/mu/{clouds → providers}/azure/firewall_rule.rb +8 -1
  128. data/modules/mu/{clouds → providers}/azure/habitat.rb +0 -0
  129. data/modules/mu/{clouds → providers}/azure/loadbalancer.rb +0 -0
  130. data/modules/mu/{clouds → providers}/azure/role.rb +0 -0
  131. data/modules/mu/{clouds → providers}/azure/server.rb +32 -24
  132. data/modules/mu/{clouds → providers}/azure/user.rb +1 -1
  133. data/modules/mu/{clouds → providers}/azure/userdata/README.md +0 -0
  134. data/modules/mu/{clouds → providers}/azure/userdata/linux.erb +0 -0
  135. data/modules/mu/{clouds → providers}/azure/userdata/windows.erb +0 -0
  136. data/modules/mu/{clouds → providers}/azure/vpc.rb +4 -6
  137. data/modules/mu/{clouds → providers}/cloudformation.rb +10 -0
  138. data/modules/mu/{clouds → providers}/cloudformation/alarm.rb +3 -3
  139. data/modules/mu/{clouds → providers}/cloudformation/cache_cluster.rb +3 -3
  140. data/modules/mu/{clouds → providers}/cloudformation/collection.rb +3 -3
  141. data/modules/mu/{clouds → providers}/cloudformation/database.rb +6 -17
  142. data/modules/mu/{clouds → providers}/cloudformation/dnszone.rb +3 -3
  143. data/modules/mu/{clouds → providers}/cloudformation/firewall_rule.rb +3 -3
  144. data/modules/mu/{clouds → providers}/cloudformation/loadbalancer.rb +3 -3
  145. data/modules/mu/{clouds → providers}/cloudformation/log.rb +3 -3
  146. data/modules/mu/{clouds → providers}/cloudformation/server.rb +7 -7
  147. data/modules/mu/{clouds → providers}/cloudformation/server_pool.rb +5 -5
  148. data/modules/mu/{clouds → providers}/cloudformation/vpc.rb +3 -3
  149. data/modules/mu/{clouds → providers}/docker.rb +0 -0
  150. data/modules/mu/{clouds → providers}/google.rb +29 -6
  151. data/modules/mu/{clouds → providers}/google/bucket.rb +4 -4
  152. data/modules/mu/{clouds → providers}/google/container_cluster.rb +38 -20
  153. data/modules/mu/{clouds → providers}/google/database.rb +5 -12
  154. data/modules/mu/{clouds → providers}/google/firewall_rule.rb +5 -5
  155. data/modules/mu/{clouds → providers}/google/folder.rb +5 -9
  156. data/modules/mu/{clouds → providers}/google/function.rb +6 -6
  157. data/modules/mu/{clouds → providers}/google/group.rb +9 -17
  158. data/modules/mu/{clouds → providers}/google/habitat.rb +4 -8
  159. data/modules/mu/{clouds → providers}/google/loadbalancer.rb +5 -5
  160. data/modules/mu/{clouds → providers}/google/role.rb +50 -31
  161. data/modules/mu/{clouds → providers}/google/server.rb +41 -24
  162. data/modules/mu/{clouds → providers}/google/server_pool.rb +14 -14
  163. data/modules/mu/{clouds → providers}/google/user.rb +34 -24
  164. data/modules/mu/{clouds → providers}/google/userdata/README.md +0 -0
  165. data/modules/mu/{clouds → providers}/google/userdata/linux.erb +0 -0
  166. data/modules/mu/{clouds → providers}/google/userdata/windows.erb +0 -0
  167. data/modules/mu/{clouds → providers}/google/vpc.rb +45 -14
  168. data/modules/tests/aws-jobs-functions.yaml +46 -0
  169. data/modules/tests/centos6.yaml +15 -0
  170. data/modules/tests/centos7.yaml +15 -0
  171. data/modules/tests/centos8.yaml +12 -0
  172. data/modules/tests/ecs.yaml +2 -2
  173. data/modules/tests/eks.yaml +1 -1
  174. data/modules/tests/functions/node-function/lambda_function.js +10 -0
  175. data/modules/tests/functions/python-function/lambda_function.py +12 -0
  176. data/modules/tests/microservice_app.yaml +288 -0
  177. data/modules/tests/rds.yaml +108 -0
  178. data/modules/tests/regrooms/rds.yaml +123 -0
  179. data/modules/tests/server-with-scrub-muisms.yaml +1 -1
  180. data/modules/tests/super_complex_bok.yml +2 -2
  181. data/modules/tests/super_simple_bok.yml +3 -5
  182. data/spec/mu/clouds/azure_spec.rb +2 -2
  183. metadata +122 -92
  184. data/modules/mu/clouds/aws/database.rb +0 -1974
  185. data/modules/mu/clouds/aws/endpoint.rb +0 -596
@@ -22,9 +22,9 @@ module MU
22
22
  # @param args [Hash]: Hash of named arguments passed via Ruby's double-splat
23
23
  def initialize(**args)
24
24
  super
25
- if @cloud_id and !@config['domain_name']
26
- @config['domain_name'] = @cloud_id
27
- end
25
+ describe if @mu_name and !@deploydata
26
+ @cloud_id ||= @deploydata['domain_name'] if @deploydata
27
+
28
28
  @mu_name ||= @deploy.getResourceName(@config["name"])
29
29
  end
30
30
 
@@ -35,7 +35,8 @@ module MU
35
35
  params = genParams
36
36
 
37
37
  MU.log "Creating ElasticSearch domain #{@config['domain_name']}", details: params
38
- MU::Cloud::AWS.elasticsearch(region: @config['region'], credentials: @config['credentials']).create_elasticsearch_domain(params).domain_status
38
+ @cloud_id = @config['domain_name']
39
+ MU::Cloud::AWS.elasticsearch(region: @config['region'], credentials: @credentials).create_elasticsearch_domain(params).domain_status
39
40
 
40
41
  tagDomain
41
42
 
@@ -44,17 +45,18 @@ module MU
44
45
  # Called automatically by {MU::Deploy#createResources}
45
46
  def groom
46
47
  tagDomain
47
- @config['domain_name'] ||= @deploydata['domain_name']
48
+ @config['domain_name'] ||= @cloud_id
48
49
  params = genParams(cloud_desc) # get parameters that would change only
49
50
 
50
51
  if params.size > 1
51
52
  waitWhileProcessing # wait until the create finishes, if still going
52
53
 
53
54
  MU.log "Updating ElasticSearch domain #{@config['domain_name']}", MU::NOTICE, details: params
54
- MU::Cloud::AWS.elasticsearch(region: @config['region'], credentials: @config['credentials']).update_elasticsearch_domain_config(params)
55
+ MU::Cloud::AWS.elasticsearch(region: @config['region'], credentials: @credentials).update_elasticsearch_domain_config(params)
55
56
  end
56
57
 
57
58
  waitWhileProcessing # don't return until creation/updating is complete
59
+ MU.log "Search Domain #{@config['name']}: #{cloud_desc.endpoint}", MU::SUMMARY
58
60
  end
59
61
 
60
62
  @cloud_desc_cache = nil
@@ -63,31 +65,30 @@ module MU
63
65
  # our druthers.
64
66
  def cloud_desc(use_cache: true)
65
67
  return @cloud_desc_cache if @cloud_desc_cache and use_cache
66
- @cloud_desc_cache = if @config['domain_name']
67
- MU::Cloud::AWS.elasticsearch(region: @config['region'], credentials: @config['credentials']).describe_elasticsearch_domain(
68
- domain_name: @config['domain_name']
68
+ @cloud_id ||= @config['domain_name']
69
+ return nil if !@cloud_id
70
+ MU.retrier([::Aws::ElasticsearchService::Errors::ResourceNotFoundException], wait: 10, max: 12) {
71
+ @cloud_desc_cache = MU::Cloud::AWS.elasticsearch(region: @config['region'], credentials: @credentials).describe_elasticsearch_domain(
72
+ domain_name: @cloud_id
69
73
  ).domain_status
70
- elsif @deploydata and @deploydata['domain_name']
71
- MU::Cloud::AWS.elasticsearch(region: @config['region'], credentials: @config['credentials']).describe_elasticsearch_domain(
72
- domain_name: @deploydata['domain_name']
73
- ).domain_status
74
- else
75
- raise MuError, "#{@mu_name} can't find its official Elasticsearch domain name!"
76
- end
74
+ }
75
+
77
76
  @cloud_desc_cache
78
77
  end
79
78
 
80
79
  # Canonical Amazon Resource Number for this resource
81
80
  # @return [String]
82
81
  def arn
83
- cloud_desc.arn
82
+ return nil if !cloud_desc
83
+ cloud_desc.arn.dup
84
84
  end
85
85
 
86
86
  # Return the metadata for this SearchDomain rule
87
87
  # @return [Hash]
88
88
  def notify
89
- deploy_struct = MU.structToHash(cloud_desc)
90
- tags = MU::Cloud::AWS.elasticsearch(region: @config['region'], credentials: @config['credentials']).list_tags(arn: deploy_struct[:arn]).tag_list
89
+ return nil if !cloud_desc(use_cache: false)
90
+ deploy_struct = MU.structToHash(cloud_desc, stringify_keys: true)
91
+ tags = MU::Cloud::AWS.elasticsearch(region: @config['region'], credentials: @credentials).list_tags(arn: arn).tag_list
91
92
  deploy_struct['tags'] = tags.map { |t| { t.key => t.value } }
92
93
  if deploy_struct['endpoint']
93
94
  deploy_struct['kibana'] = deploy_struct['endpoint']+"/_plugin/kibana/"
@@ -119,7 +120,7 @@ module MU
119
120
  # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
120
121
  # @param region [String]: The cloud provider region
121
122
  # @return [void]
122
- def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
123
+ def self.cleanup(noop: false, deploy_id: MU.deploy_id, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
123
124
  MU.log "AWS::SearchDomain.cleanup: need to support flags['known']", MU::DEBUG, details: flags
124
125
 
125
126
  list = MU::Cloud::AWS.elasticsearch(region: region, credentials: credentials).list_domain_names
@@ -135,7 +136,7 @@ module MU
135
136
  deploy_match = false
136
137
  master_match = false
137
138
  tags.tag_list.each { |tag|
138
- if tag.key == "MU-ID" and tag.value == MU.deploy_id
139
+ if tag.key == "MU-ID" and tag.value == deploy_id
139
140
  deploy_match = true
140
141
  elsif tag.key == "MU-MASTER-IP" and tag.value == MU.mu_public_ip
141
142
  master_match = true
@@ -156,8 +157,8 @@ module MU
156
157
  begin
157
158
  resp = MU::Cloud::AWS.iam(credentials: credentials).list_roles(marker: marker)
158
159
  resp.roles.each{ |role|
159
- # XXX Maybe we should have a more generic way to delete IAM profiles and policies. The call itself should be moved from MU::Cloud::AWS::Server.
160
- # MU::Cloud::AWS::Server.removeIAMProfile(role.role_name) if role.role_name.match(/^#{Regexp.quote(MU.deploy_id)}/)
160
+ # XXX Maybe we should have a more generic way to delete IAM profiles and policies. The call itself should be moved from MU::Cloud.resourceClass("AWS", "Server").
161
+ # MU::Cloud.resourceClass("AWS", "Server").removeIAMProfile(role.role_name) if role.role_name.match(/^#{Regexp.quote(deploy_id)}/)
161
162
  }
162
163
  marker = resp.marker
163
164
  end while resp.is_truncated
@@ -191,6 +192,96 @@ module MU
191
192
  found
192
193
  end
193
194
 
195
+ # Reverse-map our cloud description into a runnable config hash.
196
+ # We assume that any values we have in +@config+ are placeholders, and
197
+ # calculate our own accordingly based on what's live in the cloud.
198
+ def toKitten(**_args)
199
+ bok = {
200
+ "cloud" => "AWS",
201
+ "credentials" => @credentials,
202
+ "cloud_id" => @cloud_id,
203
+ "region" => @config['region']
204
+ }
205
+
206
+ if !cloud_desc
207
+ MU.log "toKitten failed to load a cloud_desc from #{@cloud_id}", MU::ERR, details: @config
208
+ return nil
209
+ end
210
+
211
+ bok['name'] = cloud_desc.domain_name
212
+ bok['elasticsearch_version'] = cloud_desc.elasticsearch_version
213
+ bok['instance_count'] = cloud_desc.elasticsearch_cluster_config.instance_count
214
+ bok['instance_type'] = cloud_desc.elasticsearch_cluster_config.instance_type
215
+ bok['zone_aware'] = cloud_desc.elasticsearch_cluster_config.zone_awareness_enabled
216
+
217
+ if cloud_desc.elasticsearch_cluster_config.dedicated_master_enabled
218
+ bok['dedicated_masters'] = cloud_desc.elasticsearch_cluster_config.dedicated_master_count
219
+ bok['master_instance_type'] = cloud_desc.elasticsearch_cluster_config.dedicated_master_type
220
+ end
221
+
222
+ if cloud_desc.access_policies and !cloud_desc.access_policies.empty?
223
+ bok['access_policies'] = JSON.parse(cloud_desc.access_policies)
224
+ end
225
+
226
+ if cloud_desc.advanced_options and !cloud_desc.advanced_options.empty?
227
+ bok['advanced_options'] = cloud_desc.advanced_options
228
+ end
229
+
230
+ bok['ebs_size'] = cloud_desc.ebs_options.volume_size
231
+ bok['ebs_type'] = cloud_desc.ebs_options.volume_type
232
+ bok['ebs_iops'] = cloud_desc.ebs_options.iops if cloud_desc.ebs_options.iops
233
+
234
+ if cloud_desc.snapshot_options and cloud_desc.snapshot_options.automated_snapshot_start_hour
235
+ bok['snapshot_hour'] = cloud_desc.snapshot_options.automated_snapshot_start_hour
236
+ end
237
+
238
+ if cloud_desc.cognito_options.user_pool_id and
239
+ cloud_desc.cognito_options.identity_pool_id
240
+ bok['user_pool_id'] = cloud_desc.cognito_options.user_pool_id
241
+ bok['identity_pool_id'] = cloud_desc.cognito_options.identity_pool_id
242
+ end
243
+
244
+ tags = MU::Cloud::AWS.elasticsearch(region: @config['region'], credentials: @credentials).list_tags(arn: cloud_desc.arn).tag_list
245
+ if tags and !tags.empty?
246
+ bok['tags'] = MU.structToHash(tags)
247
+ end
248
+
249
+ if cloud_desc.vpc_options
250
+ bok['vpc'] = MU::Config::Ref.get(
251
+ id: cloud_desc.vpc_options.vpc_id,
252
+ cloud: "AWS",
253
+ credentials: @credentials,
254
+ type: "vpcs",
255
+ region: @config['region'],
256
+ subnets: cloud_desc.vpc_options.subnet_ids.map { |s| { "subnet_id" => s } }
257
+ )
258
+ if cloud_desc.vpc_options.security_group_ids and
259
+ !cloud_desc.vpc_options.security_group_ids.empty?
260
+ bok['add_firewall_rules'] = cloud_desc.vpc_options.security_group_ids.map { |sg|
261
+ MU::Config::Ref.get(
262
+ id: sg,
263
+ cloud: "AWS",
264
+ credentials: @credentials,
265
+ region: @config['region'],
266
+ type: "firewall_rules",
267
+ )
268
+ }
269
+ end
270
+ end
271
+
272
+ if cloud_desc.log_publishing_options
273
+ # XXX this is primitive... there are multiple other log types now,
274
+ # and this should be a Ref blob, not a flat string
275
+ cloud_desc.log_publishing_options.each_pair { |type, whither|
276
+ if type == "SEARCH_SLOW_LOGS"
277
+ bok['slow_logs'] = whither.cloud_watch_logs_log_group_arn
278
+ end
279
+ }
280
+ end
281
+
282
+ bok
283
+ end
284
+
194
285
  # Cloud-specific configuration properties.
195
286
  # @param _config [MU::Config]: The calling MU::Config object
196
287
  # @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
@@ -200,7 +291,7 @@ module MU
200
291
  versions = begin
201
292
  MU::Cloud::AWS.elasticsearch.list_elasticsearch_versions.elasticsearch_versions
202
293
  rescue MuError
203
- ["7.1", "6.8", "6.7", "6.5", "6.4", "6.3", "6.2", "6.0", "5.6"]
294
+ ["7.4", "7.1", "6.8", "6.7", "6.5", "6.4", "6.3", "6.2", "6.0", "5.6"]
204
295
  end
205
296
  instance_types = begin
206
297
  MU::Cloud::AWS.elasticsearch.list_elasticsearch_instance_types(
@@ -215,6 +306,8 @@ module MU
215
306
  ).elasticsearch_instance_types
216
307
  end
217
308
 
309
+ polschema = MU::Config::Role.schema["properties"]["policies"]
310
+ polschema.deep_merge!(MU::Cloud.resourceClass("AWS", "Role").condition_schema)
218
311
 
219
312
  schema = {
220
313
  "name" => {
@@ -236,9 +329,10 @@ module MU
236
329
  "default" => 0,
237
330
  "description" => "Separate, dedicated master node(s), over and above the search instances specified in instance_count."
238
331
  },
332
+ "policies" => polschema,
239
333
  "access_policies" => {
240
334
  "type" => "object",
241
- "description" => "An IAM policy document for access to ElasticSearch. Our parser expects this to be defined inline like the rest of your YAML/JSON Basket of Kittens, not as raw JSON. For guidance on ElasticSearch IAM capabilities, see: https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/es-ac.html"
335
+ "description" => "An IAM policy document for access to ElasticSearch (see {policies} for setting complex access policies with runtime dependencies). Our parser expects this to be defined inline like the rest of your YAML/JSON Basket of Kittens, not as raw JSON. For guidance on ElasticSearch IAM capabilities, see: https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/es-ac.html"
242
336
  },
243
337
  "master_instance_type" => {
244
338
  "type" => "string",
@@ -246,7 +340,7 @@ module MU
246
340
  },
247
341
  "ebs_type" => {
248
342
  "type" => "string",
249
- "default" => "standard",
343
+ "default" => "gp2",
250
344
  "description" => "Type of EBS storage to use for cluster nodes. If 'none' is specified, EBS storage will not be used, but this is only valid for certain instance types.",
251
345
  "enum" => ["standard", "gp2", "io1", "none"]
252
346
  },
@@ -378,9 +472,9 @@ module MU
378
472
 
379
473
  if dom['slow_logs']
380
474
  if configurator.haveLitterMate?(dom['slow_logs'], "log")
381
- dom['dependencies'] << { "name" => dom['slow_logs'], "type" => "log" }
475
+ MU::Config.addDependency(dom, dom['slow_logs'], "log")
382
476
  else
383
- log_group = MU::Cloud::AWS::Log.find(cloud_id: dom['slow_logs'], region: dom['region']).values.first
477
+ log_group = MU::Cloud.resourceClass("AWS", "Log").find(cloud_id: dom['slow_logs'], region: dom['region']).values.first
384
478
  if !log_group
385
479
  MU.log "Specified slow_logs CloudWatch log group '#{dom['slow_logs']}' in SearchDomain '#{dom['name']}' doesn't appear to exist", MU::ERR
386
480
  ok = false
@@ -395,7 +489,7 @@ module MU
395
489
  "credentials" => dom['credentials']
396
490
  }
397
491
  ok = false if !configurator.insertKitten(log_group, "logs")
398
- dom['dependencies'] << { "name" => dom['slow_logs'], "type" => "log" }
492
+ MU::Config.addDependency(dom, dom['slow_logs'], "log")
399
493
  end
400
494
 
401
495
  if dom['advanced_options']
@@ -456,12 +550,7 @@ module MU
456
550
  ]
457
551
  }
458
552
  configurator.insertKitten(roledesc, "roles")
459
-
460
- dom['dependencies'] ||= []
461
- dom['dependencies'] << {
462
- "type" => "role",
463
- "name" => dom['name']+"cognitorole"
464
- }
553
+ MU::Config.addDependency(dom, dom['name']+"cognitorole", "role")
465
554
  end
466
555
 
467
556
  end
@@ -514,9 +603,51 @@ module MU
514
603
  params[:snapshot_options][:automated_snapshot_start_hour] = @config['snapshot_hour']
515
604
  end
516
605
 
517
- if @config['access_policies']
518
- # TODO check against ext.access_policies.options
519
- params[:access_policies] = JSON.generate(@config['access_policies'])
606
+ if ext
607
+ # Despite being called access_policies, this parameter actually
608
+ # only accepts one policy. So, we'll munge everything we have
609
+ # together into one policy with multiple Statements.
610
+ policy = nil
611
+ # TODO check against ext.access_policy.options
612
+
613
+ if @config['access_policies']
614
+ policy = @config['access_policies']
615
+ # ensure the "Statement" key is cased in a predictable way
616
+ statement_key = nil
617
+ policy.each_pair { |k, v|
618
+ if k.downcase == "statement" and k != "Statement"
619
+ statement_key = k
620
+ break
621
+ end
622
+ }
623
+ if statement_key
624
+ policy["Statement"] = policy.delete(statement_key)
625
+ end
626
+ if !policy["Statement"].is_a?(Array)
627
+ policy["Statement"] = [policy["Statement"]]
628
+ end
629
+ end
630
+
631
+ if @config['policies']
632
+ @config['policies'].each { |p|
633
+ p['targets'].each { |t|
634
+ if t['path']
635
+ t['path'].gsub!(/#SELF/, @mu_name.downcase)
636
+ end
637
+ }
638
+ parsed = MU::Cloud.resourceClass("AWS", "Role").genPolicyDocument([p], deploy_obj: @deploy, bucket_style: true).first.values.first
639
+
640
+ if policy and policy["Statement"]
641
+ policy["Statement"].concat(parsed["Statement"])
642
+ else
643
+ policy = parsed
644
+ end
645
+ }
646
+ end
647
+
648
+ if policy
649
+ params[:access_policies] = JSON.generate(policy)
650
+ end
520
651
  end
521
652
 
522
653
  if @config['slow_logs']
@@ -525,7 +656,7 @@ module MU
525
656
  arn = @config['slow_logs']
526
657
  else
527
658
  log_group = @deploy.findLitterMate(type: "log", name: @config['slow_logs'])
528
- log_group = MU::Cloud::AWS::Log.find(cloud_id: log_group.mu_name, region: log_group.cloudobj.config['region']).values.first
659
+ log_group = MU::Cloud.resourceClass("AWS", "Log").find(cloud_id: log_group.mu_name, region: log_group.cloudobj.config['region']).values.first
529
660
  if log_group.nil? or log_group.arn.nil?
530
661
  raise MuError, "Failed to retrieve ARN of sibling LogGroup '#{@config['slow_logs']}'"
531
662
  end
@@ -552,7 +683,7 @@ module MU
552
683
  params[:log_publishing_options]["SEARCH_SLOW_LOGS"] = {}
553
684
  params[:log_publishing_options]["SEARCH_SLOW_LOGS"][:enabled] = true
554
685
  params[:log_publishing_options]["SEARCH_SLOW_LOGS"][:cloud_watch_logs_log_group_arn] = arn
555
- MU::Cloud::AWS::Log.allowService("es.amazonaws.com", arn, @config['region'])
686
+ MU::Cloud.resourceClass("AWS", "Log").allowService("es.amazonaws.com", arn, @config['region'])
556
687
  end
557
688
  end
558
689
 
@@ -682,7 +813,7 @@ module MU
682
813
  raise MU::MuError, "Can't tag ElasticSearch domain, cloud descriptor came back without an ARN"
683
814
  end
684
815
 
685
- MU::Cloud::AWS.elasticsearch(region: @config['region'], credentials: @config['credentials']).add_tags(
816
+ MU::Cloud::AWS.elasticsearch(region: @config['region'], credentials: @credentials).add_tags(
686
817
  arn: domain.arn,
687
818
  tag_list: tags
688
819
  )
@@ -89,7 +89,7 @@ module MU
89
89
  template_variables: {
90
90
  "deployKey" => Base64.urlsafe_encode64(@deploy.public_key),
91
91
  "deploySSHKey" => @deploy.ssh_public_key,
92
- "muID" => MU.deploy_id,
92
+ "muID" => @deploy.deploy_id,
93
93
  "muUser" => MU.mu_user,
94
94
  "publicIP" => MU.mu_public_ip,
95
95
  "mommaCatPort" => MU.mommaCatPort,
@@ -145,7 +145,7 @@ module MU
145
145
  raise MuError, "My second argument should be a hash of variables to pass into ERB templates"
146
146
  end
147
147
  $mu = OpenStruct.new(template_variables)
148
- userdata_dir = File.expand_path(MU.myRoot+"/modules/mu/clouds/aws/userdata")
148
+ userdata_dir = File.expand_path(MU.myRoot+"/modules/mu/providers/aws/userdata")
149
149
  platform = "linux" if %w{centos centos6 centos7 ubuntu ubuntu14 rhel rhel7 rhel71 amazon}.include? platform
150
150
  platform = "windows" if %w{win2k12r2 win2k12 win2k8 win2k8r2 win2k16}.include? platform
151
151
  erbfile = "#{userdata_dir}/#{platform}.erb"
@@ -299,7 +299,7 @@ module MU
299
299
  raise MuError, "Got null subnet id out of #{@config['vpc']}"
300
300
  end
301
301
  MU.log "Deploying #{@mu_name} into VPC #{@vpc.cloud_id} Subnet #{subnet.cloud_id}"
302
- punchAdminNAT
302
+ allowBastionAccess
303
303
  instance_descriptor[:subnet_id] = subnet.cloud_id
304
304
  end
305
305
 
@@ -399,13 +399,13 @@ module MU
399
399
  # Figure out what's needed to SSH into this server.
400
400
  # @return [Array<String>]: nat_ssh_key, nat_ssh_user, nat_ssh_host, canonical_ip, ssh_user, ssh_key_name, alternate_names
401
401
  def getSSHConfig
402
- describe(cloud_id: @cloud_id)
402
+ cloud_desc(use_cache: false) # make sure we're current
403
403
  # XXX add some awesome alternate names from metadata and make sure they end
404
404
  # up in MU::MommaCat's ssh config wangling
405
405
  return nil if @config.nil? or @deploy.nil?
406
406
 
407
407
  nat_ssh_key = nat_ssh_user = nat_ssh_host = nil
408
- if !@config["vpc"].nil? and !MU::Cloud::AWS::VPC.haveRouteToInstance?(cloud_desc, region: @config['region'], credentials: @config['credentials'])
408
+ if !@config["vpc"].nil? and !MU::Cloud.resourceClass("AWS", "VPC").haveRouteToInstance?(cloud_desc, region: @config['region'], credentials: @config['credentials'])
409
409
  if !@nat.nil?
410
410
  if @nat.is_a?(Struct) && @nat.nat_gateway_id && @nat.nat_gateway_id.start_with?("nat-")
411
411
  raise MuError, "Configured to use NAT Gateway, but I have no route to instance. Either use Bastion, or configure VPC peering"
@@ -444,8 +444,7 @@ module MU
444
444
  # administravia for a new instance.
445
445
  def postBoot(instance_id = nil)
446
446
  @cloud_id ||= instance_id
447
- node, _config, deploydata = describe(cloud_id: @cloud_id)
448
- @mu_name ||= node
447
+ _node, _config, deploydata = describe(cloud_id: @cloud_id)
449
448
 
450
449
  raise MuError, "Couldn't find instance #{@mu_name} (#{@cloud_id})" if !cloud_desc
451
450
  return false if !MU::MommaCat.lock(@cloud_id+"-orchestrate", true)
@@ -482,7 +481,7 @@ module MU
482
481
  end
483
482
  }
484
483
 
485
- punchAdminNAT
484
+ allowBastionAccess
486
485
 
487
486
  setAlarms
488
487
 
@@ -615,7 +614,7 @@ module MU
615
614
  return nil
616
615
  end
617
616
 
618
- asgs = MU::Cloud::AWS::ServerPool.find(
617
+ asgs = MU::Cloud.resourceClass("AWS", "ServerPool").find(
619
618
  instance_id: @cloud_id,
620
619
  region: @config['region'],
621
620
  credentials: @credentials
@@ -725,15 +724,15 @@ module MU
725
724
 
726
725
  if int.groups.size > 0
727
726
 
728
- require 'mu/clouds/aws/firewall_rule'
729
- ifaces = MU::Cloud::AWS::FirewallRule.getAssociatedInterfaces(int.groups.map { |sg| sg.group_id }, credentials: @credentials, region: @config['region'])
727
+ require 'mu/providers/aws/firewall_rule'
728
+ ifaces = MU::Cloud.resourceClass("AWS", "FirewallRule").getAssociatedInterfaces(int.groups.map { |sg| sg.group_id }, credentials: @credentials, region: @config['region'])
730
729
  done_local_rules = false
731
730
  int.groups.each { |sg|
732
731
  if !done_local_rules and ifaces[sg.group_id].size == 1
733
- sg_desc = MU::Cloud::AWS::FirewallRule.find(cloud_id: sg.group_id, credentials: @credentials, region: @config['region']).values.first
732
+ sg_desc = MU::Cloud.resourceClass("AWS", "FirewallRule").find(cloud_id: sg.group_id, credentials: @credentials, region: @config['region']).values.first
734
733
  if sg_desc
735
- bok["ingress_rules"] = MU::Cloud::AWS::FirewallRule.rulesToBoK(sg_desc.ip_permissions)
736
- bok["ingress_rules"].concat(MU::Cloud::AWS::FirewallRule.rulesToBoK(sg_desc.ip_permissions_egress, egress: true))
734
+ bok["ingress_rules"] = MU::Cloud.resourceClass("AWS", "FirewallRule").rulesToBoK(sg_desc.ip_permissions)
735
+ bok["ingress_rules"].concat(MU::Cloud.resourceClass("AWS", "FirewallRule").rulesToBoK(sg_desc.ip_permissions_egress, egress: true))
737
736
  done_local_rules = true
738
737
  next
739
738
  end
@@ -802,44 +801,13 @@ module MU
802
801
  end
803
802
  deploydata["region"] = @config['region'] if !@config['region'].nil?
804
803
  if !@named
805
- MU::MommaCat.nameKitten(self)
804
+ MU::MommaCat.nameKitten(self, no_dns: true)
806
805
  @named = true
807
806
  end
808
807
 
809
808
  return deploydata
810
809
  end
811
810
 
812
- # If the specified server is in a VPC, and has a NAT, make sure we'll
813
- # be letting ssh traffic in from said NAT.
814
- def punchAdminNAT
815
- if @config['vpc'].nil? or
816
- (
817
- !@config['vpc'].has_key?("nat_host_id") and
818
- !@config['vpc'].has_key?("nat_host_tag") and
819
- !@config['vpc'].has_key?("nat_host_ip") and
820
- !@config['vpc'].has_key?("nat_host_name")
821
- )
822
- return nil
823
- end
824
-
825
- return nil if @nat.is_a?(Struct) && @nat.nat_gateway_id && @nat.nat_gateway_id.start_with?("nat-")
826
-
827
- dependencies if @nat.nil?
828
- if @nat.nil? or @nat.cloud_desc.nil?
829
- raise MuError, "#{@mu_name} (#{MU.deploy_id}) is configured to use #{@config['vpc']} but I can't find the cloud descriptor for a matching NAT instance"
830
- end
831
- MU.log "Adding administrative holes for NAT host #{@nat.cloud_desc.private_ip_address} to #{@mu_name}"
832
- if !@deploy.kittens['firewall_rules'].nil?
833
- @deploy.kittens['firewall_rules'].values.each { |acl|
834
- if acl.config["admin"]
835
- acl.addRule([@nat.cloud_desc.private_ip_address], proto: "tcp")
836
- acl.addRule([@nat.cloud_desc.private_ip_address], proto: "udp")
837
- acl.addRule([@nat.cloud_desc.private_ip_address], proto: "icmp")
838
- end
839
- }
840
- end
841
- end
842
-
843
811
  # Called automatically by {MU::Deploy#createResources}
844
812
  def groom
845
813
  MU::MommaCat.lock(@cloud_id+"-groom")
@@ -851,7 +819,7 @@ module MU
851
819
  end
852
820
  end
853
821
 
854
- punchAdminNAT
822
+ allowBastionAccess
855
823
 
856
824
  tagVolumes
857
825
 
@@ -883,12 +851,25 @@ module MU
883
851
 
884
852
  begin
885
853
  getIAMProfile
854
+
855
+ dbs = @deploy.findLitterMate(type: "database", return_all: true)
856
+ if dbs
857
+ dbs.each_pair { |sib_name, sib|
858
+ @groomer.groomer_class.grantSecretAccess(@mu_name, sib_name, "database_credentials")
859
+ if sib.config and sib.config['auth_vault']
860
+ @groomer.groomer_class.grantSecretAccess(@mu_name, sib.config['auth_vault']['vault'], sib.config['auth_vault']['item'])
861
+ end
862
+ }
863
+ end
864
+
886
865
  if @config['groom'].nil? or @config['groom']
887
866
  @groomer.run(purpose: "Full Initial Run", max_retries: 15, reboot_first_fail: (windows? and @config['groomer'] != "Ansible"), timeout: @config['groomer_timeout'])
888
867
  end
889
868
  rescue MU::Groomer::RunError => e
869
+ raise e if !@config['create_image'].nil? and !@config['image_created']
890
870
  MU.log "Proceeding after failed initial Groomer run, but #{@mu_name} may not behave as expected!", MU::WARN, details: e.message
891
871
  rescue StandardError => e
872
+ raise e if !@config['create_image'].nil? and !@config['image_created']
892
873
  MU.log "Caught #{e.inspect} on #{@mu_name} in an unexpected place (after @groomer.run on Full Initial Run)", MU::ERR
893
874
  end
894
875
 
@@ -910,6 +891,7 @@ module MU
910
891
  # @return [Openstruct]
911
892
  def cloud_desc(use_cache: true)
912
893
  return @cloud_desc_cache if @cloud_desc_cache and use_cache
894
+ return nil if !@cloud_id
913
895
  max_retries = 5
914
896
  retries = 0
915
897
  if !@cloud_id.nil?
@@ -961,7 +943,7 @@ module MU
961
943
  # Our deploydata gets corrupted often with server pools, this will cause us to use the wrong IP to identify a node
962
944
  # which will cause us to create certificates, DNS records and other artifacts with incorrect information which will cause our deploy to fail.
963
945
  # The cloud_id is always correct so lets use 'cloud_desc' to get the correct IPs
964
- if MU::Cloud::AWS::VPC.haveRouteToInstance?(cloud_desc, region: @config['region'], credentials: @config['credentials']) or @deploydata["public_ip_address"].nil?
946
+ if MU::Cloud.resourceClass("AWS", "VPC").haveRouteToInstance?(cloud_desc, region: @config['region'], credentials: @config['credentials']) or @deploydata["public_ip_address"].nil?
965
947
  @config['canonical_ip'] = cloud_desc.private_ip_address
966
948
  @deploydata["private_ip_address"] = cloud_desc.private_ip_address
967
949
  return cloud_desc.private_ip_address
@@ -1181,10 +1163,7 @@ module MU
1181
1163
  end
1182
1164
  end
1183
1165
 
1184
- if @cloud_id.nil?
1185
- describe
1186
- @cloud_id = cloud_desc.instance_id
1187
- end
1166
+ @cloud_id ||= cloud_desc(use_cache: false).instance_id
1188
1167
  ssh_keydir = "#{Etc.getpwuid(Process.uid).dir}/.ssh"
1189
1168
  ssh_key_name = @deploy.ssh_key_name
1190
1169
 
@@ -1318,7 +1297,7 @@ module MU
1318
1297
 
1319
1298
  if @deploy
1320
1299
  MU::Cloud::AWS.createStandardTags(
1321
- resource_id,
1300
+ creation.volume_id,
1322
1301
  region: @config['region'],
1323
1302
  credentials: @config['credentials'],
1324
1303
  optional: @config['optional_tags'],
@@ -1476,11 +1455,11 @@ module MU
1476
1455
  # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
1477
1456
  # @param region [String]: The cloud provider region
1478
1457
  # @return [void]
1479
- def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
1458
+ def self.cleanup(noop: false, deploy_id: MU.deploy_id, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
1480
1459
  onlycloud = flags["onlycloud"]
1481
1460
  skipsnapshots = flags["skipsnapshots"]
1482
1461
  tagfilters = [
1483
- {name: "tag:MU-ID", values: [MU.deploy_id]}
1462
+ {name: "tag:MU-ID", values: [deploy_id]}
1484
1463
  ]
1485
1464
  if !ignoremaster
1486
1465
  tagfilters << {name: "tag:MU-MASTER-IP", values: [MU.mu_public_ip]}
@@ -1514,7 +1493,7 @@ module MU
1514
1493
  threads << Thread.new(instance) { |myinstance|
1515
1494
  MU.dupGlobals(parent_thread_id)
1516
1495
  Thread.abort_on_exception = true
1517
- MU::Cloud::AWS::Server.terminateInstance(id: myinstance.instance_id, noop: noop, onlycloud: onlycloud, region: region, deploy_id: MU.deploy_id, credentials: credentials)
1496
+ MU::Cloud::AWS::Server.terminateInstance(id: myinstance.instance_id, noop: noop, onlycloud: onlycloud, region: region, deploy_id: deploy_id, credentials: credentials)
1518
1497
  }
1519
1498
  }
1520
1499
 
@@ -1525,7 +1504,7 @@ module MU
1525
1504
  threads << Thread.new(volume) { |myvolume|
1526
1505
  MU.dupGlobals(parent_thread_id)
1527
1506
  Thread.abort_on_exception = true
1528
- delete_volume(myvolume, noop, skipsnapshots, credentials: credentials)
1507
+ delete_volume(myvolume, noop, skipsnapshots, credentials: credentials, deploy_id: deploy_id)
1529
1508
  }
1530
1509
  }
1531
1510
 
@@ -1577,7 +1556,11 @@ module MU
1577
1556
  return if !instance
1578
1557
 
1579
1558
  id ||= instance.instance_id
1580
- MU::MommaCat.lock(".cleanup-"+id)
1559
+ begin
1560
+ MU::MommaCat.lock(".cleanup-"+id)
1561
+ rescue Errno::ENOENT => e
1562
+ MU.log "No lock for terminating instance #{id} due to missing metadata", MU::DEBUG
1563
+ end
1581
1564
 
1582
1565
  ips, names = getAddresses(instance, region: region, credentials: credentials)
1583
1566
  targets = ips +names
@@ -1632,7 +1615,11 @@ module MU
1632
1615
  end
1633
1616
 
1634
1617
  MU.log "#{instance.instance_id}#{server_obj ? " ("+server_obj.mu_name+")" : ""} terminated" if !noop
1635
- MU::MommaCat.unlock(".cleanup-"+id)
1618
+ begin
1619
+ MU::MommaCat.unlock(".cleanup-"+id)
1620
+ rescue Errno::ENOENT => e
1621
+ MU.log "No lock for terminating instance #{id} due to missing metadata", MU::DEBUG
1622
+ end
1636
1623
 
1637
1624
  end
1638
1625
 
@@ -1690,26 +1677,7 @@ module MU
1690
1677
  "type" => "object"
1691
1678
  }
1692
1679
  },
1693
- "ingress_rules" => {
1694
- "items" => {
1695
- "properties" => {
1696
- "sgs" => {
1697
- "type" => "array",
1698
- "items" => {
1699
- "description" => "Other AWS Security Groups; resources that are associated with this group will have this rule applied to their traffic",
1700
- "type" => "string"
1701
- }
1702
- },
1703
- "lbs" => {
1704
- "type" => "array",
1705
- "items" => {
1706
- "description" => "AWS Load Balancers which will have this rule applied to their traffic",
1707
- "type" => "string"
1708
- }
1709
- }
1710
- }
1711
- }
1712
- },
1680
+ "ingress_rules" => MU::Cloud.resourceClass("AWS", "FirewallRule").ingressRuleAddtlSchema,
1713
1681
  "ssh_user" => {
1714
1682
  "type" => "string",
1715
1683
  "default" => "root",
@@ -1777,8 +1745,7 @@ module MU
1777
1745
 
1778
1746
  MU::Cloud.availableClouds.each { |cloud|
1779
1747
  next if cloud == "AWS"
1780
- cloudbase = Object.const_get("MU").const_get("Cloud").const_get(cloud)
1781
- foreign_types = (cloudbase.listInstanceTypes).values.first
1748
+ foreign_types = (MU::Cloud.cloudClass(cloud).listInstanceTypes).values.first
1782
1749
  if foreign_types.size == 1
1783
1750
  foreign_types = foreign_types.values.first
1784
1751
  end
@@ -1845,12 +1812,7 @@ module MU
1845
1812
  end
1846
1813
 
1847
1814
  configurator.insertKitten(role, "roles")
1848
-
1849
- server["dependencies"] ||= []
1850
- server["dependencies"] << {
1851
- "type" => "role",
1852
- "name" => server["name"]
1853
- }
1815
+ MU::Config.addDependency(server, server["name"], "role")
1854
1816
  end
1855
1817
 
1856
1818
  # Cloud-specific pre-processing of {MU::Config::BasketofKittens::servers}, bare and unvalidated.
@@ -1901,10 +1863,7 @@ module MU
1901
1863
  server["loadbalancers"].each { |lb|
1902
1864
  lb["name"] ||= lb["concurrent_load_balancer"]
1903
1865
  if lb["name"]
1904
- server["dependencies"] << {
1905
- "type" => "loadbalancer",
1906
- "name" => lb["name"]
1907
- }
1866
+ MU::Config.addDependency(server, lb["name"], "loadbalancer")
1908
1867
  end
1909
1868
  }
1910
1869
  end
@@ -1931,7 +1890,7 @@ module MU
1931
1890
  # @param volume [OpenStruct]: The cloud provider's description of the volume.
1932
1891
  # @param region [String]: The cloud provider region
1933
1892
  # @return [void]
1934
- def self.delete_volume(volume, noop, skipsnapshots, region: MU.curRegion, credentials: nil)
1893
+ def self.delete_volume(volume, noop, skipsnapshots, region: MU.curRegion, credentials: nil, deploy_id: MU.deploy_id)
1935
1894
  if !volume.nil?
1936
1895
  resp = MU::Cloud::AWS.ec2(region: region, credentials: credentials).describe_volumes(volume_ids: [volume.volume_id])
1937
1896
  volume = resp.data.volumes.first
@@ -1946,9 +1905,9 @@ module MU
1946
1905
  if !noop
1947
1906
  if !skipsnapshots
1948
1907
  if !name.nil? and !name.empty?
1949
- desc = "#{MU.deploy_id}-MUfinal (#{name})"
1908
+ desc = "#{deploy_id}-MUfinal (#{name})"
1950
1909
  else
1951
- desc = "#{MU.deploy_id}-MUfinal"
1910
+ desc = "#{deploy_id}-MUfinal"
1952
1911
  end
1953
1912
 
1954
1913
  begin
@@ -2019,6 +1978,13 @@ module MU
2019
1978
  configured_storage
2020
1979
  end
2021
1980
 
1981
+ # Return all of the IP addresses, public and private, from all of our
1982
+ # network interfaces.
1983
+ # @return [Array<String>]
1984
+ def listIPs
1985
+ MU::Cloud::AWS::Server.getAddresses(cloud_desc).first
1986
+ end
1987
+
2022
1988
  private
2023
1989
 
2024
1990
  def bootstrapGroomer
@@ -2144,7 +2110,7 @@ module MU
2144
2110
  subnet = @vpc.getSubnet(cloud_id: cloud_desc.subnet_id)
2145
2111
 
2146
2112
  _nat_ssh_key, _nat_ssh_user, nat_ssh_host, _canonical_ip, _ssh_user, _ssh_key_name = getSSHConfig
2147
- if subnet.private? and !nat_ssh_host and !MU::Cloud::AWS::VPC.haveRouteToInstance?(cloud_desc, region: @config['region'], credentials: @config['credentials'])
2113
+ if subnet.private? and !nat_ssh_host and !MU::Cloud.resourceClass("AWS", "VPC").haveRouteToInstance?(cloud_desc, region: @config['region'], credentials: @config['credentials'])
2148
2114
  raise MuError, "#{@mu_name} is in a private subnet (#{subnet}), but has no bastion host configured, and I have no other route to it"
2149
2115
  end
2150
2116
 
@@ -2236,15 +2202,17 @@ module MU
2236
2202
  alarm["dimensions"] = [{:name => "InstanceId", :value => @cloud_id}]
2237
2203
 
2238
2204
  if alarm["enable_notifications"]
2239
- topic_arn = MU::Cloud::AWS::Notification.createTopic(alarm["notification_group"], region: @config["region"], credentials: @config['credentials'])
2240
- MU::Cloud::AWS::Notification.subscribe(arn: topic_arn, protocol: alarm["notification_type"], endpoint: alarm["notification_endpoint"], region: @config["region"], credentials: @config["credentials"])
2205
+ # XXX vile, this should be a sibling resource generated by the
2206
+ # parser
2207
+ topic_arn = MU::Cloud.resourceClass("AWS", "Notification").createTopic(alarm["notification_group"], region: @config["region"], credentials: @config['credentials'])
2208
+ MU::Cloud.resourceClass("AWS", "Notification").subscribe(topic_arn, alarm["notification_endpoint"], alarm["notification_type"], region: @config["region"], credentials: @config["credentials"])
2241
2209
  alarm["alarm_actions"] = [topic_arn]
2242
2210
  alarm["ok_actions"] = [topic_arn]
2243
2211
  end
2244
2212
 
2245
2213
  alarm_name = alarm_obj ? alarm_obj.cloud_id : "#{@mu_name}-#{alarm['name']}".upcase
2246
2214
 
2247
- MU::Cloud::AWS::Alarm.setAlarm(
2215
+ MU::Cloud.resourceClass("AWS", "Alarm").setAlarm(
2248
2216
  name: alarm_name,
2249
2217
  ok_actions: alarm["ok_actions"],
2250
2218
  alarm_actions: alarm["alarm_actions"],