cloud-mu 3.3.0 → 3.5.1

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 (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