cloud-mu 3.3.0 → 3.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (114) hide show
  1. checksums.yaml +4 -4
  2. data/ansible/roles/mu-nat/tasks/main.yml +3 -0
  3. data/bin/mu-aws-setup +41 -7
  4. data/bin/mu-azure-setup +36 -2
  5. data/bin/mu-configure +214 -119
  6. data/bin/mu-gcp-setup +37 -2
  7. data/bin/mu-node-manage +3 -0
  8. data/bin/mu-refresh-ssl +67 -0
  9. data/bin/mu-run-tests +14 -4
  10. data/bin/mu-self-update +30 -10
  11. data/bin/mu-upload-chef-artifacts +30 -26
  12. data/cloud-mu.gemspec +9 -7
  13. data/cookbooks/mu-master/attributes/default.rb +5 -1
  14. data/cookbooks/mu-master/metadata.rb +2 -2
  15. data/cookbooks/mu-master/recipes/default.rb +81 -26
  16. data/cookbooks/mu-master/recipes/init.rb +197 -62
  17. data/cookbooks/mu-master/recipes/update_nagios_only.rb +1 -1
  18. data/cookbooks/mu-master/recipes/vault.rb +78 -77
  19. data/cookbooks/mu-master/templates/default/mods/rewrite.conf.erb +1 -0
  20. data/cookbooks/mu-master/templates/default/nagios.conf.erb +103 -0
  21. data/cookbooks/mu-master/templates/default/web_app.conf.erb +14 -30
  22. data/cookbooks/mu-tools/attributes/default.rb +12 -0
  23. data/cookbooks/mu-tools/files/centos-6/CentOS-Base.repo +47 -0
  24. data/cookbooks/mu-tools/libraries/helper.rb +98 -4
  25. data/cookbooks/mu-tools/libraries/monkey.rb +1 -1
  26. data/cookbooks/mu-tools/recipes/apply_security.rb +31 -9
  27. data/cookbooks/mu-tools/recipes/aws_api.rb +8 -2
  28. data/cookbooks/mu-tools/recipes/base_repositories.rb +1 -1
  29. data/cookbooks/mu-tools/recipes/gcloud.rb +2 -9
  30. data/cookbooks/mu-tools/recipes/google_api.rb +7 -0
  31. data/cookbooks/mu-tools/recipes/rsyslog.rb +8 -1
  32. data/cookbooks/mu-tools/resources/disk.rb +113 -42
  33. data/cookbooks/mu-tools/resources/mommacat_request.rb +1 -2
  34. data/cookbooks/mu-tools/templates/centos-8/sshd_config.erb +215 -0
  35. data/extras/Gemfile.lock.bootstrap +394 -0
  36. data/extras/bucketstubs/error.html +0 -0
  37. data/extras/bucketstubs/index.html +0 -0
  38. data/extras/clean-stock-amis +11 -3
  39. data/extras/generate-stock-images +6 -3
  40. data/extras/git_rpm/build.sh +20 -0
  41. data/extras/git_rpm/mugit.spec +53 -0
  42. data/extras/image-generators/AWS/centos7.yaml +19 -16
  43. data/extras/image-generators/AWS/{rhel7.yaml → rhel71.yaml} +0 -0
  44. data/extras/image-generators/AWS/{win2k12.yaml → win2k12r2.yaml} +0 -0
  45. data/extras/image-generators/VMWare/centos8.yaml +15 -0
  46. data/extras/openssl_rpm/build.sh +19 -0
  47. data/extras/openssl_rpm/mussl.spec +46 -0
  48. data/extras/python_rpm/muthon.spec +14 -4
  49. data/extras/ruby_rpm/muby.spec +9 -5
  50. data/extras/sqlite_rpm/build.sh +19 -0
  51. data/extras/sqlite_rpm/muqlite.spec +47 -0
  52. data/install/installer +7 -5
  53. data/modules/mommacat.ru +2 -2
  54. data/modules/mu.rb +12 -5
  55. data/modules/mu/cloud/machine_images.rb +1 -1
  56. data/modules/mu/cloud/providers.rb +6 -1
  57. data/modules/mu/cloud/resource_base.rb +7 -4
  58. data/modules/mu/cloud/ssh_sessions.rb +5 -1
  59. data/modules/mu/cloud/wrappers.rb +16 -7
  60. data/modules/mu/config.rb +28 -12
  61. data/modules/mu/config/database.rb +2 -2
  62. data/modules/mu/config/firewall_rule.rb +1 -1
  63. data/modules/mu/config/ref.rb +3 -3
  64. data/modules/mu/config/schema_helpers.rb +12 -3
  65. data/modules/mu/config/server.rb +10 -4
  66. data/modules/mu/config/server_pool.rb +2 -2
  67. data/modules/mu/config/vpc.rb +10 -10
  68. data/modules/mu/defaults/AWS.yaml +96 -96
  69. data/modules/mu/deploy.rb +27 -14
  70. data/modules/mu/groomers/chef.rb +2 -2
  71. data/modules/mu/master.rb +49 -3
  72. data/modules/mu/mommacat.rb +27 -9
  73. data/modules/mu/mommacat/naming.rb +2 -2
  74. data/modules/mu/mommacat/search.rb +16 -5
  75. data/modules/mu/mommacat/storage.rb +67 -32
  76. data/modules/mu/providers/aws.rb +185 -71
  77. data/modules/mu/providers/aws/alarm.rb +3 -3
  78. data/modules/mu/providers/aws/bucket.rb +19 -19
  79. data/modules/mu/providers/aws/cache_cluster.rb +22 -22
  80. data/modules/mu/providers/aws/cdn.rb +2 -2
  81. data/modules/mu/providers/aws/collection.rb +14 -14
  82. data/modules/mu/providers/aws/container_cluster.rb +27 -27
  83. data/modules/mu/providers/aws/database.rb +49 -45
  84. data/modules/mu/providers/aws/dnszone.rb +5 -5
  85. data/modules/mu/providers/aws/endpoint.rb +35 -35
  86. data/modules/mu/providers/aws/firewall_rule.rb +26 -23
  87. data/modules/mu/providers/aws/function.rb +35 -32
  88. data/modules/mu/providers/aws/group.rb +7 -7
  89. data/modules/mu/providers/aws/habitat.rb +2 -2
  90. data/modules/mu/providers/aws/job.rb +35 -32
  91. data/modules/mu/providers/aws/loadbalancer.rb +58 -37
  92. data/modules/mu/providers/aws/log.rb +14 -14
  93. data/modules/mu/providers/aws/msg_queue.rb +10 -10
  94. data/modules/mu/providers/aws/nosqldb.rb +8 -8
  95. data/modules/mu/providers/aws/notifier.rb +7 -7
  96. data/modules/mu/providers/aws/role.rb +69 -47
  97. data/modules/mu/providers/aws/search_domain.rb +10 -10
  98. data/modules/mu/providers/aws/server.rb +198 -110
  99. data/modules/mu/providers/aws/server_pool.rb +71 -119
  100. data/modules/mu/providers/aws/storage_pool.rb +17 -9
  101. data/modules/mu/providers/aws/user.rb +1 -1
  102. data/modules/mu/providers/aws/vpc.rb +106 -51
  103. data/modules/mu/providers/aws/vpc_subnet.rb +43 -39
  104. data/modules/mu/providers/azure.rb +82 -16
  105. data/modules/mu/providers/azure/server.rb +18 -3
  106. data/modules/mu/providers/cloudformation/server.rb +1 -1
  107. data/modules/mu/providers/google.rb +20 -5
  108. data/modules/mu/providers/google/folder.rb +6 -2
  109. data/modules/mu/providers/google/function.rb +65 -30
  110. data/modules/mu/providers/google/role.rb +2 -1
  111. data/modules/mu/providers/google/vpc.rb +27 -2
  112. data/modules/tests/aws-servers-with-handrolled-iam.yaml +37 -0
  113. data/modules/tests/k8s.yaml +1 -1
  114. metadata +32 -15
@@ -36,7 +36,7 @@ module MU
36
36
 
37
37
  MU.log "Creating ElasticSearch domain #{@config['domain_name']}", details: params
38
38
  @cloud_id = @config['domain_name']
39
- MU::Cloud::AWS.elasticsearch(region: @config['region'], credentials: @credentials).create_elasticsearch_domain(params).domain_status
39
+ MU::Cloud::AWS.elasticsearch(region: @region, credentials: @credentials).create_elasticsearch_domain(params).domain_status
40
40
 
41
41
  tagDomain
42
42
 
@@ -52,7 +52,7 @@ module MU
52
52
  waitWhileProcessing # wait until the create finishes, if still going
53
53
 
54
54
  MU.log "Updating ElasticSearch domain #{@config['domain_name']}", MU::NOTICE, details: params
55
- MU::Cloud::AWS.elasticsearch(region: @config['region'], credentials: @credentials).update_elasticsearch_domain_config(params)
55
+ MU::Cloud::AWS.elasticsearch(region: @region, credentials: @credentials).update_elasticsearch_domain_config(params)
56
56
  end
57
57
 
58
58
  waitWhileProcessing # don't return until creation/updating is complete
@@ -68,7 +68,7 @@ module MU
68
68
  @cloud_id ||= @config['domain_name']
69
69
  return nil if !@cloud_id
70
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(
71
+ @cloud_desc_cache = MU::Cloud::AWS.elasticsearch(region: @region, credentials: @credentials).describe_elasticsearch_domain(
72
72
  domain_name: @cloud_id
73
73
  ).domain_status
74
74
  }
@@ -88,7 +88,7 @@ module MU
88
88
  def notify
89
89
  return nil if !cloud_desc(use_cache: false)
90
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
+ tags = MU::Cloud::AWS.elasticsearch(region: @region, credentials: @credentials).list_tags(arn: arn).tag_list
92
92
  deploy_struct['tags'] = tags.map { |t| { t.key => t.value } }
93
93
  if deploy_struct['endpoint']
94
94
  deploy_struct['kibana'] = deploy_struct['endpoint']+"/_plugin/kibana/"
@@ -200,7 +200,7 @@ module MU
200
200
  "cloud" => "AWS",
201
201
  "credentials" => @credentials,
202
202
  "cloud_id" => @cloud_id,
203
- "region" => @config['region']
203
+ "region" => @region
204
204
  }
205
205
 
206
206
  if !cloud_desc
@@ -241,7 +241,7 @@ module MU
241
241
  bok['identity_pool_id'] = cloud_desc.cognito_options.identity_pool_id
242
242
  end
243
243
 
244
- tags = MU::Cloud::AWS.elasticsearch(region: @config['region'], credentials: @credentials).list_tags(arn: cloud_desc.arn).tag_list
244
+ tags = MU::Cloud::AWS.elasticsearch(region: @region, credentials: @credentials).list_tags(arn: cloud_desc.arn).tag_list
245
245
  if tags and !tags.empty?
246
246
  bok['tags'] = MU.structToHash(tags)
247
247
  end
@@ -252,7 +252,7 @@ module MU
252
252
  cloud: "AWS",
253
253
  credentials: @credentials,
254
254
  type: "vpcs",
255
- region: @config['region'],
255
+ region: @region,
256
256
  subnets: cloud_desc.vpc_options.subnet_ids.map { |s| { "subnet_id" => s } }
257
257
  )
258
258
  if cloud_desc.vpc_options.security_group_ids and
@@ -262,7 +262,7 @@ module MU
262
262
  id: sg,
263
263
  cloud: "AWS",
264
264
  credentials: @credentials,
265
- region: @config['region'],
265
+ region: @region,
266
266
  type: "firewall_rules",
267
267
  )
268
268
  }
@@ -683,7 +683,7 @@ module MU
683
683
  params[:log_publishing_options]["SEARCH_SLOW_LOGS"] = {}
684
684
  params[:log_publishing_options]["SEARCH_SLOW_LOGS"][:enabled] = true
685
685
  params[:log_publishing_options]["SEARCH_SLOW_LOGS"][:cloud_watch_logs_log_group_arn] = arn
686
- MU::Cloud.resourceClass("AWS", "Log").allowService("es.amazonaws.com", arn, @config['region'])
686
+ MU::Cloud.resourceClass("AWS", "Log").allowService("es.amazonaws.com", arn, @region)
687
687
  end
688
688
  end
689
689
 
@@ -813,7 +813,7 @@ module MU
813
813
  raise MU::MuError, "Can't tag ElasticSearch domain, cloud descriptor came back without an ARN"
814
814
  end
815
815
 
816
- MU::Cloud::AWS.elasticsearch(region: @config['region'], credentials: @credentials).add_tags(
816
+ MU::Cloud::AWS.elasticsearch(region: @region, credentials: @credentials).add_tags(
817
817
  arn: domain.arn,
818
818
  tag_list: tags
819
819
  )
@@ -85,7 +85,7 @@ 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,
@@ -242,8 +242,8 @@ module MU
242
242
  else
243
243
  MU::Cloud::AWS.createStandardTags(
244
244
  instance.instance_id,
245
- region: @config['region'],
246
- credentials: @config['credentials'],
245
+ region: @region,
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: @region, credentials: @credentials, flags: { "skipsnapshots" => true } )
262
262
  }
263
263
  end
264
264
  end
@@ -307,9 +307,9 @@ 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: @region, credentials: @credentials)
311
311
 
312
- instance_descriptor[:block_device_mappings] = MU::Cloud::AWS::Server.configureBlockDevices(image_id: @config["image_id"], storage: @config['storage'], region: @config['region'], credentials: @credentials)
312
+ instance_descriptor[:block_device_mappings] = MU::Cloud::AWS::Server.configureBlockDevices(image_id: @config["image_id"], storage: @config['storage'], region: @region, credentials: @credentials)
313
313
 
314
314
  instance_descriptor[:monitoring] = {enabled: @config['monitoring']}
315
315
 
@@ -330,10 +330,26 @@ module MU
330
330
  resp.nil? or resp.instances.nil? or instance.nil?
331
331
  }
332
332
 
333
+ bad_subnets = []
334
+ mysubnet_ids = if mySubnets
335
+ mySubnets.map { |s| s.cloud_id }
336
+ end
333
337
  begin
334
338
  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)
339
+ resp = MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).run_instances(instance_descriptor)
336
340
  }
341
+ rescue Aws::EC2::Errors::Unsupported => e
342
+ bad_subnets << instance_descriptor[:subnet_id]
343
+ better_subnet = (mysubnet_ids - bad_subnets).sample
344
+ if e.message !~ /is not supported in your requested Availability Zone/ and
345
+ (mysubnet_ids.nil? or mysubnet_ids.empty? or
346
+ mysubnet_ids.size == bad_subnets.size or
347
+ better_subnet.nil? or better_subnet == "")
348
+ raise MuError.new e.message, details: mysubnet_ids
349
+ end
350
+ instance_descriptor[:subnet_id] = (mysubnet_ids - bad_subnets).sample
351
+ MU.log "One or more subnets does not support this instance type, attempting with #{instance_descriptor[:subnet_id]} instead", MU::WARN, details: bad_subnets
352
+ retry
337
353
  rescue Aws::EC2::Errors::InvalidRequest => e
338
354
  MU.log e.message, MU::ERR, details: instance_descriptor
339
355
  raise e
@@ -351,12 +367,12 @@ module MU
351
367
  if hard
352
368
  groupname = nil
353
369
  if !@config['basis'].nil?
354
- resp = MU::Cloud::AWS.autoscale(region: @config['region'], credentials: @config['credentials']).describe_auto_scaling_instances(
370
+ resp = MU::Cloud::AWS.autoscale(region: @region, credentials: @credentials).describe_auto_scaling_instances(
355
371
  instance_ids: [@cloud_id]
356
372
  )
357
373
  groupname = resp.auto_scaling_instances.first.auto_scaling_group_name
358
374
  MU.log "Pausing Autoscale processes in #{groupname}", MU::NOTICE
359
- MU::Cloud::AWS.autoscale(region: @config['region'], credentials: @config['credentials']).suspend_processes(
375
+ MU::Cloud::AWS.autoscale(region: @region, credentials: @credentials).suspend_processes(
360
376
  auto_scaling_group_name: groupname,
361
377
  scaling_processes: [
362
378
  "Terminate",
@@ -365,22 +381,22 @@ module MU
365
381
  end
366
382
  begin
367
383
  MU.log "Stopping #{@mu_name} (#{@cloud_id})", MU::NOTICE
368
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).stop_instances(
384
+ MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).stop_instances(
369
385
  instance_ids: [@cloud_id]
370
386
  )
371
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).wait_until(:instance_stopped, instance_ids: [@cloud_id]) do |waiter|
387
+ MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).wait_until(:instance_stopped, instance_ids: [@cloud_id]) do |waiter|
372
388
  waiter.before_attempt do
373
389
  MU.log "Waiting for #{@mu_name} to stop for hard reboot"
374
390
  end
375
391
  end
376
392
  MU.log "Starting #{@mu_name} (#{@cloud_id})"
377
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).start_instances(
393
+ MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).start_instances(
378
394
  instance_ids: [@cloud_id]
379
395
  )
380
396
  ensure
381
397
  if !groupname.nil?
382
398
  MU.log "Resuming Autoscale processes in #{groupname}", MU::NOTICE
383
- MU::Cloud::AWS.autoscale(region: @config['region'], credentials: @config['credentials']).resume_processes(
399
+ MU::Cloud::AWS.autoscale(region: @region, credentials: @credentials).resume_processes(
384
400
  auto_scaling_group_name: groupname,
385
401
  scaling_processes: [
386
402
  "Terminate",
@@ -390,7 +406,7 @@ module MU
390
406
  end
391
407
  else
392
408
  MU.log "Rebooting #{@mu_name} (#{@cloud_id})"
393
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).reboot_instances(
409
+ MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).reboot_instances(
394
410
  instance_ids: [@cloud_id]
395
411
  )
396
412
  end
@@ -405,7 +421,7 @@ module MU
405
421
  return nil if @config.nil? or @deploy.nil?
406
422
 
407
423
  nat_ssh_key = nat_ssh_user = nat_ssh_host = nil
408
- if !@config["vpc"].nil? and !MU::Cloud.resourceClass("AWS", "VPC").haveRouteToInstance?(cloud_desc, region: @config['region'], credentials: @config['credentials'])
424
+ if !@config["vpc"].nil? and !MU::Cloud.resourceClass("AWS", "VPC").haveRouteToInstance?(cloud_desc, region: @region, credentials: @credentials)
409
425
  if !@nat.nil?
410
426
  if @nat.is_a?(Struct) && @nat.nat_gateway_id && @nat.nat_gateway_id.start_with?("nat-")
411
427
  raise MuError, "Configured to use NAT Gateway, but I have no route to instance. Either use Bastion, or configure VPC peering"
@@ -449,6 +465,9 @@ module MU
449
465
  raise MuError, "Couldn't find instance #{@mu_name} (#{@cloud_id})" if !cloud_desc
450
466
  return false if !MU::MommaCat.lock(@cloud_id+"-orchestrate", true)
451
467
  return false if !MU::MommaCat.lock(@cloud_id+"-groom", true)
468
+
469
+ getIAMProfile
470
+
452
471
  finish = Proc.new { |status|
453
472
  MU::MommaCat.unlock(@cloud_id+"-orchestrate")
454
473
  MU::MommaCat.unlock(@cloud_id+"-groom")
@@ -457,8 +476,8 @@ module MU
457
476
 
458
477
  MU::Cloud::AWS.createStandardTags(
459
478
  @cloud_id,
460
- region: @config['region'],
461
- credentials: @config['credentials'],
479
+ region: @region,
480
+ credentials: @credentials,
462
481
  optional: @config['optional_tags'],
463
482
  nametag: @mu_name,
464
483
  othertags: @config['tags']
@@ -474,7 +493,15 @@ module MU
474
493
  }
475
494
  MU.retrier([Aws::EC2::Errors::ServiceError], max: 30, wait: 40, loop_if: loop_if) { |retries, _wait|
476
495
  if cloud_desc and cloud_desc.state.name == "terminated"
477
- raise MuError, "#{@cloud_id} appears to have been terminated mid-bootstrap!"
496
+ logs = if !@config['basis'].nil?
497
+ pool = @deploy.findLitterMate(type: "server_pools", name: @config["name"])
498
+ if pool
499
+ MU::Cloud::AWS.autoscale(region: @region, credentials: @credentials).describe_scaling_activities(auto_scaling_group_name: pool.cloud_id).activities
500
+ else
501
+ nil
502
+ end
503
+ end
504
+ raise MuError.new, "#{@cloud_id} appears to have been terminated mid-bootstrap!", details: logs
478
505
  end
479
506
  if retries % 3 == 0
480
507
  MU.log "Waiting for EC2 instance #{@mu_name} (#{@cloud_id}) to be ready...", MU::NOTICE
@@ -495,7 +522,7 @@ module MU
495
522
 
496
523
  if !@config['src_dst_check'] and !@config["vpc"].nil?
497
524
  MU.log "Disabling source_dest_check #{@mu_name} (making it NAT-worthy)"
498
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).modify_instance_attribute(
525
+ MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).modify_instance_attribute(
499
526
  instance_id: @cloud_id,
500
527
  source_dest_check: { value: false }
501
528
  )
@@ -503,7 +530,7 @@ module MU
503
530
 
504
531
  # Set console termination protection. Autoscale nodes won't set this
505
532
  # by default.
506
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).modify_instance_attribute(
533
+ MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).modify_instance_attribute(
507
534
  instance_id: @cloud_id,
508
535
  disable_api_termination: { value: true}
509
536
  )
@@ -516,7 +543,6 @@ module MU
516
543
  notify
517
544
  end
518
545
 
519
- getIAMProfile
520
546
  finish.call(false) if !bootstrapGroomer
521
547
 
522
548
  # Make sure we got our name written everywhere applicable
@@ -574,7 +600,7 @@ module MU
574
600
  regions.each { |r|
575
601
  searches.each { |search|
576
602
  search_threads << Thread.new(search) { |params|
577
- MU.retrier([Aws::EC2::Errors::InvalidInstanceIDNotFound], wait: 5, max: 5, ignoreme: [Aws::EC2::Errors::InvalidInstanceIDNotFound]) {
603
+ MU.retrier([], wait: 5, max: 5, ignoreme: [Aws::EC2::Errors::InvalidInstanceIDNotFound]) {
578
604
  MU::Cloud::AWS.ec2(region: r, credentials: args[:credentials]).describe_instances(params).reservations.each { |resp|
579
605
  next if resp.nil? or resp.instances.nil?
580
606
  resp.instances.each { |i|
@@ -604,9 +630,9 @@ module MU
604
630
  def toKitten(**_args)
605
631
  bok = {
606
632
  "cloud" => "AWS",
607
- "credentials" => @config['credentials'],
633
+ "credentials" => @credentials,
608
634
  "cloud_id" => @cloud_id,
609
- "region" => @config['region']
635
+ "region" => @region
610
636
  }
611
637
 
612
638
  if !cloud_desc
@@ -616,7 +642,7 @@ module MU
616
642
 
617
643
  asgs = MU::Cloud.resourceClass("AWS", "ServerPool").find(
618
644
  instance_id: @cloud_id,
619
- region: @config['region'],
645
+ region: @region,
620
646
  credentials: @credentials
621
647
  )
622
648
  if asgs.size > 0
@@ -651,7 +677,7 @@ module MU
651
677
 
652
678
  bok['image_id'] = cloud_desc.image_id
653
679
 
654
- ami = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @credentials).describe_images(image_ids: [bok['image_id']]).images.first
680
+ ami = MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).describe_images(image_ids: [bok['image_id']]).images.first
655
681
 
656
682
  if ami.nil? or ami.empty?
657
683
  MU.log "#{@mu_name} source image #{bok['image_id']} no longer exists", MU::WARN
@@ -660,7 +686,7 @@ module MU
660
686
 
661
687
  if cloud_desc.block_device_mappings and !cloud_desc.block_device_mappings.empty?
662
688
  vol_map = {}
663
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @credentials).describe_volumes(
689
+ MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).describe_volumes(
664
690
  volume_ids: cloud_desc.block_device_mappings.map { |d| d.ebs.volume_id if d.ebs }
665
691
  ).volumes.each { |vol|
666
692
  vol_map[vol.volume_id] = vol
@@ -696,7 +722,7 @@ module MU
696
722
  id: int.vpc_id,
697
723
  cloud: "AWS",
698
724
  credentials: @credentials,
699
- region: @config['region'],
725
+ region: @region,
700
726
  subnet_id: int.subnet_id,
701
727
  habitat: MU::Config::Ref.get(
702
728
  id: int.owner_id,
@@ -725,11 +751,11 @@ module MU
725
751
  if int.groups.size > 0
726
752
 
727
753
  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'])
754
+ ifaces = MU::Cloud.resourceClass("AWS", "FirewallRule").getAssociatedInterfaces(int.groups.map { |sg| sg.group_id }, credentials: @credentials, region: @region)
729
755
  done_local_rules = false
730
756
  int.groups.each { |sg|
731
757
  if !done_local_rules and ifaces[sg.group_id].size == 1
732
- sg_desc = MU::Cloud.resourceClass("AWS", "FirewallRule").find(cloud_id: sg.group_id, credentials: @credentials, region: @config['region']).values.first
758
+ sg_desc = MU::Cloud.resourceClass("AWS", "FirewallRule").find(cloud_id: sg.group_id, credentials: @credentials, region: @region).values.first
733
759
  if sg_desc
734
760
  bok["ingress_rules"] = MU::Cloud.resourceClass("AWS", "FirewallRule").rulesToBoK(sg_desc.ip_permissions)
735
761
  bok["ingress_rules"].concat(MU::Cloud.resourceClass("AWS", "FirewallRule").rulesToBoK(sg_desc.ip_permissions_egress, egress: true))
@@ -743,7 +769,7 @@ module MU
743
769
  cloud: "AWS",
744
770
  credentials: @credentials,
745
771
  type: "firewall_rules",
746
- region: @config['region']
772
+ region: @region
747
773
  )
748
774
  }
749
775
  end
@@ -799,7 +825,7 @@ module MU
799
825
  if !@config['chef_data'].nil?
800
826
  deploydata.merge!(@config['chef_data'])
801
827
  end
802
- deploydata["region"] = @config['region'] if !@config['region'].nil?
828
+ deploydata["region"] = @region if !@region.nil?
803
829
  if !@named
804
830
  MU::MommaCat.nameKitten(self, no_dns: true)
805
831
  @named = true
@@ -883,7 +909,7 @@ module MU
883
909
  # Canonical Amazon Resource Number for this resource
884
910
  # @return [String]
885
911
  def arn
886
- "arn:"+(MU::Cloud::AWS.isGovCloud?(@config["region"]) ? "aws-us-gov" : "aws")+":ec2:"+@config['region']+":"+MU::Cloud::AWS.credToAcct(@config['credentials'])+":instance/"+@cloud_id
912
+ "arn:"+(MU::Cloud::AWS.isGovCloud?(@region) ? "aws-us-gov" : "aws")+":ec2:"+@region+":"+MU::Cloud::AWS.credToAcct(@credentials)+":instance/"+@cloud_id
887
913
  end
888
914
 
889
915
  @cloud_desc_cache = nil
@@ -896,7 +922,7 @@ module MU
896
922
  retries = 0
897
923
  if !@cloud_id.nil?
898
924
  begin
899
- resp = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_instances(instance_ids: [@cloud_id])
925
+ resp = MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).describe_instances(instance_ids: [@cloud_id])
900
926
  if resp and resp.reservations and resp.reservations.first and
901
927
  resp.reservations.first.instances and
902
928
  resp.reservations.first.instances.first
@@ -943,7 +969,7 @@ module MU
943
969
  # Our deploydata gets corrupted often with server pools, this will cause us to use the wrong IP to identify a node
944
970
  # which will cause us to create certificates, DNS records and other artifacts with incorrect information which will cause our deploy to fail.
945
971
  # The cloud_id is always correct so lets use 'cloud_desc' to get the correct IPs
946
- if MU::Cloud.resourceClass("AWS", "VPC").haveRouteToInstance?(cloud_desc, region: @config['region'], credentials: @config['credentials']) or @deploydata["public_ip_address"].nil?
972
+ if MU::Cloud.resourceClass("AWS", "VPC").haveRouteToInstance?(cloud_desc, region: @region, credentials: @credentials) or @deploydata["public_ip_address"].nil?
947
973
  @config['canonical_ip'] = cloud_desc.private_ip_address
948
974
  @deploydata["private_ip_address"] = cloud_desc.private_ip_address
949
975
  return cloud_desc.private_ip_address
@@ -1170,7 +1196,7 @@ module MU
1170
1196
  retries = 0
1171
1197
  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
1172
1198
  begin
1173
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).wait_until(:password_data_available, instance_id: @cloud_id) do |waiter|
1199
+ MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).wait_until(:password_data_available, instance_id: @cloud_id) do |waiter|
1174
1200
  waiter.max_attempts = 60
1175
1201
  waiter.before_attempt do |attempts|
1176
1202
  MU.log "Waiting for Windows password data to be available for node #{@mu_name}", MU::NOTICE if attempts % 5 == 0
@@ -1182,15 +1208,15 @@ module MU
1182
1208
  rescue Aws::Waiters::Errors::TooManyAttemptsError => e
1183
1209
  if retries < 2
1184
1210
  retries = retries + 1
1185
- MU.log "wait_until(:password_data_available, instance_id: #{@cloud_id}) in #{@config['region']} never got a good response, retrying (#{retries}/2)", MU::WARN, details: e.inspect
1211
+ MU.log "wait_until(:password_data_available, instance_id: #{@cloud_id}) in #{@region} never got a good response, retrying (#{retries}/2)", MU::WARN, details: e.inspect
1186
1212
  retry
1187
1213
  else
1188
- MU.log "wait_until(:password_data_available, instance_id: #{@cloud_id}) in #{@config['region']} never returned- this image may not be configured to have its password set by AWS.", MU::ERR
1214
+ MU.log "wait_until(:password_data_available, instance_id: #{@cloud_id}) in #{@region} never returned- this image may not be configured to have its password set by AWS.", MU::ERR
1189
1215
  return nil
1190
1216
  end
1191
1217
  end
1192
1218
 
1193
- resp = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).get_password_data(instance_id: @cloud_id)
1219
+ resp = MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).get_password_data(instance_id: @cloud_id)
1194
1220
  encrypted_password = resp.password_data
1195
1221
 
1196
1222
  # Note: This is already implemented in the decrypt_windows_password API call
@@ -1209,7 +1235,7 @@ module MU
1209
1235
  # instead of VPC.
1210
1236
  # @param ip [String]: Request a specific IP address.
1211
1237
  # @param region [String]: The cloud provider region
1212
- def self.findFreeElasticIp(classic: false, ip: nil, region: MU.curRegion)
1238
+ def self.findFreeElasticIp(classic: false, ip: nil, region: MU.curRegion, credentials: nil)
1213
1239
  filters = Array.new
1214
1240
  if !classic
1215
1241
  filters << {name: "domain", values: ["vpc"]}
@@ -1219,25 +1245,22 @@ module MU
1219
1245
  filters << {name: "public-ip", values: [ip]} if ip != nil
1220
1246
 
1221
1247
  if filters.size > 0
1222
- resp = MU::Cloud::AWS.ec2(region: region).describe_addresses(filters: filters)
1248
+ resp = MU::Cloud::AWS.ec2(region: region, credentials: credentials).describe_addresses(filters: filters)
1223
1249
  else
1224
- resp = MU::Cloud::AWS.ec2(region: region).describe_addresses()
1250
+ resp = MU::Cloud::AWS.ec2(region: region, credentials: credentials).describe_addresses
1225
1251
  end
1226
1252
  resp.addresses.each { |address|
1227
- return address if (address.network_interface_id.nil? || address.network_interface_id.empty?) && !@eips_used.include?(address.public_ip)
1253
+ return address if (address.network_interface_id.nil? or address.network_interface_id.empty?) or !@eips_used.include?(address.public_ip)
1228
1254
  }
1229
- if ip != nil
1230
- if !classic
1231
- raise MuError, "Requested EIP #{ip}, but no such IP exists or is avaulable in VPC"
1232
- else
1233
- raise MuError, "Requested EIP #{ip}, but no such IP exists or is available in EC2 Classic"
1234
- end
1255
+ if !ip.nil?
1256
+ mode = classic ? "EC2 Classic" : "VPC"
1257
+ raise MuError.new "Requested EIP #{ip}, but no such IP exists or is available in #{mode} mode#{credentials ? " with credentials #{credentials}" : ""}", details: { "describe_address filters" => filters, "describe_address response" => resp }
1235
1258
  end
1236
1259
  if !classic
1237
- resp = MU::Cloud::AWS.ec2(region: region).allocate_address(domain: "vpc")
1260
+ resp = MU::Cloud::AWS.ec2(region: region, credentials: credentials).allocate_address(domain: "vpc")
1238
1261
  new_ip = resp.public_ip
1239
1262
  else
1240
- new_ip = MU::Cloud::AWS.ec2(region: region).allocate_address().public_ip
1263
+ new_ip = MU::Cloud::AWS.ec2(region: region, credentials: credentials).allocate_address().public_ip
1241
1264
  end
1242
1265
  filters = [{name: "public-ip", values: [new_ip]}]
1243
1266
  if resp.domain
@@ -1253,8 +1276,8 @@ module MU
1253
1276
  begin
1254
1277
  begin
1255
1278
  sleep 5
1256
- resp = MU::Cloud::AWS.ec2(region: region).describe_addresses(
1257
- filters: filters
1279
+ resp = MU::Cloud::AWS.ec2(region: region, credentials: credentials).describe_addresses(
1280
+ filters: filters
1258
1281
  )
1259
1282
  addr = resp.addresses.first
1260
1283
  end while resp.addresses.size < 1 or addr.public_ip.nil?
@@ -1275,19 +1298,19 @@ module MU
1275
1298
  def addVolume(dev, size, type: "gp2", delete_on_termination: false)
1276
1299
 
1277
1300
  if setDeleteOntermination(dev, delete_on_termination)
1278
- MU.log "A volume #{device} already attached to #{self}, skipping", MU::NOTICE
1301
+ MU.log "A volume #{dev} already attached to #{self}, skipping", MU::NOTICE
1279
1302
  return
1280
1303
  end
1281
1304
 
1282
1305
  MU.log "Creating #{size}GB #{type} volume on #{dev} for #{@cloud_id}"
1283
- creation = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).create_volume(
1306
+ creation = MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).create_volume(
1284
1307
  availability_zone: cloud_desc.placement.availability_zone,
1285
1308
  size: size,
1286
1309
  volume_type: type
1287
1310
  )
1288
1311
 
1289
1312
  MU.retrier(wait: 3, loop_if: Proc.new {
1290
- creation = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_volumes(volume_ids: [creation.volume_id]).volumes.first
1313
+ creation = MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).describe_volumes(volume_ids: [creation.volume_id]).volumes.first
1291
1314
  if !["creating", "available"].include?(creation.state)
1292
1315
  raise MuError, "Saw state '#{creation.state}' while creating #{size}GB #{type} volume on #{dev} for #{@cloud_id}"
1293
1316
  end
@@ -1298,27 +1321,35 @@ module MU
1298
1321
  if @deploy
1299
1322
  MU::Cloud::AWS.createStandardTags(
1300
1323
  creation.volume_id,
1301
- region: @config['region'],
1302
- credentials: @config['credentials'],
1324
+ region: @region,
1325
+ credentials: @credentials,
1303
1326
  optional: @config['optional_tags'],
1304
1327
  nametag: @mu_name+"-"+dev.upcase,
1305
1328
  othertags: @config['tags']
1306
1329
  )
1307
1330
  end
1308
1331
 
1309
- attachment = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).attach_volume(
1310
- device: dev,
1311
- instance_id: @cloud_id,
1312
- volume_id: creation.volume_id
1313
- )
1332
+ MU.log "Attaching #{creation.volume_id} as #{dev} to #{@cloud_id} in #{@region} (credentials #{@credentials})"
1333
+ attachment = nil
1334
+ MU.retrier([Aws::EC2::Errors::IncorrectState], wait: 15, max: 4) {
1335
+ attachment = MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).attach_volume(
1336
+ device: dev,
1337
+ instance_id: @cloud_id,
1338
+ volume_id: creation.volume_id
1339
+ )
1340
+ }
1314
1341
 
1315
1342
  begin
1316
- sleep 3
1317
- attachment = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_volumes(volume_ids: [attachment.volume_id]).volumes.first.attachments.first
1318
- if !["attaching", "attached"].include?(attachment.state)
1319
- raise MuError, "Saw state '#{creation.state}' while creating #{size}GB #{type} volume on #{dev} for #{@cloud_id}"
1343
+ att_resp = MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).describe_volumes(volume_ids: [attachment.volume_id])
1344
+ if att_resp and att_resp.volumes and !att_resp.volumes.empty? and
1345
+ att_resp.volumes.first.attachments and
1346
+ !att_resp.volumes.first.attachments.empty?
1347
+ attachment = att_resp.volumes.first.attachments.first
1348
+ if !attachment.nil? and !["attaching", "attached"].include?(attachment.state)
1349
+ raise MuError, "Saw state '#{creation.state}' while creating #{size}GB #{type} volume on #{dev} for #{@cloud_id}"
1350
+ end
1320
1351
  end
1321
- end while attachment.state != "attached"
1352
+ end while attachment.nil? or attachment.state != "attached"
1322
1353
 
1323
1354
  # Set delete_on_termination, which for some reason is an instance
1324
1355
  # attribute and not on the attachment
@@ -1334,7 +1365,7 @@ module MU
1334
1365
  return true
1335
1366
  end
1336
1367
  begin
1337
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_instances(
1368
+ MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).describe_instances(
1338
1369
  instance_ids: [@cloud_id]
1339
1370
  ).reservations.each { |resp|
1340
1371
  if !resp.nil? and !resp.instances.nil?
@@ -1385,7 +1416,7 @@ module MU
1385
1416
  }
1386
1417
  end
1387
1418
  end
1388
- elastic_ip = findFreeElasticIp(classic: classic, ip: ip)
1419
+ elastic_ip = findFreeElasticIp(classic: classic, ip: ip, credentials: credentials)
1389
1420
  if !ip.nil? and (elastic_ip.nil? or ip != elastic_ip.public_ip)
1390
1421
  raise MuError, "Requested EIP #{ip}, but this IP does not exist or is not available"
1391
1422
  end
@@ -1737,6 +1768,7 @@ module MU
1737
1768
  return size
1738
1769
  end
1739
1770
 
1771
+
1740
1772
  return size if types.has_key?(size)
1741
1773
 
1742
1774
  if size.nil? or !types.has_key?(size)
@@ -1782,7 +1814,8 @@ module MU
1782
1814
  def self.generateStandardRole(server, configurator)
1783
1815
  role = {
1784
1816
  "name" => server["name"],
1785
- "credentials" => server["credentials"],
1817
+ "bare_policies" => !server['generate_iam_role'],
1818
+ "strip_path" => server["role_strip_path"],
1786
1819
  "can_assume" => [
1787
1820
  {
1788
1821
  "entity_id" => "ec2.amazonaws.com",
@@ -1801,6 +1834,7 @@ module MU
1801
1834
  }
1802
1835
  ]
1803
1836
  }
1837
+ role["credentials"] = server["credentials"] if server["credentials"]
1804
1838
  if server['iam_policies']
1805
1839
  role['iam_policies'] = server['iam_policies'].dup
1806
1840
  end
@@ -1834,9 +1868,10 @@ module MU
1834
1868
  MU.log "Cannot mix iam_policies with generate_iam_role set to false", MU::ERR
1835
1869
  ok = false
1836
1870
  end
1837
- else
1838
- generateStandardRole(server, configurator)
1839
1871
  end
1872
+
1873
+ generateStandardRole(server, configurator)
1874
+
1840
1875
  if !server['create_image'].nil?
1841
1876
  if server['create_image'].has_key?('copy_to_regions') and
1842
1877
  (server['create_image']['copy_to_regions'].nil? or
@@ -2085,7 +2120,7 @@ module MU
2085
2120
  def haveElasticIP?
2086
2121
  if !cloud_desc.public_ip_address.nil?
2087
2122
  begin
2088
- resp = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_addresses(public_ips: [cloud_desc.public_ip_address])
2123
+ resp = MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).describe_addresses(public_ips: [cloud_desc.public_ip_address])
2089
2124
  if resp.addresses.size > 0 and resp.addresses.first.instance_id == @cloud_id
2090
2125
  return true
2091
2126
  end
@@ -2100,9 +2135,9 @@ module MU
2100
2135
  def configureNetworking
2101
2136
  if !@config['static_ip'].nil?
2102
2137
  if !@config['static_ip']['ip'].nil?
2103
- MU::Cloud::AWS::Server.associateElasticIp(@cloud_id, classic: @vpc.nil?, ip: @config['static_ip']['ip'])
2138
+ MU::Cloud::AWS::Server.associateElasticIp(@cloud_id, classic: @vpc.nil?, ip: @config['static_ip']['ip'], credentials: @credentials)
2104
2139
  elsif !haveElasticIP?
2105
- MU::Cloud::AWS::Server.associateElasticIp(@cloud_id, classic: @vpc.nil?)
2140
+ MU::Cloud::AWS::Server.associateElasticIp(@cloud_id, classic: @vpc.nil?, credentials: @credentials)
2106
2141
  end
2107
2142
  end
2108
2143
 
@@ -2110,7 +2145,7 @@ module MU
2110
2145
  subnet = @vpc.getSubnet(cloud_id: cloud_desc.subnet_id)
2111
2146
 
2112
2147
  _nat_ssh_key, _nat_ssh_user, nat_ssh_host, _canonical_ip, _ssh_user, _ssh_key_name = getSSHConfig
2113
- if subnet.private? and !nat_ssh_host and !MU::Cloud.resourceClass("AWS", "VPC").haveRouteToInstance?(cloud_desc, region: @config['region'], credentials: @config['credentials'])
2148
+ if subnet.private? and !nat_ssh_host and !MU::Cloud.resourceClass("AWS", "VPC").haveRouteToInstance?(cloud_desc, region: @region, credentials: @credentials)
2114
2149
  raise MuError, "#{@mu_name} is in a private subnet (#{subnet}), but has no bastion host configured, and I have no other route to it"
2115
2150
  end
2116
2151
 
@@ -2127,17 +2162,17 @@ module MU
2127
2162
  next
2128
2163
  end
2129
2164
  MU.log "Adding network interface on subnet #{s.cloud_id} for #{@mu_name}"
2130
- iface = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).create_network_interface(subnet_id: s.cloud_id).network_interface
2165
+ iface = MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).create_network_interface(subnet_id: s.cloud_id).network_interface
2131
2166
  MU::Cloud::AWS.createStandardTags(
2132
2167
  iface.network_interface_id,
2133
- region: @config['region'],
2134
- credentials: @config['credentials'],
2168
+ region: @region,
2169
+ credentials: @credentials,
2135
2170
  optional: @config['optional_tags'],
2136
2171
  nametag: @mu_name+"-ETH"+device_index.to_s,
2137
2172
  othertags: @config['tags']
2138
2173
  )
2139
2174
 
2140
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).attach_network_interface(
2175
+ MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).attach_network_interface(
2141
2176
  network_interface_id: iface.network_interface_id,
2142
2177
  instance_id: cloud_desc.instance_id,
2143
2178
  device_index: device_index
@@ -2156,7 +2191,7 @@ module MU
2156
2191
  cloud_desc.network_interfaces.each { |int|
2157
2192
  if int.private_ip_address == cloud_desc.private_ip_address and int.private_ip_addresses.size < (@config['add_private_ips'] + 1)
2158
2193
  MU.log "Adding #{@config['add_private_ips']} extra private IP addresses to #{cloud_desc.instance_id}"
2159
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).assign_private_ip_addresses(
2194
+ MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).assign_private_ip_addresses(
2160
2195
  network_interface_id: int.network_interface_id,
2161
2196
  secondary_private_ip_address_count: @config['add_private_ips'],
2162
2197
  allow_reassignment: false
@@ -2167,14 +2202,14 @@ module MU
2167
2202
  end
2168
2203
 
2169
2204
  def tagVolumes
2170
- volumes = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_volumes(filters: [name: "attachment.instance-id", values: [@cloud_id]])
2205
+ volumes = MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).describe_volumes(filters: [name: "attachment.instance-id", values: [@cloud_id]])
2171
2206
  volumes.each { |vol|
2172
2207
  vol.volumes.each { |volume|
2173
2208
  volume.attachments.each { |attachment|
2174
2209
  MU::Cloud::AWS.createStandardTags(
2175
2210
  attachment.volume_id,
2176
- region: @config['region'],
2177
- credentials: @config['credentials'],
2211
+ region: @region,
2212
+ credentials: @credentials,
2178
2213
  optional: @config['optional_tags'],
2179
2214
  nametag: ["/dev/sda", "/dev/sda1"].include?(attachment.device) ? "ROOT-"+@mu_name : @mu_name+"-"+attachment.device.upcase,
2180
2215
  othertags: @config['tags']
@@ -2195,7 +2230,7 @@ module MU
2195
2230
  alarm_obj = MU::MommaCat.findStray(
2196
2231
  "AWS",
2197
2232
  "alarms",
2198
- region: @config["region"],
2233
+ region: @region,
2199
2234
  deploy_id: @deploy.deploy_id,
2200
2235
  name: alarm['name']
2201
2236
  ).first
@@ -2204,8 +2239,8 @@ module MU
2204
2239
  if alarm["enable_notifications"]
2205
2240
  # XXX vile, this should be a sibling resource generated by the
2206
2241
  # 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"])
2242
+ topic_arn = MU::Cloud.resourceClass("AWS", "Notification").createTopic(alarm["notification_group"], region: @region, credentials: @credentials)
2243
+ MU::Cloud.resourceClass("AWS", "Notification").subscribe(topic_arn, alarm["notification_endpoint"], alarm["notification_type"], region: @region, credentials: @credentials)
2209
2244
  alarm["alarm_actions"] = [topic_arn]
2210
2245
  alarm["ok_actions"] = [topic_arn]
2211
2246
  end
@@ -2226,36 +2261,88 @@ module MU
2226
2261
  evaluation_periods: alarm["evaluation_periods"],
2227
2262
  threshold: alarm["threshold"],
2228
2263
  comparison_operator: alarm["comparison_operator"],
2229
- region: @config["region"],
2230
- credentials: @config['credentials']
2264
+ region: @region,
2265
+ credentials: @credentials
2231
2266
  )
2232
2267
  }
2233
2268
  end
2234
2269
  end
2235
2270
 
2236
- # We have issues sometimes where our dns_records are pointing at the wrong node name and IP address.
2237
-
2238
2271
  def getIAMProfile
2239
- arn = if @config['generate_iam_role']
2240
- role = @deploy.findLitterMate(name: @config['name'], type: "roles")
2241
- 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|
2242
- 'arn:'+(MU::Cloud::AWS.isGovCloud?(@config['region']) ? "aws-us-gov" : "aws")+':s3:::'+MU::Cloud::AWS.adminBucketName(@credentials)+'/'+file
2272
+ self.class.getIAMProfile(
2273
+ @config['name'],
2274
+ @deploy,
2275
+ generated: @config['generate_iam_role'],
2276
+ role_name: @config['iam_role'],
2277
+ region: @region,
2278
+ credentials: @credentials,
2279
+ want_arn: true
2280
+ )
2281
+ end
2282
+
2283
+ # XXX move to public section
2284
+ def self.getIAMProfile(myname, deploy, generated: true, role_name: nil, region: nil, credentials: nil, want_arn: false)
2285
+
2286
+ arn = if generated
2287
+ role = deploy.findLitterMate(name: myname, type: "roles", debug: true)
2288
+ if !role
2289
+ raise MuError, "Failed to find a role matching #{myname}"
2290
+ end
2291
+ 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|
2292
+ 'arn:'+(MU::Cloud::AWS.isGovCloud?(region) ? "aws-us-gov" : "aws")+':s3:::'+MU::Cloud::AWS.adminBucketName(credentials)+'/'+file
2243
2293
  }
2244
- MU.log "Adding S3 read permissions to #{@mu_name}'s IAM profile", MU::NOTICE, details: s3_objs
2294
+ MU.log "Adding S3 read permissions to #{myname}'s IAM profile", MU::NOTICE, details: s3_objs
2245
2295
  role.cloudobj.injectPolicyTargets("MuSecrets", s3_objs)
2246
2296
 
2247
- @config['iam_role'] = role.mu_name
2297
+ role_name = role.mu_name
2248
2298
  role.cloudobj.createInstanceProfile
2249
2299
 
2250
- elsif @config['iam_role'].nil?
2251
- raise MuError, "#{@mu_name} has generate_iam_role set to false, but no iam_role assigned."
2300
+ elsif role_name.nil?
2301
+ raise MuError, "#{myname} has generate_iam_role set to false, but no iam_role assigned."
2302
+ else
2303
+ begin
2304
+ ext_prof = MU::Cloud::AWS.iam(credentials: credentials).get_instance_profile(instance_profile_name: role_name)
2305
+ role_name = ext_prof.instance_profile.instance_profile_name
2306
+ ext_prof.instance_profile.arn
2307
+ rescue Aws::IAM::Errors::NoSuchEntity
2308
+ role = MU::MommaCat.findStray("AWS", "role", cloud_id: role_name, dummy_ok: true, credentials: credentials).first
2309
+ if !role
2310
+ raise MuError, "#{myname} specified iam_role '#{role_name}', but I can't find a role with that name to use when creating an instance profile"
2311
+ end
2312
+ role.cloudobj.createInstanceProfile
2313
+ end
2252
2314
  end
2253
2315
 
2254
- if !@config["iam_role"].nil?
2255
- if arn
2316
+ role_or_policy = deploy.findLitterMate(name: myname, type: "roles")
2317
+
2318
+ # Make sure our permissions to read our identity secrets are set
2319
+ s3_objs = [
2320
+ "#{deploy.deploy_id}-secret",
2321
+ "#{role_or_policy.mu_name}.pfx",
2322
+ "#{role_or_policy.mu_name}.crt",
2323
+ "#{role_or_policy.mu_name}.key",
2324
+ "#{role_or_policy.mu_name}-winrm.crt",
2325
+ "#{role_or_policy.mu_name}-winrm.key"].map { |file|
2326
+ 'arn:'+(MU::Cloud::AWS.isGovCloud?(region) ? "aws-us-gov" : "aws")+':s3:::'+MU::Cloud::AWS.adminBucketName(credentials)+'/'+file
2327
+ }
2328
+ if generated
2329
+ role_or_policy.injectPolicyTargets("MuSecrets", s3_objs)
2330
+ elsif role_name
2331
+ realrole = MU::MommaCat.findStray("AWS", "role", cloud_id: role_name, dummy_ok: true, credentials: credentials).first
2332
+ if !role_or_policy
2333
+ raise MuError, "I should have a bare policy littermate named #{name} but I can't find it"
2334
+ end
2335
+ if realrole
2336
+ role_or_policy.bindTo("role", realrole.cloud_id)
2337
+ realrole.injectPolicyTargets(role_or_policy.mu_name+"-MUSECRETS", s3_objs)
2338
+ end
2339
+ end
2340
+
2341
+ if !role_name.nil?
2342
+ if arn and want_arn
2256
2343
  return {arn: arn}
2257
2344
  else
2258
- return {name: @config["iam_role"]}
2345
+ return {name: role_name}
2259
2346
  end
2260
2347
  end
2261
2348
 
@@ -2272,8 +2359,8 @@ module MU
2272
2359
  if vol[:device_name] == device
2273
2360
  if vol[:ebs][:delete_on_termination] != delete_on_termination
2274
2361
  vol[:ebs][:delete_on_termination] = delete_on_termination
2275
- MU.log "Setting delete_on_termination flag to #{delete_on_termination.to_s} on #{@mu_name}'s #{dev}"
2276
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).modify_instance_attribute(
2362
+ MU.log "Setting delete_on_termination flag to #{delete_on_termination.to_s} on #{@mu_name}'s #{device}"
2363
+ MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).modify_instance_attribute(
2277
2364
  instance_id: @cloud_id,
2278
2365
  block_device_mappings: mappings
2279
2366
  )
@@ -2317,16 +2404,17 @@ module MU
2317
2404
  exclude_storage: img_cfg['image_exclude_storage'],
2318
2405
  copy_to_regions: img_cfg['copy_to_regions'],
2319
2406
  make_public: img_cfg['public'],
2320
- region: @config['region'],
2407
+ region: @region,
2321
2408
  tags: @config['tags'],
2322
- credentials: @config['credentials']
2409
+ credentials: @credentials
2323
2410
  )
2411
+
2324
2412
  @deploy.notify("images", @config['name'], ami_ids)
2325
2413
  @config['image_created'] = true
2326
2414
  if img_cfg['image_then_destroy']
2327
- MU::Cloud::AWS::Server.waitForAMI(ami_ids[@config['region']], region: @config['region'], credentials: @config['credentials'])
2328
- MU.log "AMI #{ami_ids[@config['region']]} ready, removing source node #{@mu_name}"
2329
- MU::Cloud::AWS::Server.terminateInstance(id: @cloud_id, region: @config['region'], deploy_id: @deploy.deploy_id, mu_name: @mu_name, credentials: @config['credentials'])
2415
+ MU::Cloud::AWS::Server.waitForAMI(ami_ids[@region], region: @region, credentials: @credentials)
2416
+ MU.log "AMI #{ami_ids[@region]} ready, removing source node #{@mu_name}"
2417
+ MU::Cloud::AWS::Server.terminateInstance(id: @cloud_id, region: @region, deploy_id: @deploy.deploy_id, mu_name: @mu_name, credentials: @credentials)
2330
2418
  destroy
2331
2419
  end
2332
2420
  end