cloud-mu 3.1.6 → 3.4.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 (181) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/bin/mu-adopt +15 -12
  4. data/bin/mu-azure-tests +57 -0
  5. data/bin/mu-cleanup +2 -4
  6. data/bin/mu-configure +37 -1
  7. data/bin/mu-deploy +3 -3
  8. data/bin/mu-findstray-tests +25 -0
  9. data/bin/mu-gen-docs +2 -4
  10. data/bin/mu-load-config.rb +2 -1
  11. data/bin/mu-run-tests +37 -12
  12. data/cloud-mu.gemspec +4 -4
  13. data/cookbooks/mu-tools/attributes/default.rb +7 -0
  14. data/cookbooks/mu-tools/libraries/helper.rb +87 -3
  15. data/cookbooks/mu-tools/recipes/apply_security.rb +39 -23
  16. data/cookbooks/mu-tools/recipes/aws_api.rb +13 -0
  17. data/cookbooks/mu-tools/recipes/google_api.rb +4 -0
  18. data/cookbooks/mu-tools/recipes/rsyslog.rb +8 -1
  19. data/cookbooks/mu-tools/resources/disk.rb +33 -12
  20. data/cookbooks/mu-tools/resources/mommacat_request.rb +1 -2
  21. data/cookbooks/mu-tools/templates/centos-8/sshd_config.erb +215 -0
  22. data/extras/clean-stock-amis +10 -2
  23. data/extras/generate-stock-images +7 -3
  24. data/extras/image-generators/AWS/centos7.yaml +19 -16
  25. data/extras/image-generators/AWS/{rhel7.yaml → rhel71.yaml} +0 -0
  26. data/extras/image-generators/AWS/{win2k12.yaml → win2k12r2.yaml} +0 -0
  27. data/modules/mommacat.ru +2 -2
  28. data/modules/mu.rb +84 -97
  29. data/modules/mu/adoption.rb +359 -59
  30. data/modules/mu/cleanup.rb +67 -44
  31. data/modules/mu/cloud.rb +108 -1754
  32. data/modules/mu/cloud/database.rb +49 -0
  33. data/modules/mu/cloud/dnszone.rb +44 -0
  34. data/modules/mu/cloud/machine_images.rb +212 -0
  35. data/modules/mu/cloud/providers.rb +81 -0
  36. data/modules/mu/cloud/resource_base.rb +929 -0
  37. data/modules/mu/cloud/server.rb +40 -0
  38. data/modules/mu/cloud/server_pool.rb +1 -0
  39. data/modules/mu/cloud/ssh_sessions.rb +228 -0
  40. data/modules/mu/cloud/winrm_sessions.rb +237 -0
  41. data/modules/mu/cloud/wrappers.rb +178 -0
  42. data/modules/mu/config.rb +122 -80
  43. data/modules/mu/config/alarm.rb +2 -6
  44. data/modules/mu/config/bucket.rb +32 -3
  45. data/modules/mu/config/cache_cluster.rb +2 -2
  46. data/modules/mu/config/cdn.rb +100 -0
  47. data/modules/mu/config/collection.rb +1 -1
  48. data/modules/mu/config/container_cluster.rb +2 -2
  49. data/modules/mu/config/database.rb +84 -105
  50. data/modules/mu/config/database.yml +1 -2
  51. data/modules/mu/config/dnszone.rb +5 -4
  52. data/modules/mu/config/doc_helpers.rb +4 -5
  53. data/modules/mu/config/endpoint.rb +2 -1
  54. data/modules/mu/config/firewall_rule.rb +3 -19
  55. data/modules/mu/config/folder.rb +1 -1
  56. data/modules/mu/config/function.rb +17 -8
  57. data/modules/mu/config/group.rb +1 -1
  58. data/modules/mu/config/habitat.rb +1 -1
  59. data/modules/mu/config/job.rb +89 -0
  60. data/modules/mu/config/loadbalancer.rb +57 -11
  61. data/modules/mu/config/log.rb +1 -1
  62. data/modules/mu/config/msg_queue.rb +1 -1
  63. data/modules/mu/config/nosqldb.rb +1 -1
  64. data/modules/mu/config/notifier.rb +8 -19
  65. data/modules/mu/config/ref.rb +81 -9
  66. data/modules/mu/config/role.rb +1 -1
  67. data/modules/mu/config/schema_helpers.rb +30 -34
  68. data/modules/mu/config/search_domain.rb +1 -1
  69. data/modules/mu/config/server.rb +5 -13
  70. data/modules/mu/config/server_pool.rb +3 -7
  71. data/modules/mu/config/storage_pool.rb +1 -1
  72. data/modules/mu/config/tail.rb +10 -0
  73. data/modules/mu/config/user.rb +1 -1
  74. data/modules/mu/config/vpc.rb +13 -17
  75. data/modules/mu/defaults/AWS.yaml +106 -106
  76. data/modules/mu/defaults/Azure.yaml +1 -0
  77. data/modules/mu/defaults/Google.yaml +1 -0
  78. data/modules/mu/deploy.rb +33 -19
  79. data/modules/mu/groomer.rb +15 -0
  80. data/modules/mu/groomers/chef.rb +3 -0
  81. data/modules/mu/logger.rb +120 -144
  82. data/modules/mu/master.rb +22 -1
  83. data/modules/mu/mommacat.rb +71 -26
  84. data/modules/mu/mommacat/daemon.rb +23 -14
  85. data/modules/mu/mommacat/naming.rb +82 -3
  86. data/modules/mu/mommacat/search.rb +59 -16
  87. data/modules/mu/mommacat/storage.rb +119 -48
  88. data/modules/mu/{clouds → providers}/README.md +1 -1
  89. data/modules/mu/{clouds → providers}/aws.rb +248 -62
  90. data/modules/mu/{clouds → providers}/aws/alarm.rb +3 -3
  91. data/modules/mu/{clouds → providers}/aws/bucket.rb +275 -41
  92. data/modules/mu/{clouds → providers}/aws/cache_cluster.rb +14 -50
  93. data/modules/mu/providers/aws/cdn.rb +782 -0
  94. data/modules/mu/{clouds → providers}/aws/collection.rb +5 -5
  95. data/modules/mu/{clouds → providers}/aws/container_cluster.rb +65 -63
  96. data/modules/mu/providers/aws/database.rb +1747 -0
  97. data/modules/mu/{clouds → providers}/aws/dnszone.rb +26 -12
  98. data/modules/mu/providers/aws/endpoint.rb +1072 -0
  99. data/modules/mu/{clouds → providers}/aws/firewall_rule.rb +39 -32
  100. data/modules/mu/{clouds → providers}/aws/folder.rb +1 -1
  101. data/modules/mu/{clouds → providers}/aws/function.rb +291 -133
  102. data/modules/mu/{clouds → providers}/aws/group.rb +18 -20
  103. data/modules/mu/{clouds → providers}/aws/habitat.rb +3 -3
  104. data/modules/mu/providers/aws/job.rb +469 -0
  105. data/modules/mu/{clouds → providers}/aws/loadbalancer.rb +77 -47
  106. data/modules/mu/{clouds → providers}/aws/log.rb +5 -5
  107. data/modules/mu/{clouds → providers}/aws/msg_queue.rb +14 -11
  108. data/modules/mu/{clouds → providers}/aws/nosqldb.rb +96 -5
  109. data/modules/mu/{clouds → providers}/aws/notifier.rb +135 -63
  110. data/modules/mu/{clouds → providers}/aws/role.rb +112 -78
  111. data/modules/mu/{clouds → providers}/aws/search_domain.rb +172 -41
  112. data/modules/mu/{clouds → providers}/aws/server.rb +120 -145
  113. data/modules/mu/{clouds → providers}/aws/server_pool.rb +42 -60
  114. data/modules/mu/{clouds → providers}/aws/storage_pool.rb +21 -38
  115. data/modules/mu/{clouds → providers}/aws/user.rb +12 -16
  116. data/modules/mu/{clouds → providers}/aws/userdata/README.md +0 -0
  117. data/modules/mu/{clouds → providers}/aws/userdata/linux.erb +5 -4
  118. data/modules/mu/{clouds → providers}/aws/userdata/windows.erb +0 -0
  119. data/modules/mu/{clouds → providers}/aws/vpc.rb +141 -73
  120. data/modules/mu/{clouds → providers}/aws/vpc_subnet.rb +0 -0
  121. data/modules/mu/{clouds → providers}/azure.rb +4 -1
  122. data/modules/mu/{clouds → providers}/azure/container_cluster.rb +1 -5
  123. data/modules/mu/{clouds → providers}/azure/firewall_rule.rb +8 -1
  124. data/modules/mu/{clouds → providers}/azure/habitat.rb +0 -0
  125. data/modules/mu/{clouds → providers}/azure/loadbalancer.rb +0 -0
  126. data/modules/mu/{clouds → providers}/azure/role.rb +0 -0
  127. data/modules/mu/{clouds → providers}/azure/server.rb +32 -24
  128. data/modules/mu/{clouds → providers}/azure/user.rb +1 -1
  129. data/modules/mu/{clouds → providers}/azure/userdata/README.md +0 -0
  130. data/modules/mu/{clouds → providers}/azure/userdata/linux.erb +0 -0
  131. data/modules/mu/{clouds → providers}/azure/userdata/windows.erb +0 -0
  132. data/modules/mu/{clouds → providers}/azure/vpc.rb +4 -6
  133. data/modules/mu/{clouds → providers}/cloudformation.rb +1 -1
  134. data/modules/mu/{clouds → providers}/cloudformation/alarm.rb +3 -3
  135. data/modules/mu/{clouds → providers}/cloudformation/cache_cluster.rb +3 -3
  136. data/modules/mu/{clouds → providers}/cloudformation/collection.rb +3 -3
  137. data/modules/mu/{clouds → providers}/cloudformation/database.rb +6 -17
  138. data/modules/mu/{clouds → providers}/cloudformation/dnszone.rb +3 -3
  139. data/modules/mu/{clouds → providers}/cloudformation/firewall_rule.rb +3 -3
  140. data/modules/mu/{clouds → providers}/cloudformation/loadbalancer.rb +3 -3
  141. data/modules/mu/{clouds → providers}/cloudformation/log.rb +3 -3
  142. data/modules/mu/{clouds → providers}/cloudformation/server.rb +7 -7
  143. data/modules/mu/{clouds → providers}/cloudformation/server_pool.rb +5 -5
  144. data/modules/mu/{clouds → providers}/cloudformation/vpc.rb +3 -3
  145. data/modules/mu/{clouds → providers}/docker.rb +0 -0
  146. data/modules/mu/{clouds → providers}/google.rb +15 -6
  147. data/modules/mu/{clouds → providers}/google/bucket.rb +2 -2
  148. data/modules/mu/{clouds → providers}/google/container_cluster.rb +29 -14
  149. data/modules/mu/{clouds → providers}/google/database.rb +2 -9
  150. data/modules/mu/{clouds → providers}/google/firewall_rule.rb +3 -3
  151. data/modules/mu/{clouds → providers}/google/folder.rb +5 -9
  152. data/modules/mu/{clouds → providers}/google/function.rb +4 -4
  153. data/modules/mu/{clouds → providers}/google/group.rb +9 -17
  154. data/modules/mu/{clouds → providers}/google/habitat.rb +4 -8
  155. data/modules/mu/{clouds → providers}/google/loadbalancer.rb +2 -2
  156. data/modules/mu/{clouds → providers}/google/role.rb +46 -35
  157. data/modules/mu/{clouds → providers}/google/server.rb +26 -11
  158. data/modules/mu/{clouds → providers}/google/server_pool.rb +11 -11
  159. data/modules/mu/{clouds → providers}/google/user.rb +32 -22
  160. data/modules/mu/{clouds → providers}/google/userdata/README.md +0 -0
  161. data/modules/mu/{clouds → providers}/google/userdata/linux.erb +0 -0
  162. data/modules/mu/{clouds → providers}/google/userdata/windows.erb +0 -0
  163. data/modules/mu/{clouds → providers}/google/vpc.rb +38 -3
  164. data/modules/tests/aws-jobs-functions.yaml +46 -0
  165. data/modules/tests/centos6.yaml +15 -0
  166. data/modules/tests/centos7.yaml +15 -0
  167. data/modules/tests/centos8.yaml +12 -0
  168. data/modules/tests/ecs.yaml +2 -2
  169. data/modules/tests/eks.yaml +1 -1
  170. data/modules/tests/functions/node-function/lambda_function.js +10 -0
  171. data/modules/tests/functions/python-function/lambda_function.py +12 -0
  172. data/modules/tests/microservice_app.yaml +288 -0
  173. data/modules/tests/rds.yaml +108 -0
  174. data/modules/tests/regrooms/rds.yaml +123 -0
  175. data/modules/tests/server-with-scrub-muisms.yaml +1 -1
  176. data/modules/tests/super_complex_bok.yml +2 -2
  177. data/modules/tests/super_simple_bok.yml +2 -2
  178. data/spec/mu/clouds/azure_spec.rb +2 -2
  179. metadata +126 -98
  180. data/modules/mu/clouds/aws/database.rb +0 -1974
  181. 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
  )
@@ -85,11 +85,11 @@ module MU
85
85
  MU::Cloud.fetchUserdata(
86
86
  platform: @config["platform"],
87
87
  cloud: "AWS",
88
- credentials: @config['credentials'],
88
+ credentials: @credentials,
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"
@@ -243,7 +243,7 @@ module MU
243
243
  MU::Cloud::AWS.createStandardTags(
244
244
  instance.instance_id,
245
245
  region: @config['region'],
246
- credentials: @config['credentials'],
246
+ credentials: @credentials,
247
247
  optional: @config['optional_tags'],
248
248
  nametag: @mu_name,
249
249
  othertags: @config['tags']
@@ -258,7 +258,7 @@ module MU
258
258
  parent_thread_id = Thread.current.object_id
259
259
  Thread.new {
260
260
  MU.dupGlobals(parent_thread_id)
261
- MU::Cloud::AWS::Server.cleanup(noop: false, ignoremaster: false, region: @config['region'], credentials: @config['credentials'], flags: { "skipsnapshots" => true } )
261
+ MU::Cloud::AWS::Server.cleanup(noop: false, ignoremaster: false, region: @config['region'], credentials: @credentials, flags: { "skipsnapshots" => true } )
262
262
  }
263
263
  end
264
264
  end
@@ -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
 
@@ -307,7 +307,7 @@ module MU
307
307
  instance_descriptor[:user_data] = Base64.encode64(@userdata)
308
308
  end
309
309
 
310
- MU::Cloud::AWS::Server.waitForAMI(@config["image_id"], region: @config['region'], credentials: @config['credentials'])
310
+ MU::Cloud::AWS::Server.waitForAMI(@config["image_id"], region: @config['region'], credentials: @credentials)
311
311
 
312
312
  instance_descriptor[:block_device_mappings] = MU::Cloud::AWS::Server.configureBlockDevices(image_id: @config["image_id"], storage: @config['storage'], region: @config['region'], credentials: @credentials)
313
313
 
@@ -332,7 +332,7 @@ module MU
332
332
 
333
333
  begin
334
334
  MU.retrier([Aws::EC2::Errors::InvalidGroupNotFound, Aws::EC2::Errors::InvalidSubnetIDNotFound, Aws::EC2::Errors::InvalidParameterValue], loop_if: loop_if, loop_msg: "Waiting for run_instances to return #{@mu_name}") {
335
- resp = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).run_instances(instance_descriptor)
335
+ resp = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @credentials).run_instances(instance_descriptor)
336
336
  }
337
337
  rescue Aws::EC2::Errors::InvalidRequest => e
338
338
  MU.log e.message, MU::ERR, details: instance_descriptor
@@ -351,12 +351,12 @@ module MU
351
351
  if hard
352
352
  groupname = nil
353
353
  if !@config['basis'].nil?
354
- resp = MU::Cloud::AWS.autoscale(region: @config['region'], credentials: @config['credentials']).describe_auto_scaling_instances(
354
+ resp = MU::Cloud::AWS.autoscale(region: @config['region'], credentials: @credentials).describe_auto_scaling_instances(
355
355
  instance_ids: [@cloud_id]
356
356
  )
357
357
  groupname = resp.auto_scaling_instances.first.auto_scaling_group_name
358
358
  MU.log "Pausing Autoscale processes in #{groupname}", MU::NOTICE
359
- MU::Cloud::AWS.autoscale(region: @config['region'], credentials: @config['credentials']).suspend_processes(
359
+ MU::Cloud::AWS.autoscale(region: @config['region'], credentials: @credentials).suspend_processes(
360
360
  auto_scaling_group_name: groupname,
361
361
  scaling_processes: [
362
362
  "Terminate",
@@ -365,22 +365,22 @@ module MU
365
365
  end
366
366
  begin
367
367
  MU.log "Stopping #{@mu_name} (#{@cloud_id})", MU::NOTICE
368
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).stop_instances(
368
+ MU::Cloud::AWS.ec2(region: @config['region'], credentials: @credentials).stop_instances(
369
369
  instance_ids: [@cloud_id]
370
370
  )
371
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).wait_until(:instance_stopped, instance_ids: [@cloud_id]) do |waiter|
371
+ MU::Cloud::AWS.ec2(region: @config['region'], credentials: @credentials).wait_until(:instance_stopped, instance_ids: [@cloud_id]) do |waiter|
372
372
  waiter.before_attempt do
373
373
  MU.log "Waiting for #{@mu_name} to stop for hard reboot"
374
374
  end
375
375
  end
376
376
  MU.log "Starting #{@mu_name} (#{@cloud_id})"
377
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).start_instances(
377
+ MU::Cloud::AWS.ec2(region: @config['region'], credentials: @credentials).start_instances(
378
378
  instance_ids: [@cloud_id]
379
379
  )
380
380
  ensure
381
381
  if !groupname.nil?
382
382
  MU.log "Resuming Autoscale processes in #{groupname}", MU::NOTICE
383
- MU::Cloud::AWS.autoscale(region: @config['region'], credentials: @config['credentials']).resume_processes(
383
+ MU::Cloud::AWS.autoscale(region: @config['region'], credentials: @credentials).resume_processes(
384
384
  auto_scaling_group_name: groupname,
385
385
  scaling_processes: [
386
386
  "Terminate",
@@ -390,7 +390,7 @@ module MU
390
390
  end
391
391
  else
392
392
  MU.log "Rebooting #{@mu_name} (#{@cloud_id})"
393
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).reboot_instances(
393
+ MU::Cloud::AWS.ec2(region: @config['region'], credentials: @credentials).reboot_instances(
394
394
  instance_ids: [@cloud_id]
395
395
  )
396
396
  end
@@ -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: @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)
@@ -459,7 +458,7 @@ module MU
459
458
  MU::Cloud::AWS.createStandardTags(
460
459
  @cloud_id,
461
460
  region: @config['region'],
462
- credentials: @config['credentials'],
461
+ credentials: @credentials,
463
462
  optional: @config['optional_tags'],
464
463
  nametag: @mu_name,
465
464
  othertags: @config['tags']
@@ -482,7 +481,7 @@ module MU
482
481
  end
483
482
  }
484
483
 
485
- punchAdminNAT
484
+ allowBastionAccess
486
485
 
487
486
  setAlarms
488
487
 
@@ -496,7 +495,7 @@ module MU
496
495
 
497
496
  if !@config['src_dst_check'] and !@config["vpc"].nil?
498
497
  MU.log "Disabling source_dest_check #{@mu_name} (making it NAT-worthy)"
499
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).modify_instance_attribute(
498
+ MU::Cloud::AWS.ec2(region: @config['region'], credentials: @credentials).modify_instance_attribute(
500
499
  instance_id: @cloud_id,
501
500
  source_dest_check: { value: false }
502
501
  )
@@ -504,7 +503,7 @@ module MU
504
503
 
505
504
  # Set console termination protection. Autoscale nodes won't set this
506
505
  # by default.
507
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).modify_instance_attribute(
506
+ MU::Cloud::AWS.ec2(region: @config['region'], credentials: @credentials).modify_instance_attribute(
508
507
  instance_id: @cloud_id,
509
508
  disable_api_termination: { value: true}
510
509
  )
@@ -575,7 +574,7 @@ module MU
575
574
  regions.each { |r|
576
575
  searches.each { |search|
577
576
  search_threads << Thread.new(search) { |params|
578
- MU.retrier([Aws::EC2::Errors::InvalidInstanceIDNotFound], wait: 5, max: 5, ignoreme: [Aws::EC2::Errors::InvalidInstanceIDNotFound]) {
577
+ MU.retrier([], wait: 5, max: 5, ignoreme: [Aws::EC2::Errors::InvalidInstanceIDNotFound]) {
579
578
  MU::Cloud::AWS.ec2(region: r, credentials: args[:credentials]).describe_instances(params).reservations.each { |resp|
580
579
  next if resp.nil? or resp.instances.nil?
581
580
  resp.instances.each { |i|
@@ -605,7 +604,7 @@ module MU
605
604
  def toKitten(**_args)
606
605
  bok = {
607
606
  "cloud" => "AWS",
608
- "credentials" => @config['credentials'],
607
+ "credentials" => @credentials,
609
608
  "cloud_id" => @cloud_id,
610
609
  "region" => @config['region']
611
610
  }
@@ -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
 
@@ -902,7 +883,7 @@ module MU
902
883
  # Canonical Amazon Resource Number for this resource
903
884
  # @return [String]
904
885
  def arn
905
- "arn:"+(MU::Cloud::AWS.isGovCloud?(@config["region"]) ? "aws-us-gov" : "aws")+":ec2:"+@config['region']+":"+MU::Cloud::AWS.credToAcct(@config['credentials'])+":instance/"+@cloud_id
886
+ "arn:"+(MU::Cloud::AWS.isGovCloud?(@config["region"]) ? "aws-us-gov" : "aws")+":ec2:"+@config['region']+":"+MU::Cloud::AWS.credToAcct(@credentials)+":instance/"+@cloud_id
906
887
  end
907
888
 
908
889
  @cloud_desc_cache = nil
@@ -910,11 +891,12 @@ 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?
916
898
  begin
917
- resp = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_instances(instance_ids: [@cloud_id])
899
+ resp = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @credentials).describe_instances(instance_ids: [@cloud_id])
918
900
  if resp and resp.reservations and resp.reservations.first and
919
901
  resp.reservations.first.instances and
920
902
  resp.reservations.first.instances.first
@@ -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: @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,17 +1163,14 @@ 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
 
1191
1170
  retries = 0
1192
1171
  MU.log "Waiting for Windows instance password to be set by Amazon and flagged as available from the API. Note- if you're using a source AMI that already has its password set, this may fail. You'll want to set use_cloud_provider_windows_password to false if this is the case.", MU::NOTICE
1193
1172
  begin
1194
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).wait_until(:password_data_available, instance_id: @cloud_id) do |waiter|
1173
+ MU::Cloud::AWS.ec2(region: @config['region'], credentials: @credentials).wait_until(:password_data_available, instance_id: @cloud_id) do |waiter|
1195
1174
  waiter.max_attempts = 60
1196
1175
  waiter.before_attempt do |attempts|
1197
1176
  MU.log "Waiting for Windows password data to be available for node #{@mu_name}", MU::NOTICE if attempts % 5 == 0
@@ -1211,7 +1190,7 @@ module MU
1211
1190
  end
1212
1191
  end
1213
1192
 
1214
- resp = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).get_password_data(instance_id: @cloud_id)
1193
+ resp = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @credentials).get_password_data(instance_id: @cloud_id)
1215
1194
  encrypted_password = resp.password_data
1216
1195
 
1217
1196
  # Note: This is already implemented in the decrypt_windows_password API call
@@ -1296,19 +1275,19 @@ module MU
1296
1275
  def addVolume(dev, size, type: "gp2", delete_on_termination: false)
1297
1276
 
1298
1277
  if setDeleteOntermination(dev, delete_on_termination)
1299
- MU.log "A volume #{device} already attached to #{self}, skipping", MU::NOTICE
1278
+ MU.log "A volume #{dev} already attached to #{self}, skipping", MU::NOTICE
1300
1279
  return
1301
1280
  end
1302
1281
 
1303
1282
  MU.log "Creating #{size}GB #{type} volume on #{dev} for #{@cloud_id}"
1304
- creation = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).create_volume(
1283
+ creation = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @credentials).create_volume(
1305
1284
  availability_zone: cloud_desc.placement.availability_zone,
1306
1285
  size: size,
1307
1286
  volume_type: type
1308
1287
  )
1309
1288
 
1310
1289
  MU.retrier(wait: 3, loop_if: Proc.new {
1311
- creation = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_volumes(volume_ids: [creation.volume_id]).volumes.first
1290
+ creation = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @credentials).describe_volumes(volume_ids: [creation.volume_id]).volumes.first
1312
1291
  if !["creating", "available"].include?(creation.state)
1313
1292
  raise MuError, "Saw state '#{creation.state}' while creating #{size}GB #{type} volume on #{dev} for #{@cloud_id}"
1314
1293
  end
@@ -1318,24 +1297,27 @@ 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
- credentials: @config['credentials'],
1302
+ credentials: @credentials,
1324
1303
  optional: @config['optional_tags'],
1325
1304
  nametag: @mu_name+"-"+dev.upcase,
1326
1305
  othertags: @config['tags']
1327
1306
  )
1328
1307
  end
1329
1308
 
1330
- attachment = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).attach_volume(
1331
- device: dev,
1332
- instance_id: @cloud_id,
1333
- volume_id: creation.volume_id
1334
- )
1309
+ MU.log "Attaching #{creation.volume_id} as #{dev} to #{@cloud_id} in #{@config['region']} (credentials #{@credentials})"
1310
+ attachment = nil
1311
+ MU.retrier([Aws::EC2::Errors::IncorrectState], wait: 15, max: 4) {
1312
+ attachment = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @credentials).attach_volume(
1313
+ device: dev,
1314
+ instance_id: @cloud_id,
1315
+ volume_id: creation.volume_id
1316
+ )
1317
+ }
1335
1318
 
1336
1319
  begin
1337
- sleep 3
1338
- attachment = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_volumes(volume_ids: [attachment.volume_id]).volumes.first.attachments.first
1320
+ attachment = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @credentials).describe_volumes(volume_ids: [attachment.volume_id]).volumes.first.attachments.first
1339
1321
  if !["attaching", "attached"].include?(attachment.state)
1340
1322
  raise MuError, "Saw state '#{creation.state}' while creating #{size}GB #{type} volume on #{dev} for #{@cloud_id}"
1341
1323
  end
@@ -1355,7 +1337,7 @@ module MU
1355
1337
  return true
1356
1338
  end
1357
1339
  begin
1358
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_instances(
1340
+ MU::Cloud::AWS.ec2(region: @config['region'], credentials: @credentials).describe_instances(
1359
1341
  instance_ids: [@cloud_id]
1360
1342
  ).reservations.each { |resp|
1361
1343
  if !resp.nil? and !resp.instances.nil?
@@ -1476,11 +1458,11 @@ module MU
1476
1458
  # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
1477
1459
  # @param region [String]: The cloud provider region
1478
1460
  # @return [void]
1479
- def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
1461
+ def self.cleanup(noop: false, deploy_id: MU.deploy_id, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
1480
1462
  onlycloud = flags["onlycloud"]
1481
1463
  skipsnapshots = flags["skipsnapshots"]
1482
1464
  tagfilters = [
1483
- {name: "tag:MU-ID", values: [MU.deploy_id]}
1465
+ {name: "tag:MU-ID", values: [deploy_id]}
1484
1466
  ]
1485
1467
  if !ignoremaster
1486
1468
  tagfilters << {name: "tag:MU-MASTER-IP", values: [MU.mu_public_ip]}
@@ -1514,7 +1496,7 @@ module MU
1514
1496
  threads << Thread.new(instance) { |myinstance|
1515
1497
  MU.dupGlobals(parent_thread_id)
1516
1498
  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)
1499
+ MU::Cloud::AWS::Server.terminateInstance(id: myinstance.instance_id, noop: noop, onlycloud: onlycloud, region: region, deploy_id: deploy_id, credentials: credentials)
1518
1500
  }
1519
1501
  }
1520
1502
 
@@ -1525,7 +1507,7 @@ module MU
1525
1507
  threads << Thread.new(volume) { |myvolume|
1526
1508
  MU.dupGlobals(parent_thread_id)
1527
1509
  Thread.abort_on_exception = true
1528
- delete_volume(myvolume, noop, skipsnapshots, credentials: credentials)
1510
+ delete_volume(myvolume, noop, skipsnapshots, credentials: credentials, deploy_id: deploy_id)
1529
1511
  }
1530
1512
  }
1531
1513
 
@@ -1577,7 +1559,11 @@ module MU
1577
1559
  return if !instance
1578
1560
 
1579
1561
  id ||= instance.instance_id
1580
- MU::MommaCat.lock(".cleanup-"+id)
1562
+ begin
1563
+ MU::MommaCat.lock(".cleanup-"+id)
1564
+ rescue Errno::ENOENT => e
1565
+ MU.log "No lock for terminating instance #{id} due to missing metadata", MU::DEBUG
1566
+ end
1581
1567
 
1582
1568
  ips, names = getAddresses(instance, region: region, credentials: credentials)
1583
1569
  targets = ips +names
@@ -1632,7 +1618,11 @@ module MU
1632
1618
  end
1633
1619
 
1634
1620
  MU.log "#{instance.instance_id}#{server_obj ? " ("+server_obj.mu_name+")" : ""} terminated" if !noop
1635
- MU::MommaCat.unlock(".cleanup-"+id)
1621
+ begin
1622
+ MU::MommaCat.unlock(".cleanup-"+id)
1623
+ rescue Errno::ENOENT => e
1624
+ MU.log "No lock for terminating instance #{id} due to missing metadata", MU::DEBUG
1625
+ end
1636
1626
 
1637
1627
  end
1638
1628
 
@@ -1690,26 +1680,7 @@ module MU
1690
1680
  "type" => "object"
1691
1681
  }
1692
1682
  },
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
- },
1683
+ "ingress_rules" => MU::Cloud.resourceClass("AWS", "FirewallRule").ingressRuleAddtlSchema,
1713
1684
  "ssh_user" => {
1714
1685
  "type" => "string",
1715
1686
  "default" => "root",
@@ -1777,8 +1748,7 @@ module MU
1777
1748
 
1778
1749
  MU::Cloud.availableClouds.each { |cloud|
1779
1750
  next if cloud == "AWS"
1780
- cloudbase = Object.const_get("MU").const_get("Cloud").const_get(cloud)
1781
- foreign_types = (cloudbase.listInstanceTypes).values.first
1751
+ foreign_types = (MU::Cloud.cloudClass(cloud).listInstanceTypes).values.first
1782
1752
  if foreign_types.size == 1
1783
1753
  foreign_types = foreign_types.values.first
1784
1754
  end
@@ -1845,12 +1815,7 @@ module MU
1845
1815
  end
1846
1816
 
1847
1817
  configurator.insertKitten(role, "roles")
1848
-
1849
- server["dependencies"] ||= []
1850
- server["dependencies"] << {
1851
- "type" => "role",
1852
- "name" => server["name"]
1853
- }
1818
+ MU::Config.addDependency(server, server["name"], "role")
1854
1819
  end
1855
1820
 
1856
1821
  # Cloud-specific pre-processing of {MU::Config::BasketofKittens::servers}, bare and unvalidated.
@@ -1901,10 +1866,7 @@ module MU
1901
1866
  server["loadbalancers"].each { |lb|
1902
1867
  lb["name"] ||= lb["concurrent_load_balancer"]
1903
1868
  if lb["name"]
1904
- server["dependencies"] << {
1905
- "type" => "loadbalancer",
1906
- "name" => lb["name"]
1907
- }
1869
+ MU::Config.addDependency(server, lb["name"], "loadbalancer")
1908
1870
  end
1909
1871
  }
1910
1872
  end
@@ -1931,7 +1893,7 @@ module MU
1931
1893
  # @param volume [OpenStruct]: The cloud provider's description of the volume.
1932
1894
  # @param region [String]: The cloud provider region
1933
1895
  # @return [void]
1934
- def self.delete_volume(volume, noop, skipsnapshots, region: MU.curRegion, credentials: nil)
1896
+ def self.delete_volume(volume, noop, skipsnapshots, region: MU.curRegion, credentials: nil, deploy_id: MU.deploy_id)
1935
1897
  if !volume.nil?
1936
1898
  resp = MU::Cloud::AWS.ec2(region: region, credentials: credentials).describe_volumes(volume_ids: [volume.volume_id])
1937
1899
  volume = resp.data.volumes.first
@@ -1946,9 +1908,9 @@ module MU
1946
1908
  if !noop
1947
1909
  if !skipsnapshots
1948
1910
  if !name.nil? and !name.empty?
1949
- desc = "#{MU.deploy_id}-MUfinal (#{name})"
1911
+ desc = "#{deploy_id}-MUfinal (#{name})"
1950
1912
  else
1951
- desc = "#{MU.deploy_id}-MUfinal"
1913
+ desc = "#{deploy_id}-MUfinal"
1952
1914
  end
1953
1915
 
1954
1916
  begin
@@ -2019,6 +1981,13 @@ module MU
2019
1981
  configured_storage
2020
1982
  end
2021
1983
 
1984
+ # Return all of the IP addresses, public and private, from all of our
1985
+ # network interfaces.
1986
+ # @return [Array<String>]
1987
+ def listIPs
1988
+ MU::Cloud::AWS::Server.getAddresses(cloud_desc).first
1989
+ end
1990
+
2022
1991
  private
2023
1992
 
2024
1993
  def bootstrapGroomer
@@ -2119,7 +2088,7 @@ module MU
2119
2088
  def haveElasticIP?
2120
2089
  if !cloud_desc.public_ip_address.nil?
2121
2090
  begin
2122
- resp = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_addresses(public_ips: [cloud_desc.public_ip_address])
2091
+ resp = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @credentials).describe_addresses(public_ips: [cloud_desc.public_ip_address])
2123
2092
  if resp.addresses.size > 0 and resp.addresses.first.instance_id == @cloud_id
2124
2093
  return true
2125
2094
  end
@@ -2144,7 +2113,7 @@ module MU
2144
2113
  subnet = @vpc.getSubnet(cloud_id: cloud_desc.subnet_id)
2145
2114
 
2146
2115
  _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'])
2116
+ if subnet.private? and !nat_ssh_host and !MU::Cloud.resourceClass("AWS", "VPC").haveRouteToInstance?(cloud_desc, region: @config['region'], credentials: @credentials)
2148
2117
  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
2118
  end
2150
2119
 
@@ -2161,17 +2130,17 @@ module MU
2161
2130
  next
2162
2131
  end
2163
2132
  MU.log "Adding network interface on subnet #{s.cloud_id} for #{@mu_name}"
2164
- iface = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).create_network_interface(subnet_id: s.cloud_id).network_interface
2133
+ iface = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @credentials).create_network_interface(subnet_id: s.cloud_id).network_interface
2165
2134
  MU::Cloud::AWS.createStandardTags(
2166
2135
  iface.network_interface_id,
2167
2136
  region: @config['region'],
2168
- credentials: @config['credentials'],
2137
+ credentials: @credentials,
2169
2138
  optional: @config['optional_tags'],
2170
2139
  nametag: @mu_name+"-ETH"+device_index.to_s,
2171
2140
  othertags: @config['tags']
2172
2141
  )
2173
2142
 
2174
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).attach_network_interface(
2143
+ MU::Cloud::AWS.ec2(region: @config['region'], credentials: @credentials).attach_network_interface(
2175
2144
  network_interface_id: iface.network_interface_id,
2176
2145
  instance_id: cloud_desc.instance_id,
2177
2146
  device_index: device_index
@@ -2190,7 +2159,7 @@ module MU
2190
2159
  cloud_desc.network_interfaces.each { |int|
2191
2160
  if int.private_ip_address == cloud_desc.private_ip_address and int.private_ip_addresses.size < (@config['add_private_ips'] + 1)
2192
2161
  MU.log "Adding #{@config['add_private_ips']} extra private IP addresses to #{cloud_desc.instance_id}"
2193
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).assign_private_ip_addresses(
2162
+ MU::Cloud::AWS.ec2(region: @config['region'], credentials: @credentials).assign_private_ip_addresses(
2194
2163
  network_interface_id: int.network_interface_id,
2195
2164
  secondary_private_ip_address_count: @config['add_private_ips'],
2196
2165
  allow_reassignment: false
@@ -2201,14 +2170,14 @@ module MU
2201
2170
  end
2202
2171
 
2203
2172
  def tagVolumes
2204
- volumes = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_volumes(filters: [name: "attachment.instance-id", values: [@cloud_id]])
2173
+ volumes = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @credentials).describe_volumes(filters: [name: "attachment.instance-id", values: [@cloud_id]])
2205
2174
  volumes.each { |vol|
2206
2175
  vol.volumes.each { |volume|
2207
2176
  volume.attachments.each { |attachment|
2208
2177
  MU::Cloud::AWS.createStandardTags(
2209
2178
  attachment.volume_id,
2210
2179
  region: @config['region'],
2211
- credentials: @config['credentials'],
2180
+ credentials: @credentials,
2212
2181
  optional: @config['optional_tags'],
2213
2182
  nametag: ["/dev/sda", "/dev/sda1"].include?(attachment.device) ? "ROOT-"+@mu_name : @mu_name+"-"+attachment.device.upcase,
2214
2183
  othertags: @config['tags']
@@ -2236,15 +2205,17 @@ module MU
2236
2205
  alarm["dimensions"] = [{:name => "InstanceId", :value => @cloud_id}]
2237
2206
 
2238
2207
  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"])
2208
+ # XXX vile, this should be a sibling resource generated by the
2209
+ # parser
2210
+ topic_arn = MU::Cloud.resourceClass("AWS", "Notification").createTopic(alarm["notification_group"], region: @config["region"], credentials: @credentials)
2211
+ MU::Cloud.resourceClass("AWS", "Notification").subscribe(topic_arn, alarm["notification_endpoint"], alarm["notification_type"], region: @config["region"], credentials: @config["credentials"])
2241
2212
  alarm["alarm_actions"] = [topic_arn]
2242
2213
  alarm["ok_actions"] = [topic_arn]
2243
2214
  end
2244
2215
 
2245
2216
  alarm_name = alarm_obj ? alarm_obj.cloud_id : "#{@mu_name}-#{alarm['name']}".upcase
2246
2217
 
2247
- MU::Cloud::AWS::Alarm.setAlarm(
2218
+ MU::Cloud.resourceClass("AWS", "Alarm").setAlarm(
2248
2219
  name: alarm_name,
2249
2220
  ok_actions: alarm["ok_actions"],
2250
2221
  alarm_actions: alarm["alarm_actions"],
@@ -2259,7 +2230,7 @@ module MU
2259
2230
  threshold: alarm["threshold"],
2260
2231
  comparison_operator: alarm["comparison_operator"],
2261
2232
  region: @config["region"],
2262
- credentials: @config['credentials']
2233
+ credentials: @credentials
2263
2234
  )
2264
2235
  }
2265
2236
  end
@@ -2269,7 +2240,10 @@ module MU
2269
2240
 
2270
2241
  def getIAMProfile
2271
2242
  arn = if @config['generate_iam_role']
2272
- role = @deploy.findLitterMate(name: @config['name'], type: "roles")
2243
+ role = @deploy.findLitterMate(name: @config['name'], type: "roles", debug: true)
2244
+ if !role
2245
+ raise MuError, "Failed to find a role matching #{@config['name']}"
2246
+ end
2273
2247
  s3_objs = ["#{@deploy.deploy_id}-secret", "#{role.mu_name}.pfx", "#{role.mu_name}.crt", "#{role.mu_name}.key", "#{role.mu_name}-winrm.crt", "#{role.mu_name}-winrm.key"].map { |file|
2274
2248
  'arn:'+(MU::Cloud::AWS.isGovCloud?(@config['region']) ? "aws-us-gov" : "aws")+':s3:::'+MU::Cloud::AWS.adminBucketName(@credentials)+'/'+file
2275
2249
  }
@@ -2304,8 +2278,8 @@ module MU
2304
2278
  if vol[:device_name] == device
2305
2279
  if vol[:ebs][:delete_on_termination] != delete_on_termination
2306
2280
  vol[:ebs][:delete_on_termination] = delete_on_termination
2307
- MU.log "Setting delete_on_termination flag to #{delete_on_termination.to_s} on #{@mu_name}'s #{dev}"
2308
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).modify_instance_attribute(
2281
+ MU.log "Setting delete_on_termination flag to #{delete_on_termination.to_s} on #{@mu_name}'s #{device}"
2282
+ MU::Cloud::AWS.ec2(region: @config['region'], credentials: @credentials).modify_instance_attribute(
2309
2283
  instance_id: @cloud_id,
2310
2284
  block_device_mappings: mappings
2311
2285
  )
@@ -2351,14 +2325,15 @@ module MU
2351
2325
  make_public: img_cfg['public'],
2352
2326
  region: @config['region'],
2353
2327
  tags: @config['tags'],
2354
- credentials: @config['credentials']
2328
+ credentials: @credentials
2355
2329
  )
2330
+
2356
2331
  @deploy.notify("images", @config['name'], ami_ids)
2357
2332
  @config['image_created'] = true
2358
2333
  if img_cfg['image_then_destroy']
2359
- MU::Cloud::AWS::Server.waitForAMI(ami_ids[@config['region']], region: @config['region'], credentials: @config['credentials'])
2334
+ MU::Cloud::AWS::Server.waitForAMI(ami_ids[@config['region']], region: @config['region'], credentials: @credentials)
2360
2335
  MU.log "AMI #{ami_ids[@config['region']]} ready, removing source node #{@mu_name}"
2361
- MU::Cloud::AWS::Server.terminateInstance(id: @cloud_id, region: @config['region'], deploy_id: @deploy.deploy_id, mu_name: @mu_name, credentials: @config['credentials'])
2336
+ MU::Cloud::AWS::Server.terminateInstance(id: @cloud_id, region: @config['region'], deploy_id: @deploy.deploy_id, mu_name: @mu_name, credentials: @credentials)
2362
2337
  destroy
2363
2338
  end
2364
2339
  end