cloud-mu 3.1.4 → 3.3.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.
- checksums.yaml +4 -4
- data/Dockerfile +5 -1
- data/ansible/roles/mu-windows/README.md +33 -0
- data/ansible/roles/mu-windows/defaults/main.yml +2 -0
- data/ansible/roles/mu-windows/files/LaunchConfig.json +9 -0
- data/ansible/roles/mu-windows/files/config.xml +76 -0
- data/ansible/roles/mu-windows/handlers/main.yml +2 -0
- data/ansible/roles/mu-windows/meta/main.yml +53 -0
- data/ansible/roles/mu-windows/tasks/main.yml +36 -0
- data/ansible/roles/mu-windows/tests/inventory +2 -0
- data/ansible/roles/mu-windows/tests/test.yml +5 -0
- data/ansible/roles/mu-windows/vars/main.yml +2 -0
- data/bin/mu-adopt +16 -12
- data/bin/mu-azure-tests +57 -0
- data/bin/mu-cleanup +2 -4
- data/bin/mu-configure +52 -0
- data/bin/mu-deploy +3 -3
- data/bin/mu-findstray-tests +25 -0
- data/bin/mu-gen-docs +2 -4
- data/bin/mu-load-config.rb +2 -1
- data/bin/mu-node-manage +15 -16
- data/bin/mu-run-tests +37 -12
- data/cloud-mu.gemspec +5 -3
- data/cookbooks/mu-activedirectory/resources/domain.rb +4 -4
- data/cookbooks/mu-activedirectory/resources/domain_controller.rb +4 -4
- data/cookbooks/mu-tools/libraries/helper.rb +1 -1
- data/cookbooks/mu-tools/recipes/apply_security.rb +14 -14
- data/cookbooks/mu-tools/recipes/aws_api.rb +9 -0
- data/cookbooks/mu-tools/recipes/eks.rb +2 -2
- data/cookbooks/mu-tools/recipes/selinux.rb +2 -1
- data/cookbooks/mu-tools/recipes/windows-client.rb +163 -164
- data/cookbooks/mu-tools/resources/windows_users.rb +44 -43
- data/extras/clean-stock-amis +25 -19
- data/extras/generate-stock-images +1 -0
- data/extras/image-generators/AWS/win2k12.yaml +18 -13
- data/extras/image-generators/AWS/win2k16.yaml +18 -13
- data/extras/image-generators/AWS/win2k19.yaml +21 -0
- data/modules/mommacat.ru +1 -1
- data/modules/mu.rb +158 -107
- data/modules/mu/adoption.rb +386 -59
- data/modules/mu/cleanup.rb +214 -303
- data/modules/mu/cloud.rb +128 -1632
- data/modules/mu/cloud/database.rb +49 -0
- data/modules/mu/cloud/dnszone.rb +44 -0
- data/modules/mu/cloud/machine_images.rb +212 -0
- data/modules/mu/cloud/providers.rb +81 -0
- data/modules/mu/cloud/resource_base.rb +926 -0
- data/modules/mu/cloud/server.rb +40 -0
- data/modules/mu/cloud/server_pool.rb +1 -0
- data/modules/mu/cloud/ssh_sessions.rb +228 -0
- data/modules/mu/cloud/winrm_sessions.rb +237 -0
- data/modules/mu/cloud/wrappers.rb +169 -0
- data/modules/mu/config.rb +135 -82
- data/modules/mu/config/alarm.rb +2 -6
- data/modules/mu/config/bucket.rb +32 -3
- data/modules/mu/config/cache_cluster.rb +2 -2
- data/modules/mu/config/cdn.rb +100 -0
- data/modules/mu/config/collection.rb +1 -1
- data/modules/mu/config/container_cluster.rb +7 -2
- data/modules/mu/config/database.rb +84 -105
- data/modules/mu/config/database.yml +1 -2
- data/modules/mu/config/dnszone.rb +5 -4
- data/modules/mu/config/doc_helpers.rb +5 -6
- data/modules/mu/config/endpoint.rb +2 -1
- data/modules/mu/config/firewall_rule.rb +3 -19
- data/modules/mu/config/folder.rb +1 -1
- data/modules/mu/config/function.rb +17 -8
- data/modules/mu/config/group.rb +1 -1
- data/modules/mu/config/habitat.rb +1 -1
- data/modules/mu/config/job.rb +89 -0
- data/modules/mu/config/loadbalancer.rb +57 -11
- data/modules/mu/config/log.rb +1 -1
- data/modules/mu/config/msg_queue.rb +1 -1
- data/modules/mu/config/nosqldb.rb +1 -1
- data/modules/mu/config/notifier.rb +8 -19
- data/modules/mu/config/ref.rb +92 -14
- data/modules/mu/config/role.rb +1 -1
- data/modules/mu/config/schema_helpers.rb +38 -37
- data/modules/mu/config/search_domain.rb +1 -1
- data/modules/mu/config/server.rb +12 -13
- data/modules/mu/config/server.yml +1 -0
- data/modules/mu/config/server_pool.rb +3 -7
- data/modules/mu/config/storage_pool.rb +1 -1
- data/modules/mu/config/tail.rb +11 -0
- data/modules/mu/config/user.rb +1 -1
- data/modules/mu/config/vpc.rb +27 -23
- data/modules/mu/config/vpc.yml +0 -1
- data/modules/mu/defaults/AWS.yaml +91 -68
- data/modules/mu/defaults/Azure.yaml +1 -0
- data/modules/mu/defaults/Google.yaml +1 -0
- data/modules/mu/deploy.rb +33 -19
- data/modules/mu/groomer.rb +16 -1
- data/modules/mu/groomers/ansible.rb +123 -21
- data/modules/mu/groomers/chef.rb +64 -11
- data/modules/mu/logger.rb +120 -144
- data/modules/mu/master.rb +97 -4
- data/modules/mu/master/ssl.rb +0 -1
- data/modules/mu/mommacat.rb +154 -867
- data/modules/mu/mommacat/daemon.rb +23 -14
- data/modules/mu/mommacat/naming.rb +110 -3
- data/modules/mu/mommacat/search.rb +495 -0
- data/modules/mu/mommacat/storage.rb +225 -192
- data/modules/mu/{clouds → providers}/README.md +1 -1
- data/modules/mu/{clouds → providers}/aws.rb +281 -64
- data/modules/mu/{clouds → providers}/aws/alarm.rb +3 -3
- data/modules/mu/{clouds → providers}/aws/bucket.rb +275 -41
- data/modules/mu/{clouds → providers}/aws/cache_cluster.rb +14 -50
- data/modules/mu/providers/aws/cdn.rb +782 -0
- data/modules/mu/{clouds → providers}/aws/collection.rb +5 -5
- data/modules/mu/{clouds → providers}/aws/container_cluster.rb +708 -749
- data/modules/mu/providers/aws/database.rb +1744 -0
- data/modules/mu/{clouds → providers}/aws/dnszone.rb +75 -57
- data/modules/mu/providers/aws/endpoint.rb +1072 -0
- data/modules/mu/{clouds → providers}/aws/firewall_rule.rb +212 -242
- data/modules/mu/{clouds → providers}/aws/folder.rb +1 -1
- data/modules/mu/{clouds → providers}/aws/function.rb +289 -134
- data/modules/mu/{clouds → providers}/aws/group.rb +18 -20
- data/modules/mu/{clouds → providers}/aws/habitat.rb +3 -3
- data/modules/mu/providers/aws/job.rb +466 -0
- data/modules/mu/{clouds → providers}/aws/loadbalancer.rb +50 -41
- data/modules/mu/{clouds → providers}/aws/log.rb +5 -5
- data/modules/mu/{clouds → providers}/aws/msg_queue.rb +14 -11
- data/modules/mu/{clouds → providers}/aws/nosqldb.rb +96 -5
- data/modules/mu/{clouds → providers}/aws/notifier.rb +135 -63
- data/modules/mu/{clouds → providers}/aws/role.rb +94 -57
- data/modules/mu/{clouds → providers}/aws/search_domain.rb +173 -42
- data/modules/mu/{clouds → providers}/aws/server.rb +782 -1107
- data/modules/mu/{clouds → providers}/aws/server_pool.rb +36 -46
- data/modules/mu/{clouds → providers}/aws/storage_pool.rb +21 -38
- data/modules/mu/{clouds → providers}/aws/user.rb +12 -16
- data/modules/mu/{clouds → providers}/aws/userdata/README.md +0 -0
- data/modules/mu/{clouds → providers}/aws/userdata/linux.erb +5 -4
- data/modules/mu/{clouds → providers}/aws/userdata/windows.erb +2 -1
- data/modules/mu/{clouds → providers}/aws/vpc.rb +429 -849
- data/modules/mu/providers/aws/vpc_subnet.rb +286 -0
- data/modules/mu/{clouds → providers}/azure.rb +13 -0
- data/modules/mu/{clouds → providers}/azure/container_cluster.rb +1 -5
- data/modules/mu/{clouds → providers}/azure/firewall_rule.rb +8 -1
- data/modules/mu/{clouds → providers}/azure/habitat.rb +0 -0
- data/modules/mu/{clouds → providers}/azure/loadbalancer.rb +0 -0
- data/modules/mu/{clouds → providers}/azure/role.rb +0 -0
- data/modules/mu/{clouds → providers}/azure/server.rb +32 -24
- data/modules/mu/{clouds → providers}/azure/user.rb +1 -1
- data/modules/mu/{clouds → providers}/azure/userdata/README.md +0 -0
- data/modules/mu/{clouds → providers}/azure/userdata/linux.erb +0 -0
- data/modules/mu/{clouds → providers}/azure/userdata/windows.erb +0 -0
- data/modules/mu/{clouds → providers}/azure/vpc.rb +4 -6
- data/modules/mu/{clouds → providers}/cloudformation.rb +10 -0
- data/modules/mu/{clouds → providers}/cloudformation/alarm.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/cache_cluster.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/collection.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/database.rb +6 -17
- data/modules/mu/{clouds → providers}/cloudformation/dnszone.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/firewall_rule.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/loadbalancer.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/log.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/server.rb +7 -7
- data/modules/mu/{clouds → providers}/cloudformation/server_pool.rb +5 -5
- data/modules/mu/{clouds → providers}/cloudformation/vpc.rb +3 -3
- data/modules/mu/{clouds → providers}/docker.rb +0 -0
- data/modules/mu/{clouds → providers}/google.rb +29 -6
- data/modules/mu/{clouds → providers}/google/bucket.rb +5 -5
- data/modules/mu/{clouds → providers}/google/container_cluster.rb +59 -37
- data/modules/mu/{clouds → providers}/google/database.rb +5 -12
- data/modules/mu/{clouds → providers}/google/firewall_rule.rb +5 -5
- data/modules/mu/{clouds → providers}/google/folder.rb +5 -9
- data/modules/mu/{clouds → providers}/google/function.rb +14 -8
- data/modules/mu/{clouds → providers}/google/group.rb +9 -17
- data/modules/mu/{clouds → providers}/google/habitat.rb +4 -8
- data/modules/mu/{clouds → providers}/google/loadbalancer.rb +5 -5
- data/modules/mu/{clouds → providers}/google/role.rb +50 -31
- data/modules/mu/{clouds → providers}/google/server.rb +142 -55
- data/modules/mu/{clouds → providers}/google/server_pool.rb +14 -14
- data/modules/mu/{clouds → providers}/google/user.rb +34 -24
- data/modules/mu/{clouds → providers}/google/userdata/README.md +0 -0
- data/modules/mu/{clouds → providers}/google/userdata/linux.erb +0 -0
- data/modules/mu/{clouds → providers}/google/userdata/windows.erb +0 -0
- data/modules/mu/{clouds → providers}/google/vpc.rb +46 -15
- data/modules/tests/aws-jobs-functions.yaml +46 -0
- data/modules/tests/centos6.yaml +15 -0
- data/modules/tests/centos7.yaml +15 -0
- data/modules/tests/centos8.yaml +12 -0
- data/modules/tests/ecs.yaml +23 -0
- data/modules/tests/eks.yaml +1 -1
- data/modules/tests/functions/node-function/lambda_function.js +10 -0
- data/modules/tests/functions/python-function/lambda_function.py +12 -0
- data/modules/tests/includes-and-params.yaml +2 -1
- data/modules/tests/microservice_app.yaml +288 -0
- data/modules/tests/rds.yaml +108 -0
- data/modules/tests/regrooms/rds.yaml +123 -0
- data/modules/tests/server-with-scrub-muisms.yaml +2 -1
- data/modules/tests/super_complex_bok.yml +2 -2
- data/modules/tests/super_simple_bok.yml +3 -5
- data/modules/tests/win2k12.yaml +25 -0
- data/modules/tests/win2k16.yaml +25 -0
- data/modules/tests/win2k19.yaml +25 -0
- data/requirements.txt +1 -0
- data/spec/mu/clouds/azure_spec.rb +2 -2
- metadata +169 -93
- data/extras/image-generators/AWS/windows.yaml +0 -18
- data/modules/mu/clouds/aws/database.rb +0 -1974
- data/modules/mu/clouds/aws/endpoint.rb +0 -596
- data/modules/tests/needwork/win2k12.yaml +0 -13
|
@@ -89,7 +89,7 @@ module MU
|
|
|
89
89
|
template_variables: {
|
|
90
90
|
"deployKey" => Base64.urlsafe_encode64(@deploy.public_key),
|
|
91
91
|
"deploySSHKey" => @deploy.ssh_public_key,
|
|
92
|
-
"muID" =>
|
|
92
|
+
"muID" => @deploy.deploy_id,
|
|
93
93
|
"muUser" => MU.mu_user,
|
|
94
94
|
"publicIP" => MU.mu_public_ip,
|
|
95
95
|
"mommaCatPort" => MU.mommaCatPort,
|
|
@@ -145,7 +145,7 @@ module MU
|
|
|
145
145
|
raise MuError, "My second argument should be a hash of variables to pass into ERB templates"
|
|
146
146
|
end
|
|
147
147
|
$mu = OpenStruct.new(template_variables)
|
|
148
|
-
userdata_dir = File.expand_path(MU.myRoot+"/modules/mu/
|
|
148
|
+
userdata_dir = File.expand_path(MU.myRoot+"/modules/mu/providers/aws/userdata")
|
|
149
149
|
platform = "linux" if %w{centos centos6 centos7 ubuntu ubuntu14 rhel rhel7 rhel71 amazon}.include? platform
|
|
150
150
|
platform = "windows" if %w{win2k12r2 win2k12 win2k8 win2k8r2 win2k16}.include? platform
|
|
151
151
|
erbfile = "#{userdata_dir}/#{platform}.erb"
|
|
@@ -240,8 +240,14 @@ module MU
|
|
|
240
240
|
end
|
|
241
241
|
MU::MommaCat.unlock(instance.instance_id+"-create")
|
|
242
242
|
else
|
|
243
|
-
MU::Cloud::AWS.createStandardTags(
|
|
244
|
-
|
|
243
|
+
MU::Cloud::AWS.createStandardTags(
|
|
244
|
+
instance.instance_id,
|
|
245
|
+
region: @config['region'],
|
|
246
|
+
credentials: @config['credentials'],
|
|
247
|
+
optional: @config['optional_tags'],
|
|
248
|
+
nametag: @mu_name,
|
|
249
|
+
othertags: @config['tags']
|
|
250
|
+
)
|
|
245
251
|
end
|
|
246
252
|
done = true
|
|
247
253
|
rescue StandardError => e
|
|
@@ -262,14 +268,11 @@ module MU
|
|
|
262
268
|
return @config
|
|
263
269
|
end
|
|
264
270
|
|
|
265
|
-
|
|
266
|
-
|
|
267
271
|
# Create an Amazon EC2 instance.
|
|
268
272
|
def createEc2Instance
|
|
269
|
-
node = @config['mu_name']
|
|
270
273
|
|
|
271
274
|
instance_descriptor = {
|
|
272
|
-
:image_id => @config["
|
|
275
|
+
:image_id => @config["image_id"],
|
|
273
276
|
:key_name => @deploy.ssh_key_name,
|
|
274
277
|
:instance_type => @config["size"],
|
|
275
278
|
:disable_api_termination => true,
|
|
@@ -277,63 +280,26 @@ module MU
|
|
|
277
280
|
:max_count => 1
|
|
278
281
|
}
|
|
279
282
|
|
|
280
|
-
|
|
281
|
-
if @config['generate_iam_role']
|
|
282
|
-
role = @deploy.findLitterMate(name: @config['name'], type: "roles")
|
|
283
|
-
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|
|
|
284
|
-
'arn:'+(MU::Cloud::AWS.isGovCloud?(@config['region']) ? "aws-us-gov" : "aws")+':s3:::'+MU::Cloud::AWS.adminBucketName(@credentials)+'/'+file
|
|
285
|
-
}
|
|
286
|
-
MU.log "Adding S3 read permissions to #{@mu_name}'s IAM profile", MU::NOTICE, details: s3_objs
|
|
287
|
-
role.cloudobj.injectPolicyTargets("MuSecrets", s3_objs)
|
|
288
|
-
|
|
289
|
-
@config['iam_role'] = role.mu_name
|
|
290
|
-
arn = role.cloudobj.createInstanceProfile
|
|
291
|
-
# @cfm_role_name, @cfm_prof_name
|
|
292
|
-
|
|
293
|
-
elsif @config['iam_role'].nil?
|
|
294
|
-
raise MuError, "#{@mu_name} has generate_iam_role set to false, but no iam_role assigned."
|
|
295
|
-
end
|
|
296
|
-
if !@config["iam_role"].nil?
|
|
297
|
-
if arn
|
|
298
|
-
instance_descriptor[:iam_instance_profile] = {arn: arn}
|
|
299
|
-
else
|
|
300
|
-
instance_descriptor[:iam_instance_profile] = {name: @config["iam_role"]}
|
|
301
|
-
end
|
|
302
|
-
end
|
|
303
|
-
|
|
304
|
-
security_groups = []
|
|
305
|
-
if @dependencies.has_key?("firewall_rule")
|
|
306
|
-
@dependencies['firewall_rule'].values.each { |sg|
|
|
307
|
-
security_groups << sg.cloud_id
|
|
308
|
-
}
|
|
309
|
-
end
|
|
283
|
+
instance_descriptor[:iam_instance_profile] = getIAMProfile
|
|
310
284
|
|
|
285
|
+
security_groups = myFirewallRules.map { |fw| fw.cloud_id }
|
|
311
286
|
if security_groups.size > 0
|
|
312
287
|
instance_descriptor[:security_group_ids] = security_groups
|
|
313
288
|
else
|
|
314
289
|
raise MuError, "Didn't get any security groups assigned to be in #{@mu_name}, that shouldn't happen"
|
|
315
290
|
end
|
|
316
291
|
|
|
317
|
-
if
|
|
292
|
+
if @config['private_ip']
|
|
318
293
|
instance_descriptor[:private_ip_address] = @config['private_ip']
|
|
319
294
|
end
|
|
320
295
|
|
|
321
296
|
if !@vpc.nil? and @config.has_key?("vpc")
|
|
322
|
-
|
|
323
|
-
subnet_conf = @config['vpc']['subnets'].first if @config['vpc'].has_key?("subnets") and !@config['vpc']['subnets'].empty?
|
|
324
|
-
tag_key, tag_value = subnet_conf['tag'].split(/=/, 2) if !subnet_conf['tag'].nil?
|
|
325
|
-
|
|
326
|
-
subnet = @vpc.getSubnet(
|
|
327
|
-
cloud_id: subnet_conf['subnet_id'],
|
|
328
|
-
name: subnet_conf['subnet_name'],
|
|
329
|
-
tag_key: tag_key,
|
|
330
|
-
tag_value: tag_value
|
|
331
|
-
)
|
|
297
|
+
subnet = mySubnets.sample
|
|
332
298
|
if subnet.nil?
|
|
333
|
-
raise MuError, "Got null subnet id out of #{
|
|
299
|
+
raise MuError, "Got null subnet id out of #{@config['vpc']}"
|
|
334
300
|
end
|
|
335
|
-
MU.log "Deploying #{
|
|
336
|
-
|
|
301
|
+
MU.log "Deploying #{@mu_name} into VPC #{@vpc.cloud_id} Subnet #{subnet.cloud_id}"
|
|
302
|
+
allowBastionAccess
|
|
337
303
|
instance_descriptor[:subnet_id] = subnet.cloud_id
|
|
338
304
|
end
|
|
339
305
|
|
|
@@ -341,37 +307,10 @@ module MU
|
|
|
341
307
|
instance_descriptor[:user_data] = Base64.encode64(@userdata)
|
|
342
308
|
end
|
|
343
309
|
|
|
344
|
-
MU::Cloud::AWS::Server.waitForAMI(@config["
|
|
310
|
+
MU::Cloud::AWS::Server.waitForAMI(@config["image_id"], region: @config['region'], credentials: @config['credentials'])
|
|
345
311
|
|
|
346
|
-
|
|
347
|
-
image = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_images(image_ids: [@config["ami_id"]]).images.first
|
|
348
|
-
ext_disks = {}
|
|
349
|
-
if !image.block_device_mappings.nil?
|
|
350
|
-
image.block_device_mappings.each { |disk|
|
|
351
|
-
if !disk.device_name.nil? and !disk.device_name.empty? and !disk.ebs.nil? and !disk.ebs.empty?
|
|
352
|
-
ext_disks[disk.device_name] = MU.structToHash(disk.ebs)
|
|
353
|
-
end
|
|
354
|
-
}
|
|
355
|
-
end
|
|
356
|
-
|
|
357
|
-
configured_storage = Array.new
|
|
358
|
-
if @config["storage"]
|
|
359
|
-
@config["storage"].each { |vol|
|
|
360
|
-
# Drop the "encrypted" flag if a snapshot for this device exists
|
|
361
|
-
# in the AMI, even if they both agree about the value of said
|
|
362
|
-
# flag. Apparently that's a thing now.
|
|
363
|
-
if ext_disks.has_key?(vol["device"])
|
|
364
|
-
if ext_disks[vol["device"]].has_key?(:snapshot_id)
|
|
365
|
-
vol.delete("encrypted")
|
|
366
|
-
end
|
|
367
|
-
end
|
|
368
|
-
mapping, _cfm_mapping = MU::Cloud::AWS::Server.convertBlockDeviceMapping(vol)
|
|
369
|
-
configured_storage << mapping
|
|
370
|
-
}
|
|
371
|
-
end
|
|
312
|
+
instance_descriptor[:block_device_mappings] = MU::Cloud::AWS::Server.configureBlockDevices(image_id: @config["image_id"], storage: @config['storage'], region: @config['region'], credentials: @credentials)
|
|
372
313
|
|
|
373
|
-
instance_descriptor[:block_device_mappings] = configured_storage
|
|
374
|
-
instance_descriptor[:block_device_mappings].concat(@ephemeral_mappings)
|
|
375
314
|
instance_descriptor[:monitoring] = {enabled: @config['monitoring']}
|
|
376
315
|
|
|
377
316
|
if @tags and @tags.size > 0
|
|
@@ -383,37 +322,24 @@ module MU
|
|
|
383
322
|
}]
|
|
384
323
|
end
|
|
385
324
|
|
|
386
|
-
MU.log "Creating EC2 instance #{
|
|
387
|
-
MU.log "Instance details for #{node}: #{instance_descriptor}", MU::DEBUG
|
|
388
|
-
# if instance_descriptor[:block_device_mappings].empty?
|
|
389
|
-
# instance_descriptor.delete(:block_device_mappings)
|
|
390
|
-
# end
|
|
325
|
+
MU.log "Creating EC2 instance #{@mu_name}", details: instance_descriptor
|
|
391
326
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
327
|
+
instance = resp = nil
|
|
328
|
+
loop_if = Proc.new {
|
|
329
|
+
instance = resp.instances.first if resp and resp.instances
|
|
330
|
+
resp.nil? or resp.instances.nil? or instance.nil?
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
begin
|
|
334
|
+
MU.retrier([Aws::EC2::Errors::InvalidGroupNotFound, Aws::EC2::Errors::InvalidSubnetIDNotFound, Aws::EC2::Errors::InvalidParameterValue], loop_if: loop_if, loop_msg: "Waiting for run_instances to return #{@mu_name}") {
|
|
335
|
+
resp = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).run_instances(instance_descriptor)
|
|
336
|
+
}
|
|
400
337
|
rescue Aws::EC2::Errors::InvalidRequest => e
|
|
401
338
|
MU.log e.message, MU::ERR, details: instance_descriptor
|
|
402
339
|
raise e
|
|
403
|
-
rescue Aws::EC2::Errors::InvalidGroupNotFound, Aws::EC2::Errors::InvalidSubnetIDNotFound, Aws::EC2::Errors::InvalidParameterValue => e
|
|
404
|
-
if retries < 10
|
|
405
|
-
if retries > 7
|
|
406
|
-
MU.log "Seeing #{e.inspect} while trying to launch #{node}, retrying a few more times...", MU::WARN, details: instance_descriptor
|
|
407
|
-
end
|
|
408
|
-
sleep 10
|
|
409
|
-
retries = retries + 1
|
|
410
|
-
retry
|
|
411
|
-
else
|
|
412
|
-
raise MuError, e.inspect
|
|
413
|
-
end
|
|
414
340
|
end
|
|
415
341
|
|
|
416
|
-
MU.log "#{
|
|
342
|
+
MU.log "#{@mu_name} (#{instance.instance_id}) coming online"
|
|
417
343
|
|
|
418
344
|
instance
|
|
419
345
|
end
|
|
@@ -473,13 +399,13 @@ module MU
|
|
|
473
399
|
# Figure out what's needed to SSH into this server.
|
|
474
400
|
# @return [Array<String>]: nat_ssh_key, nat_ssh_user, nat_ssh_host, canonical_ip, ssh_user, ssh_key_name, alternate_names
|
|
475
401
|
def getSSHConfig
|
|
476
|
-
|
|
402
|
+
cloud_desc(use_cache: false) # make sure we're current
|
|
477
403
|
# XXX add some awesome alternate names from metadata and make sure they end
|
|
478
404
|
# up in MU::MommaCat's ssh config wangling
|
|
479
405
|
return nil if @config.nil? or @deploy.nil?
|
|
480
406
|
|
|
481
407
|
nat_ssh_key = nat_ssh_user = nat_ssh_host = nil
|
|
482
|
-
if !@config["vpc"].nil? and !MU::Cloud
|
|
408
|
+
if !@config["vpc"].nil? and !MU::Cloud.resourceClass("AWS", "VPC").haveRouteToInstance?(cloud_desc, region: @config['region'], credentials: @config['credentials'])
|
|
483
409
|
if !@nat.nil?
|
|
484
410
|
if @nat.is_a?(Struct) && @nat.nat_gateway_id && @nat.nat_gateway_id.start_with?("nat-")
|
|
485
411
|
raise MuError, "Configured to use NAT Gateway, but I have no route to instance. Either use Bastion, or configure VPC peering"
|
|
@@ -517,445 +443,81 @@ module MU
|
|
|
517
443
|
# Apply tags, bootstrap our configuration management, and other
|
|
518
444
|
# administravia for a new instance.
|
|
519
445
|
def postBoot(instance_id = nil)
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
MU::Cloud::AWS.createTag(instance.instance_id, "Name", node, region: @config['region'], credentials: @config['credentials'])
|
|
532
|
-
|
|
533
|
-
if @config['optional_tags']
|
|
534
|
-
MU::MommaCat.listOptionalTags.each { |key, value|
|
|
535
|
-
MU::Cloud::AWS.createTag(instance.instance_id, key, value, region: @config['region'], credentials: @config['credentials'])
|
|
536
|
-
}
|
|
537
|
-
end
|
|
446
|
+
@cloud_id ||= instance_id
|
|
447
|
+
_node, _config, deploydata = describe(cloud_id: @cloud_id)
|
|
448
|
+
|
|
449
|
+
raise MuError, "Couldn't find instance #{@mu_name} (#{@cloud_id})" if !cloud_desc
|
|
450
|
+
return false if !MU::MommaCat.lock(@cloud_id+"-orchestrate", true)
|
|
451
|
+
return false if !MU::MommaCat.lock(@cloud_id+"-groom", true)
|
|
452
|
+
finish = Proc.new { |status|
|
|
453
|
+
MU::MommaCat.unlock(@cloud_id+"-orchestrate")
|
|
454
|
+
MU::MommaCat.unlock(@cloud_id+"-groom")
|
|
455
|
+
return status
|
|
456
|
+
}
|
|
538
457
|
|
|
539
|
-
|
|
540
|
-
@
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
458
|
+
MU::Cloud::AWS.createStandardTags(
|
|
459
|
+
@cloud_id,
|
|
460
|
+
region: @config['region'],
|
|
461
|
+
credentials: @config['credentials'],
|
|
462
|
+
optional: @config['optional_tags'],
|
|
463
|
+
nametag: @mu_name,
|
|
464
|
+
othertags: @config['tags']
|
|
465
|
+
)
|
|
545
466
|
|
|
546
467
|
# Make double sure we don't lose a cached mu_windows_name value.
|
|
547
|
-
if windows? or !@config['active_directory'].nil?
|
|
548
|
-
|
|
549
|
-
@mu_windows_name = deploydata['mu_windows_name']
|
|
550
|
-
end
|
|
468
|
+
if (windows? or !@config['active_directory'].nil?)
|
|
469
|
+
@mu_windows_name ||= deploydata['mu_windows_name']
|
|
551
470
|
end
|
|
552
471
|
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
raise MuError, "#{@cloud_id} appears to have been terminated mid-bootstrap!"
|
|
560
|
-
end
|
|
561
|
-
if retries % 3 == 0
|
|
562
|
-
MU.log "Waiting for EC2 instance #{node} (#{@cloud_id}) to be ready...", MU::NOTICE
|
|
563
|
-
end
|
|
564
|
-
sleep 40
|
|
565
|
-
# Get a fresh AWS descriptor
|
|
566
|
-
instance = MU::Cloud::Server.find(cloud_id: @cloud_id, region: @config['region'], credentials: @config['credentials']).values.first
|
|
567
|
-
if instance and instance.state.name == "terminated"
|
|
568
|
-
raise MuError, "EC2 instance #{node} (#{@cloud_id}) terminating during bootstrap!"
|
|
569
|
-
end
|
|
472
|
+
loop_if = Proc.new {
|
|
473
|
+
!cloud_desc(use_cache: false) or cloud_desc.state.name != "running"
|
|
474
|
+
}
|
|
475
|
+
MU.retrier([Aws::EC2::Errors::ServiceError], max: 30, wait: 40, loop_if: loop_if) { |retries, _wait|
|
|
476
|
+
if cloud_desc and cloud_desc.state.name == "terminated"
|
|
477
|
+
raise MuError, "#{@cloud_id} appears to have been terminated mid-bootstrap!"
|
|
570
478
|
end
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
MU.log "Got #{e.inspect} during initial instance creation of #{@cloud_id}, retrying...", MU::NOTICE, details: instance
|
|
574
|
-
retries = retries + 1
|
|
575
|
-
retry
|
|
576
|
-
else
|
|
577
|
-
raise MuError, "Too many retries creating #{node} (#{e.inspect})"
|
|
479
|
+
if retries % 3 == 0
|
|
480
|
+
MU.log "Waiting for EC2 instance #{@mu_name} (#{@cloud_id}) to be ready...", MU::NOTICE
|
|
578
481
|
end
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
punchAdminNAT
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
# If we came up via AutoScale, the Alarm module won't have had our
|
|
585
|
-
# instance ID to associate us with itself. So invoke that here.
|
|
586
|
-
# XXX might be possible to do this with regular alarm resources and
|
|
587
|
-
# dependencies now
|
|
588
|
-
if !@config['basis'].nil? and @config["alarms"] and !@config["alarms"].empty?
|
|
589
|
-
@config["alarms"].each { |alarm|
|
|
590
|
-
alarm_obj = MU::MommaCat.findStray(
|
|
591
|
-
"AWS",
|
|
592
|
-
"alarms",
|
|
593
|
-
region: @config["region"],
|
|
594
|
-
deploy_id: @deploy.deploy_id,
|
|
595
|
-
name: alarm['name']
|
|
596
|
-
).first
|
|
597
|
-
alarm["dimensions"] = [{:name => "InstanceId", :value => @cloud_id}]
|
|
598
|
-
|
|
599
|
-
if alarm["enable_notifications"]
|
|
600
|
-
topic_arn = MU::Cloud::AWS::Notification.createTopic(alarm["notification_group"], region: @config["region"], credentials: @config['credentials'])
|
|
601
|
-
MU::Cloud::AWS::Notification.subscribe(arn: topic_arn, protocol: alarm["notification_type"], endpoint: alarm["notification_endpoint"], region: @config["region"], credentials: @config["credentials"])
|
|
602
|
-
alarm["alarm_actions"] = [topic_arn]
|
|
603
|
-
alarm["ok_actions"] = [topic_arn]
|
|
604
|
-
end
|
|
482
|
+
}
|
|
605
483
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
MU::Cloud::AWS::Alarm.setAlarm(
|
|
609
|
-
name: alarm_name,
|
|
610
|
-
ok_actions: alarm["ok_actions"],
|
|
611
|
-
alarm_actions: alarm["alarm_actions"],
|
|
612
|
-
insufficient_data_actions: alarm["no_data_actions"],
|
|
613
|
-
metric_name: alarm["metric_name"],
|
|
614
|
-
namespace: alarm["namespace"],
|
|
615
|
-
statistic: alarm["statistic"],
|
|
616
|
-
dimensions: alarm["dimensions"],
|
|
617
|
-
period: alarm["period"],
|
|
618
|
-
unit: alarm["unit"],
|
|
619
|
-
evaluation_periods: alarm["evaluation_periods"],
|
|
620
|
-
threshold: alarm["threshold"],
|
|
621
|
-
comparison_operator: alarm["comparison_operator"],
|
|
622
|
-
region: @config["region"],
|
|
623
|
-
credentials: @config['credentials']
|
|
624
|
-
)
|
|
625
|
-
}
|
|
626
|
-
end
|
|
484
|
+
allowBastionAccess
|
|
627
485
|
|
|
628
|
-
|
|
629
|
-
# Make sure that doesn't happen. Happens with server pools only
|
|
630
|
-
if @config['dns_records'] && !@config['dns_records'].empty?
|
|
631
|
-
@config['dns_records'].each { |dnsrec|
|
|
632
|
-
if dnsrec.has_key?("name")
|
|
633
|
-
if dnsrec['name'].start_with?(MU.deploy_id.downcase) && !dnsrec['name'].start_with?(node.downcase)
|
|
634
|
-
MU.log "DNS records for #{node} seem to be wrong, deleting from current config", MU::WARN, details: dnsrec
|
|
635
|
-
dnsrec.delete('name')
|
|
636
|
-
dnsrec.delete('target')
|
|
637
|
-
end
|
|
638
|
-
end
|
|
639
|
-
}
|
|
640
|
-
end
|
|
486
|
+
setAlarms
|
|
641
487
|
|
|
642
488
|
# Unless we're planning on associating a different IP later, set up a
|
|
643
489
|
# DNS entry for this thing and let it sync in the background. We'll come
|
|
644
490
|
# back to it later.
|
|
645
|
-
if @config['static_ip'].nil?
|
|
491
|
+
if @config['static_ip'].nil? and !@named
|
|
646
492
|
MU::MommaCat.nameKitten(self)
|
|
647
493
|
@named = true
|
|
648
494
|
end
|
|
649
495
|
|
|
650
496
|
if !@config['src_dst_check'] and !@config["vpc"].nil?
|
|
651
|
-
MU.log "Disabling source_dest_check #{
|
|
497
|
+
MU.log "Disabling source_dest_check #{@mu_name} (making it NAT-worthy)"
|
|
652
498
|
MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).modify_instance_attribute(
|
|
653
|
-
|
|
654
|
-
|
|
499
|
+
instance_id: @cloud_id,
|
|
500
|
+
source_dest_check: { value: false }
|
|
655
501
|
)
|
|
656
502
|
end
|
|
657
503
|
|
|
658
504
|
# Set console termination protection. Autoscale nodes won't set this
|
|
659
505
|
# by default.
|
|
660
506
|
MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).modify_instance_attribute(
|
|
661
|
-
|
|
662
|
-
|
|
507
|
+
instance_id: @cloud_id,
|
|
508
|
+
disable_api_termination: { value: true}
|
|
663
509
|
)
|
|
664
510
|
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
resp = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_addresses(public_ips: [instance.public_ip_address])
|
|
669
|
-
if resp.addresses.size > 0 and resp.addresses.first.instance_id == @cloud_id
|
|
670
|
-
has_elastic_ip = true
|
|
671
|
-
end
|
|
672
|
-
rescue Aws::EC2::Errors::InvalidAddressNotFound
|
|
673
|
-
# XXX this is ok to ignore, it means the public IP isn't Elastic
|
|
674
|
-
end
|
|
675
|
-
end
|
|
676
|
-
|
|
677
|
-
win_admin_password = nil
|
|
678
|
-
ec2config_password = nil
|
|
679
|
-
sshd_password = nil
|
|
680
|
-
if windows?
|
|
681
|
-
if @config['use_cloud_provider_windows_password']
|
|
682
|
-
win_admin_password = getWindowsAdminPassword
|
|
683
|
-
elsif @config['windows_auth_vault'] && !@config['windows_auth_vault'].empty?
|
|
684
|
-
if @config["windows_auth_vault"].has_key?("password_field")
|
|
685
|
-
win_admin_password = @groomer.getSecret(
|
|
686
|
-
vault: @config['windows_auth_vault']['vault'],
|
|
687
|
-
item: @config['windows_auth_vault']['item'],
|
|
688
|
-
field: @config["windows_auth_vault"]["password_field"]
|
|
689
|
-
)
|
|
690
|
-
else
|
|
691
|
-
win_admin_password = getWindowsAdminPassword
|
|
692
|
-
end
|
|
693
|
-
|
|
694
|
-
if @config["windows_auth_vault"].has_key?("ec2config_password_field")
|
|
695
|
-
ec2config_password = @groomer.getSecret(
|
|
696
|
-
vault: @config['windows_auth_vault']['vault'],
|
|
697
|
-
item: @config['windows_auth_vault']['item'],
|
|
698
|
-
field: @config["windows_auth_vault"]["ec2config_password_field"]
|
|
699
|
-
)
|
|
700
|
-
end
|
|
701
|
-
|
|
702
|
-
if @config["windows_auth_vault"].has_key?("sshd_password_field")
|
|
703
|
-
sshd_password = @groomer.getSecret(
|
|
704
|
-
vault: @config['windows_auth_vault']['vault'],
|
|
705
|
-
item: @config['windows_auth_vault']['item'],
|
|
706
|
-
field: @config["windows_auth_vault"]["sshd_password_field"]
|
|
707
|
-
)
|
|
708
|
-
end
|
|
709
|
-
end
|
|
710
|
-
|
|
711
|
-
win_admin_password = MU.generateWindowsPassword if win_admin_password.nil?
|
|
712
|
-
ec2config_password = MU.generateWindowsPassword if ec2config_password.nil?
|
|
713
|
-
sshd_password = MU.generateWindowsPassword if sshd_password.nil?
|
|
714
|
-
|
|
715
|
-
# We're creating the vault here so when we run
|
|
716
|
-
# MU::Cloud::Server.initialSSHTasks and we need to set the Windows
|
|
717
|
-
# Admin password we can grab it from said vault.
|
|
718
|
-
creds = {
|
|
719
|
-
"username" => @config['windows_admin_username'],
|
|
720
|
-
"password" => win_admin_password,
|
|
721
|
-
"ec2config_username" => "ec2config",
|
|
722
|
-
"ec2config_password" => ec2config_password,
|
|
723
|
-
"sshd_username" => "sshd_service",
|
|
724
|
-
"sshd_password" => sshd_password
|
|
725
|
-
}
|
|
726
|
-
@groomer.saveSecret(vault: @mu_name, item: "windows_credentials", data: creds, permissions: "name:#{@mu_name}")
|
|
727
|
-
end
|
|
728
|
-
|
|
729
|
-
subnet = nil
|
|
730
|
-
if !@vpc.nil? and @config.has_key?("vpc") and !instance.subnet_id.nil?
|
|
731
|
-
subnet = @vpc.getSubnet(
|
|
732
|
-
cloud_id: instance.subnet_id
|
|
733
|
-
)
|
|
734
|
-
if subnet.nil?
|
|
735
|
-
raise MuError, "Got null subnet id out of #{@config['vpc']} when asking for #{instance.subnet_id}"
|
|
736
|
-
end
|
|
737
|
-
end
|
|
738
|
-
|
|
739
|
-
if !subnet.nil?
|
|
740
|
-
if !subnet.private? or (!@config['static_ip'].nil? and !@config['static_ip']['assign_ip'].nil?)
|
|
741
|
-
if !@config['static_ip'].nil?
|
|
742
|
-
if !@config['static_ip']['ip'].nil?
|
|
743
|
-
MU::Cloud::AWS::Server.associateElasticIp(instance.instance_id, classic: false, ip: @config['static_ip']['ip'])
|
|
744
|
-
elsif !has_elastic_ip
|
|
745
|
-
MU::Cloud::AWS::Server.associateElasticIp(instance.instance_id)
|
|
746
|
-
end
|
|
747
|
-
end
|
|
748
|
-
end
|
|
749
|
-
|
|
750
|
-
_nat_ssh_key, _nat_ssh_user, nat_ssh_host, _canonical_ip, _ssh_user, _ssh_key_name = getSSHConfig
|
|
751
|
-
if subnet.private? and !nat_ssh_host and !MU::Cloud::AWS::VPC.haveRouteToInstance?(cloud_desc, region: @config['region'], credentials: @config['credentials'])
|
|
752
|
-
raise MuError, "#{node} is in a private subnet (#{subnet}), but has no bastion host configured, and I have no other route to it"
|
|
753
|
-
end
|
|
754
|
-
|
|
755
|
-
# If we've asked for additional subnets (and this @config is not a
|
|
756
|
-
# member of a Server Pool, which has different semantics), create
|
|
757
|
-
# extra interfaces to accomodate.
|
|
758
|
-
if !@config['vpc']['subnets'].nil? and @config['basis'].nil?
|
|
759
|
-
device_index = 1
|
|
760
|
-
@vpc.subnets.each { |s|
|
|
761
|
-
subnet_id = s.cloud_id
|
|
762
|
-
MU.log "Adding network interface on subnet #{subnet_id} for #{node}"
|
|
763
|
-
iface = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).create_network_interface(subnet_id: subnet_id).network_interface
|
|
764
|
-
MU::Cloud::AWS.createStandardTags(iface.network_interface_id, region: @config['region'], credentials: @config['credentials'])
|
|
765
|
-
MU::Cloud::AWS.createTag(iface.network_interface_id, "Name", node+"-ETH"+device_index.to_s, region: @config['region'], credentials: @config['credentials'])
|
|
766
|
-
|
|
767
|
-
if @config['optional_tags']
|
|
768
|
-
MU::MommaCat.listOptionalTags.each { |key, value|
|
|
769
|
-
MU::Cloud::AWS.createTag(iface.network_interface_id, key, value, region: @config['region'], credentials: @config['credentials'])
|
|
770
|
-
}
|
|
771
|
-
end
|
|
772
|
-
|
|
773
|
-
if !@config['tags'].nil?
|
|
774
|
-
@config['tags'].each { |tag|
|
|
775
|
-
MU::Cloud::AWS.createTag(iface.network_interface_id, tag['key'], tag['value'], region: @config['region'], credentials: @config['credentials'])
|
|
776
|
-
}
|
|
777
|
-
end
|
|
778
|
-
|
|
779
|
-
MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).attach_network_interface(
|
|
780
|
-
network_interface_id: iface.network_interface_id,
|
|
781
|
-
instance_id: instance.instance_id,
|
|
782
|
-
device_index: device_index
|
|
783
|
-
)
|
|
784
|
-
device_index = device_index + 1
|
|
785
|
-
}
|
|
786
|
-
end
|
|
787
|
-
elsif !@config['static_ip'].nil?
|
|
788
|
-
if !@config['static_ip']['ip'].nil?
|
|
789
|
-
MU::Cloud::AWS::Server.associateElasticIp(instance.instance_id, classic: true, ip: @config['static_ip']['ip'])
|
|
790
|
-
elsif !has_elastic_ip
|
|
791
|
-
MU::Cloud::AWS::Server.associateElasticIp(instance.instance_id, classic: true)
|
|
792
|
-
end
|
|
793
|
-
end
|
|
794
|
-
|
|
511
|
+
tagVolumes
|
|
512
|
+
configureNetworking
|
|
513
|
+
saveCredentials
|
|
795
514
|
|
|
796
515
|
if !@config['image_then_destroy']
|
|
797
516
|
notify
|
|
798
517
|
end
|
|
799
518
|
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
@config["private_dns_name"] = instance.private_dns_name
|
|
803
|
-
@config["public_dns_name"] = instance.public_dns_name
|
|
804
|
-
@config["private_ip_address"] = instance.private_ip_address
|
|
805
|
-
@config["public_ip_address"] = instance.public_ip_address
|
|
806
|
-
|
|
807
|
-
# Root disk on standard CentOS AMI
|
|
808
|
-
# tagVolumes(instance.instance_id, "/dev/sda", "Name", "ROOT-"+MU.deploy_id+"-"+@config["name"].upcase)
|
|
809
|
-
# Root disk on standard Ubuntu AMI
|
|
810
|
-
# tagVolumes(instance.instance_id, "/dev/sda1", "Name", "ROOT-"+MU.deploy_id+"-"+@config["name"].upcase)
|
|
811
|
-
|
|
812
|
-
# Generic deploy ID tag
|
|
813
|
-
# tagVolumes(instance.instance_id)
|
|
814
|
-
|
|
815
|
-
# Tag volumes with all our standard tags.
|
|
816
|
-
# Maybe replace tagVolumes with this? There is one more place tagVolumes is called from
|
|
817
|
-
volumes = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_volumes(filters: [name: "attachment.instance-id", values: [instance.instance_id]])
|
|
818
|
-
volumes.each { |vol|
|
|
819
|
-
vol.volumes.each { |volume|
|
|
820
|
-
volume.attachments.each { |attachment|
|
|
821
|
-
MU::MommaCat.listStandardTags.each_pair { |key, value|
|
|
822
|
-
MU::Cloud::AWS.createTag(attachment.volume_id, key, value, region: @config['region'], credentials: @config['credentials'])
|
|
823
|
-
|
|
824
|
-
if attachment.device == "/dev/sda" or attachment.device == "/dev/sda1"
|
|
825
|
-
MU::Cloud::AWS.createTag(attachment.volume_id, "Name", "ROOT-#{MU.deploy_id}-#{@config["name"].upcase}", region: @config['region'], credentials: @config['credentials'])
|
|
826
|
-
else
|
|
827
|
-
MU::Cloud::AWS.createTag(attachment.volume_id, "Name", "#{MU.deploy_id}-#{@config["name"].upcase}-#{attachment.device.upcase}", region: @config['region'], credentials: @config['credentials'])
|
|
828
|
-
end
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
if @config['optional_tags']
|
|
832
|
-
MU::MommaCat.listOptionalTags.each { |key, value|
|
|
833
|
-
MU::Cloud::AWS.createTag(attachment.volume_id, key, value, region: @config['region'], credentials: @config['credentials'])
|
|
834
|
-
}
|
|
835
|
-
end
|
|
836
|
-
|
|
837
|
-
if @config['tags']
|
|
838
|
-
@config['tags'].each { |tag|
|
|
839
|
-
MU::Cloud::AWS.createTag(attachment.volume_id, tag['key'], tag['value'], region: @config['region'], credentials: @config['credentials'])
|
|
840
|
-
}
|
|
841
|
-
end
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
canonical_name = instance.public_dns_name
|
|
847
|
-
canonical_name = instance.private_dns_name if !canonical_name or nat_ssh_host != nil
|
|
848
|
-
@config['canonical_name'] = canonical_name
|
|
849
|
-
|
|
850
|
-
if !@config['add_private_ips'].nil?
|
|
851
|
-
instance.network_interfaces.each { |int|
|
|
852
|
-
if int.private_ip_address == instance.private_ip_address and int.private_ip_addresses.size < (@config['add_private_ips'] + 1)
|
|
853
|
-
MU.log "Adding #{@config['add_private_ips']} extra private IP addresses to #{instance.instance_id}"
|
|
854
|
-
MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).assign_private_ip_addresses(
|
|
855
|
-
network_interface_id: int.network_interface_id,
|
|
856
|
-
secondary_private_ip_address_count: @config['add_private_ips'],
|
|
857
|
-
allow_reassignment: false
|
|
858
|
-
)
|
|
859
|
-
end
|
|
860
|
-
}
|
|
861
|
-
notify
|
|
862
|
-
end
|
|
863
|
-
|
|
864
|
-
begin
|
|
865
|
-
if @config['groom'].nil? or @config['groom']
|
|
866
|
-
if windows?
|
|
867
|
-
# kick off certificate generation early; WinRM will need it
|
|
868
|
-
@deploy.nodeSSLCerts(self)
|
|
869
|
-
if @config.has_key?("basis")
|
|
870
|
-
@deploy.nodeSSLCerts(self, true)
|
|
871
|
-
end
|
|
872
|
-
if !@groomer.haveBootstrapped?
|
|
873
|
-
session = getWinRMSession(50, 60, reboot_on_problems: true)
|
|
874
|
-
initialWinRMTasks(session)
|
|
875
|
-
begin
|
|
876
|
-
session.close
|
|
877
|
-
rescue StandardError
|
|
878
|
-
# this is allowed to fail- we're probably rebooting anyway
|
|
879
|
-
end
|
|
880
|
-
else # for an existing Windows node: WinRM, then SSH if it fails
|
|
881
|
-
begin
|
|
882
|
-
session = getWinRMSession(1, 60)
|
|
883
|
-
rescue StandardError # yeah, yeah
|
|
884
|
-
session = getSSHSession(1, 60)
|
|
885
|
-
# XXX maybe loop at least once if this also fails?
|
|
886
|
-
end
|
|
887
|
-
end
|
|
888
|
-
else
|
|
889
|
-
session = getSSHSession(40, 30)
|
|
890
|
-
initialSSHTasks(session)
|
|
891
|
-
end
|
|
892
|
-
end
|
|
893
|
-
rescue BootstrapTempFail
|
|
894
|
-
sleep 45
|
|
895
|
-
retry
|
|
896
|
-
ensure
|
|
897
|
-
session.close if !session.nil? and !windows?
|
|
898
|
-
end
|
|
899
|
-
|
|
900
|
-
if @config["existing_deploys"] && !@config["existing_deploys"].empty?
|
|
901
|
-
@config["existing_deploys"].each { |ext_deploy|
|
|
902
|
-
if ext_deploy["cloud_id"]
|
|
903
|
-
found = MU::MommaCat.findStray(
|
|
904
|
-
@config['cloud'],
|
|
905
|
-
ext_deploy["cloud_type"],
|
|
906
|
-
cloud_id: ext_deploy["cloud_id"],
|
|
907
|
-
region: @config['region'],
|
|
908
|
-
dummy_ok: false
|
|
909
|
-
).first
|
|
910
|
-
|
|
911
|
-
MU.log "Couldn't find existing resource #{ext_deploy["cloud_id"]}, #{ext_deploy["cloud_type"]}", MU::ERR if found.nil?
|
|
912
|
-
@deploy.notify(ext_deploy["cloud_type"], found.config["name"], found.deploydata, mu_name: found.mu_name, triggering_node: @mu_name)
|
|
913
|
-
elsif ext_deploy["mu_name"] && ext_deploy["deploy_id"]
|
|
914
|
-
MU.log "#{ext_deploy["mu_name"]} / #{ext_deploy["deploy_id"]}"
|
|
915
|
-
found = MU::MommaCat.findStray(
|
|
916
|
-
@config['cloud'],
|
|
917
|
-
ext_deploy["cloud_type"],
|
|
918
|
-
deploy_id: ext_deploy["deploy_id"],
|
|
919
|
-
mu_name: ext_deploy["mu_name"],
|
|
920
|
-
region: @config['region'],
|
|
921
|
-
dummy_ok: false
|
|
922
|
-
).first
|
|
923
|
-
|
|
924
|
-
MU.log "Couldn't find existing resource #{ext_deploy["mu_name"]}/#{ext_deploy["deploy_id"]}, #{ext_deploy["cloud_type"]}", MU::ERR if found.nil?
|
|
925
|
-
@deploy.notify(ext_deploy["cloud_type"], found.config["name"], found.deploydata, mu_name: ext_deploy["mu_name"], triggering_node: @mu_name)
|
|
926
|
-
else
|
|
927
|
-
MU.log "Trying to find existing deploy, but either the cloud_id is not valid or no mu_name and deploy_id where provided", MU::ERR
|
|
928
|
-
end
|
|
929
|
-
}
|
|
930
|
-
end
|
|
931
|
-
|
|
932
|
-
# See if this node already exists in our config management. If it does,
|
|
933
|
-
# we're done.
|
|
934
|
-
if MU.inGem?
|
|
935
|
-
MU.log "Deploying from a gem, not grooming"
|
|
936
|
-
MU::MommaCat.unlock(instance.instance_id+"-orchestrate")
|
|
937
|
-
MU::MommaCat.unlock(instance.instance_id+"-groom")
|
|
938
|
-
|
|
939
|
-
return true
|
|
940
|
-
elsif @groomer.haveBootstrapped?
|
|
941
|
-
MU.log "Node #{node} has already been bootstrapped, skipping groomer setup.", MU::NOTICE
|
|
942
|
-
|
|
943
|
-
if @config['groom'].nil? or @config['groom']
|
|
944
|
-
@groomer.saveDeployData
|
|
945
|
-
end
|
|
946
|
-
|
|
947
|
-
MU::MommaCat.unlock(instance.instance_id+"-orchestrate")
|
|
948
|
-
MU::MommaCat.unlock(instance.instance_id+"-groom")
|
|
949
|
-
return true
|
|
950
|
-
end
|
|
951
|
-
|
|
952
|
-
begin
|
|
953
|
-
@groomer.bootstrap if @config['groom'].nil? or @config['groom']
|
|
954
|
-
rescue MU::Groomer::RunError
|
|
955
|
-
MU::MommaCat.unlock(instance.instance_id+"-groom")
|
|
956
|
-
MU::MommaCat.unlock(instance.instance_id+"-orchestrate")
|
|
957
|
-
return false
|
|
958
|
-
end
|
|
519
|
+
getIAMProfile
|
|
520
|
+
finish.call(false) if !bootstrapGroomer
|
|
959
521
|
|
|
960
522
|
# Make sure we got our name written everywhere applicable
|
|
961
523
|
if !@named
|
|
@@ -963,140 +525,75 @@ module MU
|
|
|
963
525
|
@named = true
|
|
964
526
|
end
|
|
965
527
|
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
return true
|
|
969
|
-
end
|
|
970
|
-
|
|
971
|
-
# postBoot
|
|
528
|
+
finish.call(true)
|
|
529
|
+
end #postboot
|
|
972
530
|
|
|
973
531
|
# Locate an existing instance or instances and return an array containing matching AWS resource descriptors for those that match.
|
|
974
532
|
# @return [Hash<String,OpenStruct>]: The cloud provider's complete descriptions of matching instances
|
|
975
533
|
def self.find(**args)
|
|
976
534
|
ip ||= args[:flags]['ip'] if args[:flags] and args[:flags]['ip']
|
|
977
535
|
|
|
978
|
-
|
|
979
|
-
regions = [args[:region]]
|
|
980
|
-
else
|
|
981
|
-
regions = MU::Cloud::AWS.listRegions
|
|
982
|
-
end
|
|
536
|
+
regions = args[:region].nil? ? MU::Cloud::AWS.listRegions : [args[:region]]
|
|
983
537
|
|
|
984
538
|
found = {}
|
|
985
539
|
search_semaphore = Mutex.new
|
|
986
540
|
search_threads = []
|
|
987
541
|
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
search_threads << Thread.new {
|
|
991
|
-
MU::Cloud::AWS.ec2(region: r, credentials: args[:credentials]).describe_instances(
|
|
992
|
-
filters: [
|
|
993
|
-
{
|
|
994
|
-
name: "instance-state-name",
|
|
995
|
-
values: ["running", "pending", "stopped"]
|
|
996
|
-
}
|
|
997
|
-
]
|
|
998
|
-
).reservations.each { |resp|
|
|
999
|
-
if !resp.nil? and !resp.instances.nil?
|
|
1000
|
-
resp.instances.each { |i|
|
|
1001
|
-
search_semaphore.synchronize {
|
|
1002
|
-
found[i.instance_id] = i
|
|
1003
|
-
}
|
|
1004
|
-
}
|
|
1005
|
-
end
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
}
|
|
542
|
+
base_filter = { name: "instance-state-name", values: ["running", "pending", "stopped"] }
|
|
543
|
+
searches = []
|
|
1009
544
|
|
|
1010
|
-
|
|
1011
|
-
|
|
545
|
+
if args[:cloud_id]
|
|
546
|
+
searches << {
|
|
547
|
+
:instance_ids => [args[:cloud_id]],
|
|
548
|
+
:filters => [base_filter]
|
|
1012
549
|
}
|
|
1013
|
-
|
|
1014
|
-
return found
|
|
1015
550
|
end
|
|
1016
551
|
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
MU.log "Hunting for instance with cloud id '#{args[:cloud_id]}' in #{r}", MU::DEBUG
|
|
1022
|
-
retries = 0
|
|
1023
|
-
begin
|
|
1024
|
-
MU::Cloud::AWS.ec2(region: r, credentials: args[:credentials]).describe_instances(
|
|
1025
|
-
instance_ids: [args[:cloud_id]],
|
|
1026
|
-
filters: [
|
|
1027
|
-
{
|
|
1028
|
-
name: "instance-state-name",
|
|
1029
|
-
values: ["running", "pending", "stopped"]
|
|
1030
|
-
}
|
|
1031
|
-
]
|
|
1032
|
-
).reservations.each { |resp|
|
|
1033
|
-
if !resp.nil? and !resp.instances.nil?
|
|
1034
|
-
resp.instances.each { |i|
|
|
1035
|
-
search_semaphore.synchronize {
|
|
1036
|
-
found[i.instance_id] = i
|
|
1037
|
-
}
|
|
1038
|
-
}
|
|
1039
|
-
end
|
|
1040
|
-
}
|
|
1041
|
-
rescue Aws::EC2::Errors::InvalidInstanceIDNotFound => e
|
|
1042
|
-
retries += 1
|
|
1043
|
-
if retries <= 5
|
|
1044
|
-
sleep 5
|
|
1045
|
-
else
|
|
1046
|
-
raise MuError, "#{e.inspect} in region #{r}"
|
|
1047
|
-
end
|
|
1048
|
-
end
|
|
552
|
+
if ip
|
|
553
|
+
["ip-address", "private-ip-address"].each { |ip_type|
|
|
554
|
+
searches << {
|
|
555
|
+
filters: [base_filter, {name: ip_type, values: [ip]} ],
|
|
1049
556
|
}
|
|
1050
557
|
}
|
|
1051
|
-
done_threads = []
|
|
1052
|
-
begin
|
|
1053
|
-
search_threads.each { |t|
|
|
1054
|
-
joined = t.join(2)
|
|
1055
|
-
done_threads << joined if !joined.nil?
|
|
1056
|
-
}
|
|
1057
|
-
end while found.size < 1 and done_threads.size != search_threads.size
|
|
1058
558
|
end
|
|
1059
559
|
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
response = MU::Cloud::AWS.ec2(region: r, credentials: args[:credentials]).describe_instances(
|
|
1068
|
-
filters: [
|
|
1069
|
-
{name: filter, values: [ip]},
|
|
1070
|
-
{name: "instance-state-name", values: ["running", "pending", "stopped"]}
|
|
1071
|
-
]
|
|
1072
|
-
).reservations.first
|
|
1073
|
-
response.instances.each { |i|
|
|
1074
|
-
found[i.instance_id] = i
|
|
1075
|
-
}
|
|
1076
|
-
}
|
|
560
|
+
if args[:tag_value] and args[:tag_key]
|
|
561
|
+
searches << {
|
|
562
|
+
filters: [
|
|
563
|
+
base_filter,
|
|
564
|
+
{name: ip_type, values: [ip]},
|
|
565
|
+
{name: "tag:#{args[:tag_key]}", values: [args[:tag_value]]},
|
|
566
|
+
]
|
|
1077
567
|
}
|
|
1078
568
|
end
|
|
1079
569
|
|
|
1080
|
-
|
|
570
|
+
if searches.empty?
|
|
571
|
+
searches << { filters: [base_filter] }
|
|
572
|
+
end
|
|
1081
573
|
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
resp.instances.each { |i|
|
|
1094
|
-
found[i.instance_id] = i
|
|
574
|
+
regions.each { |r|
|
|
575
|
+
searches.each { |search|
|
|
576
|
+
search_threads << Thread.new(search) { |params|
|
|
577
|
+
MU.retrier([Aws::EC2::Errors::InvalidInstanceIDNotFound], wait: 5, max: 5, ignoreme: [Aws::EC2::Errors::InvalidInstanceIDNotFound]) {
|
|
578
|
+
MU::Cloud::AWS.ec2(region: r, credentials: args[:credentials]).describe_instances(params).reservations.each { |resp|
|
|
579
|
+
next if resp.nil? or resp.instances.nil?
|
|
580
|
+
resp.instances.each { |i|
|
|
581
|
+
search_semaphore.synchronize {
|
|
582
|
+
found[i.instance_id] = i
|
|
583
|
+
}
|
|
584
|
+
}
|
|
1095
585
|
}
|
|
1096
|
-
|
|
586
|
+
}
|
|
1097
587
|
}
|
|
1098
588
|
}
|
|
1099
|
-
|
|
589
|
+
}
|
|
590
|
+
done_threads = []
|
|
591
|
+
begin
|
|
592
|
+
search_threads.each { |t|
|
|
593
|
+
joined = t.join(2)
|
|
594
|
+
done_threads << joined if !joined.nil?
|
|
595
|
+
}
|
|
596
|
+
end while found.size < 1 and done_threads.size != search_threads.size
|
|
1100
597
|
|
|
1101
598
|
return found
|
|
1102
599
|
end
|
|
@@ -1117,7 +614,7 @@ module MU
|
|
|
1117
614
|
return nil
|
|
1118
615
|
end
|
|
1119
616
|
|
|
1120
|
-
asgs = MU::Cloud
|
|
617
|
+
asgs = MU::Cloud.resourceClass("AWS", "ServerPool").find(
|
|
1121
618
|
instance_id: @cloud_id,
|
|
1122
619
|
region: @config['region'],
|
|
1123
620
|
credentials: @credentials
|
|
@@ -1211,8 +708,8 @@ module MU
|
|
|
1211
708
|
|
|
1212
709
|
int.private_ip_addresses.each { |priv_ip|
|
|
1213
710
|
if !priv_ip.primary
|
|
1214
|
-
bok['add_private_ips'] ||=
|
|
1215
|
-
bok['add_private_ips']
|
|
711
|
+
bok['add_private_ips'] ||= 0
|
|
712
|
+
bok['add_private_ips'] += 1
|
|
1216
713
|
end
|
|
1217
714
|
if priv_ip.association and priv_ip.association.public_ip
|
|
1218
715
|
bok['associate_public_ip'] = true
|
|
@@ -1227,15 +724,15 @@ module MU
|
|
|
1227
724
|
|
|
1228
725
|
if int.groups.size > 0
|
|
1229
726
|
|
|
1230
|
-
require 'mu/
|
|
1231
|
-
ifaces = MU::Cloud
|
|
727
|
+
require 'mu/providers/aws/firewall_rule'
|
|
728
|
+
ifaces = MU::Cloud.resourceClass("AWS", "FirewallRule").getAssociatedInterfaces(int.groups.map { |sg| sg.group_id }, credentials: @credentials, region: @config['region'])
|
|
1232
729
|
done_local_rules = false
|
|
1233
730
|
int.groups.each { |sg|
|
|
1234
731
|
if !done_local_rules and ifaces[sg.group_id].size == 1
|
|
1235
|
-
sg_desc = MU::Cloud
|
|
732
|
+
sg_desc = MU::Cloud.resourceClass("AWS", "FirewallRule").find(cloud_id: sg.group_id, credentials: @credentials, region: @config['region']).values.first
|
|
1236
733
|
if sg_desc
|
|
1237
|
-
bok["ingress_rules"] = MU::Cloud
|
|
1238
|
-
bok["ingress_rules"].concat(MU::Cloud
|
|
734
|
+
bok["ingress_rules"] = MU::Cloud.resourceClass("AWS", "FirewallRule").rulesToBoK(sg_desc.ip_permissions)
|
|
735
|
+
bok["ingress_rules"].concat(MU::Cloud.resourceClass("AWS", "FirewallRule").rulesToBoK(sg_desc.ip_permissions_egress, egress: true))
|
|
1239
736
|
done_local_rules = true
|
|
1240
737
|
next
|
|
1241
738
|
end
|
|
@@ -1304,52 +801,16 @@ module MU
|
|
|
1304
801
|
end
|
|
1305
802
|
deploydata["region"] = @config['region'] if !@config['region'].nil?
|
|
1306
803
|
if !@named
|
|
1307
|
-
MU::MommaCat.nameKitten(self)
|
|
804
|
+
MU::MommaCat.nameKitten(self, no_dns: true)
|
|
1308
805
|
@named = true
|
|
1309
806
|
end
|
|
1310
807
|
|
|
1311
808
|
return deploydata
|
|
1312
809
|
end
|
|
1313
810
|
|
|
1314
|
-
# If the specified server is in a VPC, and has a NAT, make sure we'll
|
|
1315
|
-
# be letting ssh traffic in from said NAT.
|
|
1316
|
-
def punchAdminNAT
|
|
1317
|
-
if @config['vpc'].nil? or
|
|
1318
|
-
(
|
|
1319
|
-
!@config['vpc'].has_key?("nat_host_id") and
|
|
1320
|
-
!@config['vpc'].has_key?("nat_host_tag") and
|
|
1321
|
-
!@config['vpc'].has_key?("nat_host_ip") and
|
|
1322
|
-
!@config['vpc'].has_key?("nat_host_name")
|
|
1323
|
-
)
|
|
1324
|
-
return nil
|
|
1325
|
-
end
|
|
1326
|
-
|
|
1327
|
-
return nil if @nat.is_a?(Struct) && @nat.nat_gateway_id && @nat.nat_gateway_id.start_with?("nat-")
|
|
1328
|
-
|
|
1329
|
-
dependencies if @nat.nil?
|
|
1330
|
-
if @nat.nil? or @nat.cloud_desc.nil?
|
|
1331
|
-
raise MuError, "#{@mu_name} (#{MU.deploy_id}) is configured to use #{@config['vpc']} but I can't find the cloud descriptor for a matching NAT instance"
|
|
1332
|
-
end
|
|
1333
|
-
MU.log "Adding administrative holes for NAT host #{@nat.cloud_desc.private_ip_address} to #{@mu_name}"
|
|
1334
|
-
if !@deploy.kittens['firewall_rules'].nil?
|
|
1335
|
-
@deploy.kittens['firewall_rules'].values.each { |acl|
|
|
1336
|
-
if acl.config["admin"]
|
|
1337
|
-
acl.addRule([@nat.cloud_desc.private_ip_address], proto: "tcp")
|
|
1338
|
-
acl.addRule([@nat.cloud_desc.private_ip_address], proto: "udp")
|
|
1339
|
-
acl.addRule([@nat.cloud_desc.private_ip_address], proto: "icmp")
|
|
1340
|
-
end
|
|
1341
|
-
}
|
|
1342
|
-
end
|
|
1343
|
-
end
|
|
1344
|
-
|
|
1345
811
|
# Called automatically by {MU::Deploy#createResources}
|
|
1346
812
|
def groom
|
|
1347
813
|
MU::MommaCat.lock(@cloud_id+"-groom")
|
|
1348
|
-
node, _config, deploydata = describe(cloud_id: @cloud_id)
|
|
1349
|
-
|
|
1350
|
-
if node.nil? or node.empty?
|
|
1351
|
-
raise MuError, "MU::Cloud::AWS::Server.groom was called without a mu_name"
|
|
1352
|
-
end
|
|
1353
814
|
|
|
1354
815
|
# Make double sure we don't lose a cached mu_windows_name value.
|
|
1355
816
|
if windows? or !@config['active_directory'].nil?
|
|
@@ -1358,9 +819,9 @@ module MU
|
|
|
1358
819
|
end
|
|
1359
820
|
end
|
|
1360
821
|
|
|
1361
|
-
|
|
822
|
+
allowBastionAccess
|
|
1362
823
|
|
|
1363
|
-
|
|
824
|
+
tagVolumes
|
|
1364
825
|
|
|
1365
826
|
# If we have a loadbalancer configured, attach us to it
|
|
1366
827
|
if !@config['loadbalancers'].nil?
|
|
@@ -1389,55 +850,31 @@ module MU
|
|
|
1389
850
|
end
|
|
1390
851
|
|
|
1391
852
|
begin
|
|
853
|
+
getIAMProfile
|
|
854
|
+
|
|
855
|
+
dbs = @deploy.findLitterMate(type: "database", return_all: true)
|
|
856
|
+
if dbs
|
|
857
|
+
dbs.each_pair { |sib_name, sib|
|
|
858
|
+
@groomer.groomer_class.grantSecretAccess(@mu_name, sib_name, "database_credentials")
|
|
859
|
+
if sib.config and sib.config['auth_vault']
|
|
860
|
+
@groomer.groomer_class.grantSecretAccess(@mu_name, sib.config['auth_vault']['vault'], sib.config['auth_vault']['item'])
|
|
861
|
+
end
|
|
862
|
+
}
|
|
863
|
+
end
|
|
864
|
+
|
|
1392
865
|
if @config['groom'].nil? or @config['groom']
|
|
1393
|
-
@groomer.run(purpose: "Full Initial Run", max_retries: 15, reboot_first_fail: windows
|
|
866
|
+
@groomer.run(purpose: "Full Initial Run", max_retries: 15, reboot_first_fail: (windows? and @config['groomer'] != "Ansible"), timeout: @config['groomer_timeout'])
|
|
1394
867
|
end
|
|
1395
868
|
rescue MU::Groomer::RunError => e
|
|
1396
|
-
|
|
869
|
+
raise e if !@config['create_image'].nil? and !@config['image_created']
|
|
870
|
+
MU.log "Proceeding after failed initial Groomer run, but #{@mu_name} may not behave as expected!", MU::WARN, details: e.message
|
|
1397
871
|
rescue StandardError => e
|
|
1398
|
-
|
|
872
|
+
raise e if !@config['create_image'].nil? and !@config['image_created']
|
|
873
|
+
MU.log "Caught #{e.inspect} on #{@mu_name} in an unexpected place (after @groomer.run on Full Initial Run)", MU::ERR
|
|
1399
874
|
end
|
|
1400
875
|
|
|
1401
876
|
if !@config['create_image'].nil? and !@config['image_created']
|
|
1402
|
-
|
|
1403
|
-
# Scrub things that don't belong on an AMI
|
|
1404
|
-
session = getSSHSession
|
|
1405
|
-
sudo = purgecmd = ""
|
|
1406
|
-
sudo = "sudo" if @config['ssh_user'] != "root"
|
|
1407
|
-
if windows?
|
|
1408
|
-
purgecmd = "rm -rf /cygdrive/c/mu_installed_chef"
|
|
1409
|
-
else
|
|
1410
|
-
purgecmd = "rm -rf /opt/mu_installed_chef"
|
|
1411
|
-
end
|
|
1412
|
-
if img_cfg['image_then_destroy']
|
|
1413
|
-
if windows?
|
|
1414
|
-
purgecmd = "rm -rf /cygdrive/c/chef/ /home/#{@config['windows_admin_username']}/.ssh/authorized_keys /home/Administrator/.ssh/authorized_keys /cygdrive/c/mu-installer-ran-updates /cygdrive/c/mu_installed_chef"
|
|
1415
|
-
# session.exec!("powershell -Command \"& {(Get-WmiObject -Class Win32_Product -Filter \"Name='UniversalForwarder'\").Uninstall()}\"")
|
|
1416
|
-
else
|
|
1417
|
-
purgecmd = "#{sudo} rm -rf /var/lib/cloud/instances/i-* /root/.ssh/authorized_keys /etc/ssh/ssh_host_*key* /etc/chef /etc/opscode/* /.mu-installer-ran-updates /var/chef /opt/mu_installed_chef /opt/chef ; #{sudo} sed -i 's/^HOSTNAME=.*//' /etc/sysconfig/network"
|
|
1418
|
-
end
|
|
1419
|
-
end
|
|
1420
|
-
session.exec!(purgecmd)
|
|
1421
|
-
session.close
|
|
1422
|
-
ami_ids = MU::Cloud::AWS::Server.createImage(
|
|
1423
|
-
name: @mu_name,
|
|
1424
|
-
instance_id: @cloud_id,
|
|
1425
|
-
storage: @config['storage'],
|
|
1426
|
-
exclude_storage: img_cfg['image_exclude_storage'],
|
|
1427
|
-
copy_to_regions: img_cfg['copy_to_regions'],
|
|
1428
|
-
make_public: img_cfg['public'],
|
|
1429
|
-
region: @config['region'],
|
|
1430
|
-
tags: @config['tags'],
|
|
1431
|
-
credentials: @config['credentials']
|
|
1432
|
-
)
|
|
1433
|
-
@deploy.notify("images", @config['name'], ami_ids)
|
|
1434
|
-
@config['image_created'] = true
|
|
1435
|
-
if img_cfg['image_then_destroy']
|
|
1436
|
-
MU::Cloud::AWS::Server.waitForAMI(ami_ids[@config['region']], region: @config['region'], credentials: @config['credentials'])
|
|
1437
|
-
MU.log "AMI #{ami_ids[@config['region']]} ready, removing source node #{node}"
|
|
1438
|
-
MU::Cloud::AWS::Server.terminateInstance(id: @cloud_id, region: @config['region'], deploy_id: @deploy.deploy_id, mu_name: @mu_name, credentials: @config['credentials'])
|
|
1439
|
-
destroy
|
|
1440
|
-
end
|
|
877
|
+
createImage
|
|
1441
878
|
end
|
|
1442
879
|
|
|
1443
880
|
MU::MommaCat.unlock(@cloud_id+"-groom")
|
|
@@ -1454,6 +891,7 @@ module MU
|
|
|
1454
891
|
# @return [Openstruct]
|
|
1455
892
|
def cloud_desc(use_cache: true)
|
|
1456
893
|
return @cloud_desc_cache if @cloud_desc_cache and use_cache
|
|
894
|
+
return nil if !@cloud_id
|
|
1457
895
|
max_retries = 5
|
|
1458
896
|
retries = 0
|
|
1459
897
|
if !@cloud_id.nil?
|
|
@@ -1485,23 +923,19 @@ module MU
|
|
|
1485
923
|
# bastion hosts that may be in the path, see getSSHConfig if that's what
|
|
1486
924
|
# you need.
|
|
1487
925
|
def canonicalIP
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
instance = cloud_desc
|
|
1491
|
-
|
|
1492
|
-
if !instance
|
|
926
|
+
if !cloud_desc
|
|
1493
927
|
raise MuError, "Couldn't retrieve cloud descriptor for server #{self}"
|
|
1494
928
|
end
|
|
1495
929
|
|
|
1496
930
|
if deploydata.nil? or
|
|
1497
931
|
(!deploydata.has_key?("private_ip_address") and
|
|
1498
932
|
!deploydata.has_key?("public_ip_address"))
|
|
1499
|
-
return nil if
|
|
933
|
+
return nil if cloud_desc.nil?
|
|
1500
934
|
@deploydata = {} if @deploydata.nil?
|
|
1501
|
-
@deploydata["public_ip_address"] =
|
|
1502
|
-
@deploydata["public_dns_name"] =
|
|
1503
|
-
@deploydata["private_ip_address"] =
|
|
1504
|
-
@deploydata["private_dns_name"] =
|
|
935
|
+
@deploydata["public_ip_address"] = cloud_desc.public_ip_address
|
|
936
|
+
@deploydata["public_dns_name"] = cloud_desc.public_dns_name
|
|
937
|
+
@deploydata["private_ip_address"] = cloud_desc.private_ip_address
|
|
938
|
+
@deploydata["private_dns_name"] = cloud_desc.private_dns_name
|
|
1505
939
|
|
|
1506
940
|
notify
|
|
1507
941
|
end
|
|
@@ -1509,14 +943,14 @@ module MU
|
|
|
1509
943
|
# Our deploydata gets corrupted often with server pools, this will cause us to use the wrong IP to identify a node
|
|
1510
944
|
# which will cause us to create certificates, DNS records and other artifacts with incorrect information which will cause our deploy to fail.
|
|
1511
945
|
# The cloud_id is always correct so lets use 'cloud_desc' to get the correct IPs
|
|
1512
|
-
if MU::Cloud
|
|
1513
|
-
@config['canonical_ip'] =
|
|
1514
|
-
@deploydata["private_ip_address"] =
|
|
1515
|
-
return
|
|
946
|
+
if MU::Cloud.resourceClass("AWS", "VPC").haveRouteToInstance?(cloud_desc, region: @config['region'], credentials: @config['credentials']) or @deploydata["public_ip_address"].nil?
|
|
947
|
+
@config['canonical_ip'] = cloud_desc.private_ip_address
|
|
948
|
+
@deploydata["private_ip_address"] = cloud_desc.private_ip_address
|
|
949
|
+
return cloud_desc.private_ip_address
|
|
1516
950
|
else
|
|
1517
|
-
@config['canonical_ip'] =
|
|
1518
|
-
@deploydata["public_ip_address"] =
|
|
1519
|
-
return
|
|
951
|
+
@config['canonical_ip'] = cloud_desc.public_ip_address
|
|
952
|
+
@deploydata["public_ip_address"] = cloud_desc.public_ip_address
|
|
953
|
+
return cloud_desc.public_ip_address
|
|
1520
954
|
end
|
|
1521
955
|
end
|
|
1522
956
|
|
|
@@ -1709,11 +1143,27 @@ module MU
|
|
|
1709
1143
|
# Retrieves the Cloud provider's randomly generated Windows password
|
|
1710
1144
|
# Will only work on stock Amazon Windows AMIs or custom AMIs that where created with Administrator Password set to random in EC2Config
|
|
1711
1145
|
# return [String]: A password string.
|
|
1712
|
-
def getWindowsAdminPassword
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1146
|
+
def getWindowsAdminPassword(use_cache: true)
|
|
1147
|
+
@config['windows_auth_vault'] ||= {
|
|
1148
|
+
"vault" => @mu_name,
|
|
1149
|
+
"item" => "windows_credentials",
|
|
1150
|
+
"password_field" => "password"
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
if use_cache
|
|
1154
|
+
begin
|
|
1155
|
+
win_admin_password = @groomer.getSecret(
|
|
1156
|
+
vault: @config['windows_auth_vault']['vault'],
|
|
1157
|
+
item: @config['windows_auth_vault']['item'],
|
|
1158
|
+
field: @config["windows_auth_vault"]["password_field"]
|
|
1159
|
+
)
|
|
1160
|
+
|
|
1161
|
+
return win_admin_password if win_admin_password
|
|
1162
|
+
rescue MU::Groomer::MuNoSuchSecret, MU::Groomer::RunError
|
|
1163
|
+
end
|
|
1716
1164
|
end
|
|
1165
|
+
|
|
1166
|
+
@cloud_id ||= cloud_desc(use_cache: false).instance_id
|
|
1717
1167
|
ssh_keydir = "#{Etc.getpwuid(Process.uid).dir}/.ssh"
|
|
1718
1168
|
ssh_key_name = @deploy.ssh_key_name
|
|
1719
1169
|
|
|
@@ -1748,6 +1198,8 @@ module MU
|
|
|
1748
1198
|
pem_bytes = File.open("#{ssh_keydir}/#{ssh_key_name}", 'rb') { |f| f.read }
|
|
1749
1199
|
private_key = OpenSSL::PKey::RSA.new(pem_bytes)
|
|
1750
1200
|
decrypted_password = private_key.private_decrypt(decoded)
|
|
1201
|
+
saveCredentials(decrypted_password)
|
|
1202
|
+
|
|
1751
1203
|
return decrypted_password
|
|
1752
1204
|
end
|
|
1753
1205
|
|
|
@@ -1821,60 +1273,37 @@ module MU
|
|
|
1821
1273
|
# @param type [String]: Cloud storage type of the volume, if applicable
|
|
1822
1274
|
# @param delete_on_termination [Boolean]: Value of delete_on_termination flag to set
|
|
1823
1275
|
def addVolume(dev, size, type: "gp2", delete_on_termination: false)
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1276
|
+
|
|
1277
|
+
if setDeleteOntermination(dev, delete_on_termination)
|
|
1278
|
+
MU.log "A volume #{device} already attached to #{self}, skipping", MU::NOTICE
|
|
1279
|
+
return
|
|
1827
1280
|
end
|
|
1828
|
-
|
|
1829
|
-
MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_instances(
|
|
1830
|
-
instance_ids: [@cloud_id]
|
|
1831
|
-
).reservations.each { |resp|
|
|
1832
|
-
if !resp.nil? and !resp.instances.nil?
|
|
1833
|
-
resp.instances.each { |instance|
|
|
1834
|
-
az = instance.placement.availability_zone
|
|
1835
|
-
mappings = MU.structToHash(instance.block_device_mappings)
|
|
1836
|
-
mappings.each { |vol|
|
|
1837
|
-
if vol[:ebs]
|
|
1838
|
-
vol[:ebs].delete(:attach_time)
|
|
1839
|
-
vol[:ebs].delete(:status)
|
|
1840
|
-
end
|
|
1841
|
-
}
|
|
1842
|
-
mappings.each { |vol|
|
|
1843
|
-
if vol[:device_name] == dev
|
|
1844
|
-
MU.log "A volume #{dev} already attached to #{self}, skipping", MU::NOTICE
|
|
1845
|
-
if vol[:ebs][:delete_on_termination] != delete_on_termination
|
|
1846
|
-
vol[:ebs][:delete_on_termination] = delete_on_termination
|
|
1847
|
-
MU.log "Setting delete_on_termination flag to #{delete_on_termination.to_s} on #{@mu_name}'s #{dev}"
|
|
1848
|
-
MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).modify_instance_attribute(
|
|
1849
|
-
instance_id: @cloud_id,
|
|
1850
|
-
block_device_mappings: mappings
|
|
1851
|
-
)
|
|
1852
|
-
end
|
|
1853
|
-
return
|
|
1854
|
-
end
|
|
1855
|
-
}
|
|
1856
|
-
}
|
|
1857
|
-
end
|
|
1858
|
-
}
|
|
1281
|
+
|
|
1859
1282
|
MU.log "Creating #{size}GB #{type} volume on #{dev} for #{@cloud_id}"
|
|
1860
1283
|
creation = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).create_volume(
|
|
1861
|
-
availability_zone:
|
|
1284
|
+
availability_zone: cloud_desc.placement.availability_zone,
|
|
1862
1285
|
size: size,
|
|
1863
1286
|
volume_type: type
|
|
1864
1287
|
)
|
|
1865
|
-
|
|
1866
|
-
|
|
1288
|
+
|
|
1289
|
+
MU.retrier(wait: 3, loop_if: Proc.new {
|
|
1867
1290
|
creation = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_volumes(volume_ids: [creation.volume_id]).volumes.first
|
|
1868
1291
|
if !["creating", "available"].include?(creation.state)
|
|
1869
1292
|
raise MuError, "Saw state '#{creation.state}' while creating #{size}GB #{type} volume on #{dev} for #{@cloud_id}"
|
|
1870
1293
|
end
|
|
1871
|
-
|
|
1294
|
+
creation.state != "available"
|
|
1295
|
+
})
|
|
1296
|
+
|
|
1872
1297
|
|
|
1873
1298
|
if @deploy
|
|
1874
|
-
MU::
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1299
|
+
MU::Cloud::AWS.createStandardTags(
|
|
1300
|
+
creation.volume_id,
|
|
1301
|
+
region: @config['region'],
|
|
1302
|
+
credentials: @config['credentials'],
|
|
1303
|
+
optional: @config['optional_tags'],
|
|
1304
|
+
nametag: @mu_name+"-"+dev.upcase,
|
|
1305
|
+
othertags: @config['tags']
|
|
1306
|
+
)
|
|
1878
1307
|
end
|
|
1879
1308
|
|
|
1880
1309
|
attachment = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).attach_volume(
|
|
@@ -1893,29 +1322,7 @@ module MU
|
|
|
1893
1322
|
|
|
1894
1323
|
# Set delete_on_termination, which for some reason is an instance
|
|
1895
1324
|
# attribute and not on the attachment
|
|
1896
|
-
|
|
1897
|
-
changed = false
|
|
1898
|
-
|
|
1899
|
-
mappings.each { |mapping|
|
|
1900
|
-
if mapping[:ebs]
|
|
1901
|
-
mapping[:ebs].delete(:attach_time)
|
|
1902
|
-
mapping[:ebs].delete(:status)
|
|
1903
|
-
end
|
|
1904
|
-
if mapping[:device_name] == dev and
|
|
1905
|
-
mapping[:ebs][:delete_on_termination] != delete_on_termination
|
|
1906
|
-
changed = true
|
|
1907
|
-
mapping[:ebs][:delete_on_termination] = delete_on_termination
|
|
1908
|
-
end
|
|
1909
|
-
}
|
|
1910
|
-
|
|
1911
|
-
if changed
|
|
1912
|
-
MU.log "Setting delete_on_termination flag to #{delete_on_termination.to_s} on #{@mu_name}'s #{dev}"
|
|
1913
|
-
MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).modify_instance_attribute(
|
|
1914
|
-
instance_id: @cloud_id,
|
|
1915
|
-
block_device_mappings: mappings
|
|
1916
|
-
)
|
|
1917
|
-
end
|
|
1918
|
-
|
|
1325
|
+
setDeleteOntermination(dev, delete_on_termination)
|
|
1919
1326
|
end
|
|
1920
1327
|
|
|
1921
1328
|
# Determine whether the node in question exists at the Cloud provider
|
|
@@ -1953,13 +1360,13 @@ module MU
|
|
|
1953
1360
|
# @param ip [String]: Request a specific IP address.
|
|
1954
1361
|
# @param region [String]: The cloud provider region
|
|
1955
1362
|
# @return [void]
|
|
1956
|
-
def self.associateElasticIp(instance_id, classic: false, ip: nil, region: MU.curRegion)
|
|
1363
|
+
def self.associateElasticIp(instance_id, classic: false, ip: nil, region: MU.curRegion, credentials: nil)
|
|
1957
1364
|
MU.log "associateElasticIp called: #{instance_id}, classic: #{classic}, ip: #{ip}, region: #{region}", MU::DEBUG
|
|
1958
1365
|
elastic_ip = nil
|
|
1959
1366
|
@eip_semaphore.synchronize {
|
|
1960
1367
|
if !ip.nil?
|
|
1961
1368
|
filters = [{name: "public-ip", values: [ip]}]
|
|
1962
|
-
resp = MU::Cloud::AWS.ec2(region: region).describe_addresses(filters: filters)
|
|
1369
|
+
resp = MU::Cloud::AWS.ec2(region: region, credentials: credentials).describe_addresses(filters: filters)
|
|
1963
1370
|
if @eips_used.include?(ip)
|
|
1964
1371
|
is_free = false
|
|
1965
1372
|
resp.addresses.each { |address|
|
|
@@ -1988,54 +1395,44 @@ module MU
|
|
|
1988
1395
|
@eips_used << elastic_ip.public_ip
|
|
1989
1396
|
MU.log "Associating Elastic IP #{elastic_ip.public_ip} with #{instance_id}", details: elastic_ip
|
|
1990
1397
|
}
|
|
1991
|
-
|
|
1992
|
-
|
|
1398
|
+
|
|
1399
|
+
on_retry = Proc.new { |e|
|
|
1400
|
+
if e.class == Aws::EC2::Errors::ResourceAlreadyAssociated
|
|
1401
|
+
# A previous association attempt may have succeeded, albeit slowly.
|
|
1402
|
+
resp = MU::Cloud::AWS.ec2(region: region, credentials: credentials).describe_addresses(
|
|
1403
|
+
allocation_ids: [elastic_ip.allocation_id]
|
|
1404
|
+
)
|
|
1405
|
+
first_addr = resp.addresses.first
|
|
1406
|
+
if first_addr and first_addr.instance_id != instance_id
|
|
1407
|
+
raise MuError, "Tried to associate #{elastic_ip.public_ip} with #{instance_id}, but it's already associated with #{first_addr.instance_id}!"
|
|
1408
|
+
end
|
|
1409
|
+
end
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
MU.retrier([Aws::EC2::Errors::IncorrectInstanceState, Aws::EC2::Errors::ResourceAlreadyAssociated], wait: 5, max: 6, on_retry: on_retry) {
|
|
1993
1413
|
if classic
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1414
|
+
MU::Cloud::AWS.ec2(region: region, credentials: credentials).associate_address(
|
|
1415
|
+
instance_id: instance_id,
|
|
1416
|
+
public_ip: elastic_ip.public_ip
|
|
1997
1417
|
)
|
|
1998
1418
|
else
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
1419
|
+
MU::Cloud::AWS.ec2(region: region, credentials: credentials).associate_address(
|
|
1420
|
+
instance_id: instance_id,
|
|
1421
|
+
allocation_id: elastic_ip.allocation_id,
|
|
1422
|
+
allow_reassociation: false
|
|
2003
1423
|
)
|
|
2004
1424
|
end
|
|
2005
|
-
|
|
2006
|
-
attempts = attempts + 1
|
|
2007
|
-
if attempts < 6
|
|
2008
|
-
MU.log "Got #{e.message} associating #{elastic_ip.allocation_id} with #{instance_id}, retrying", MU::WARN
|
|
2009
|
-
sleep 5
|
|
2010
|
-
retry
|
|
2011
|
-
end
|
|
2012
|
-
raise MuError "#{e.message} associating #{elastic_ip.allocation_id} with #{instance_id}"
|
|
2013
|
-
rescue Aws::EC2::Errors::ResourceAlreadyAssociated => e
|
|
2014
|
-
# A previous association attempt may have succeeded, albeit slowly.
|
|
2015
|
-
resp = MU::Cloud::AWS.ec2(region: region).describe_addresses(
|
|
2016
|
-
allocation_ids: [elastic_ip.allocation_id]
|
|
2017
|
-
)
|
|
2018
|
-
first_addr = resp.addresses.first
|
|
2019
|
-
if !first_addr.nil? and first_addr.instance_id == instance_id
|
|
2020
|
-
MU.log "#{elastic_ip.public_ip} already associated with #{instance_id}", MU::WARN
|
|
2021
|
-
else
|
|
2022
|
-
MU.log "#{elastic_ip.public_ip} shows as already associated!", MU::ERR, details: resp
|
|
2023
|
-
raise MuError, "#{elastic_ip.public_ip} shows as already associated with #{first_addr.instance_id}!"
|
|
2024
|
-
end
|
|
2025
|
-
end
|
|
1425
|
+
}
|
|
2026
1426
|
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
instance = MU::Cloud::AWS.ec2(region: region).describe_instances(instance_ids: [instance_id]).reservations.first.instances.first
|
|
2035
|
-
end while instance.public_ip_address != elastic_ip.public_ip
|
|
2036
|
-
end
|
|
1427
|
+
loop_if = Proc.new {
|
|
1428
|
+
instance = find(cloud_id: instance_id, region: region, credentials: credentials).values.first
|
|
1429
|
+
instance.public_ip_address != elastic_ip.public_ip
|
|
1430
|
+
}
|
|
1431
|
+
MU.retrier(loop_if: loop_if, wait: 10, max: 3) {
|
|
1432
|
+
MU.log "Waiting for Elastic IP association of #{elastic_ip.public_ip} to #{instance_id} to take effect", MU::NOTICE
|
|
1433
|
+
}
|
|
2037
1434
|
|
|
2038
|
-
MU.log "Elastic IP #{elastic_ip.public_ip} now associated with #{instance_id}"
|
|
1435
|
+
MU.log "Elastic IP #{elastic_ip.public_ip} now associated with #{instance_id}"
|
|
2039
1436
|
|
|
2040
1437
|
return elastic_ip.public_ip
|
|
2041
1438
|
end
|
|
@@ -2058,11 +1455,11 @@ module MU
|
|
|
2058
1455
|
# @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
|
|
2059
1456
|
# @param region [String]: The cloud provider region
|
|
2060
1457
|
# @return [void]
|
|
2061
|
-
def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
|
|
1458
|
+
def self.cleanup(noop: false, deploy_id: MU.deploy_id, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
|
|
2062
1459
|
onlycloud = flags["onlycloud"]
|
|
2063
1460
|
skipsnapshots = flags["skipsnapshots"]
|
|
2064
1461
|
tagfilters = [
|
|
2065
|
-
{name: "tag:MU-ID", values: [
|
|
1462
|
+
{name: "tag:MU-ID", values: [deploy_id]}
|
|
2066
1463
|
]
|
|
2067
1464
|
if !ignoremaster
|
|
2068
1465
|
tagfilters << {name: "tag:MU-MASTER-IP", values: [MU.mu_public_ip]}
|
|
@@ -2096,7 +1493,7 @@ module MU
|
|
|
2096
1493
|
threads << Thread.new(instance) { |myinstance|
|
|
2097
1494
|
MU.dupGlobals(parent_thread_id)
|
|
2098
1495
|
Thread.abort_on_exception = true
|
|
2099
|
-
MU::Cloud::AWS::Server.terminateInstance(id: myinstance.instance_id, noop: noop, onlycloud: onlycloud, region: region, deploy_id:
|
|
1496
|
+
MU::Cloud::AWS::Server.terminateInstance(id: myinstance.instance_id, noop: noop, onlycloud: onlycloud, region: region, deploy_id: deploy_id, credentials: credentials)
|
|
2100
1497
|
}
|
|
2101
1498
|
}
|
|
2102
1499
|
|
|
@@ -2107,7 +1504,7 @@ module MU
|
|
|
2107
1504
|
threads << Thread.new(volume) { |myvolume|
|
|
2108
1505
|
MU.dupGlobals(parent_thread_id)
|
|
2109
1506
|
Thread.abort_on_exception = true
|
|
2110
|
-
delete_volume(myvolume, noop, skipsnapshots, credentials: credentials)
|
|
1507
|
+
delete_volume(myvolume, noop, skipsnapshots, credentials: credentials, deploy_id: deploy_id)
|
|
2111
1508
|
}
|
|
2112
1509
|
}
|
|
2113
1510
|
|
|
@@ -2117,193 +1514,113 @@ module MU
|
|
|
2117
1514
|
}
|
|
2118
1515
|
end
|
|
2119
1516
|
|
|
1517
|
+
# Return an instance's AWS-assigned IP addresses and hostnames.
|
|
1518
|
+
# @param instance [OpenStruct]
|
|
1519
|
+
# @param id [String]
|
|
1520
|
+
# @param region [String]
|
|
1521
|
+
# @param credentials [@String]
|
|
1522
|
+
# @return [Array<Array>]
|
|
1523
|
+
def self.getAddresses(instance = nil, id: nil, region: MU.curRegion, credentials: nil)
|
|
1524
|
+
return nil if !instance and !id
|
|
1525
|
+
|
|
1526
|
+
instance ||= find(cloud_id: id, region: region, credentials: credentials).values.first
|
|
1527
|
+
return if !instance
|
|
1528
|
+
|
|
1529
|
+
ips = []
|
|
1530
|
+
names = []
|
|
1531
|
+
instance.network_interfaces.each { |iface|
|
|
1532
|
+
iface.private_ip_addresses.each { |ip|
|
|
1533
|
+
ips << ip.private_ip_address
|
|
1534
|
+
names << ip.private_dns_name
|
|
1535
|
+
if ip.association
|
|
1536
|
+
ips << ip.association.public_ip
|
|
1537
|
+
names << ip.association.public_dns_name
|
|
1538
|
+
end
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
[ips, names]
|
|
1543
|
+
end
|
|
1544
|
+
|
|
2120
1545
|
# Terminate an instance.
|
|
2121
1546
|
# @param instance [OpenStruct]: The cloud provider's description of the instance.
|
|
2122
1547
|
# @param id [String]: The cloud provider's identifier for the instance, to use if the full description is not available.
|
|
2123
1548
|
# @param region [String]: The cloud provider region
|
|
2124
1549
|
# @return [void]
|
|
2125
1550
|
def self.terminateInstance(instance: nil, noop: false, id: nil, onlycloud: false, region: MU.curRegion, deploy_id: MU.deploy_id, mu_name: nil, credentials: nil)
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
begin
|
|
2130
|
-
resp = MU::Cloud::AWS.ec2(credentials: credentials, region: region).describe_instances(instance_ids: [id])
|
|
2131
|
-
rescue Aws::EC2::Errors::InvalidInstanceIDNotFound
|
|
2132
|
-
MU.log "Instance #{id} no longer exists", MU::WARN
|
|
2133
|
-
end
|
|
2134
|
-
if !resp.nil? and !resp.reservations.nil? and !resp.reservations.first.nil?
|
|
2135
|
-
instance = resp.reservations.first.instances.first
|
|
2136
|
-
ips << instance.public_ip_address if !instance.public_ip_address.nil?
|
|
2137
|
-
ips << instance.private_ip_address if !instance.private_ip_address.nil?
|
|
2138
|
-
end
|
|
2139
|
-
else
|
|
2140
|
-
MU.log "You must supply an instance handle or id to terminateInstance", MU::ERR
|
|
2141
|
-
end
|
|
2142
|
-
else
|
|
2143
|
-
id = instance.instance_id
|
|
2144
|
-
end
|
|
2145
|
-
if !MU.deploy_id.empty?
|
|
2146
|
-
deploy_dir = File.expand_path("#{MU.dataDir}/deployments/"+MU.deploy_id)
|
|
2147
|
-
if Dir.exist?(deploy_dir) and !noop
|
|
2148
|
-
FileUtils.touch("#{deploy_dir}/.cleanup-"+id)
|
|
2149
|
-
end
|
|
1551
|
+
if !id and !instance
|
|
1552
|
+
MU.log "You must supply an instance handle or id to terminateInstance", MU::ERR
|
|
1553
|
+
return
|
|
2150
1554
|
end
|
|
1555
|
+
instance ||= find(cloud_id: id, region: region, credentials: credentials).values.first
|
|
1556
|
+
return if !instance
|
|
2151
1557
|
|
|
2152
|
-
|
|
2153
|
-
"AWS",
|
|
2154
|
-
"servers",
|
|
2155
|
-
region: region,
|
|
2156
|
-
deploy_id: deploy_id,
|
|
2157
|
-
cloud_id: id,
|
|
2158
|
-
mu_name: mu_name
|
|
2159
|
-
).first
|
|
2160
|
-
|
|
1558
|
+
id ||= instance.instance_id
|
|
2161
1559
|
begin
|
|
2162
|
-
MU::
|
|
2163
|
-
rescue
|
|
2164
|
-
MU.log "
|
|
2165
|
-
end
|
|
2166
|
-
|
|
2167
|
-
if !server_obj.nil? and MU::Cloud::AWS.hosted? and !MU::Cloud::AWS.isGovCloud?
|
|
2168
|
-
# DNS cleanup is now done in MU::Cloud::DNSZone. Keeping this for now
|
|
2169
|
-
cleaned_dns = false
|
|
2170
|
-
mu_name = server_obj.mu_name
|
|
2171
|
-
mu_zone = MU::Cloud::DNSZone.find(cloud_id: "platform-mu", credentials: credentials).values.first
|
|
2172
|
-
if !mu_zone.nil?
|
|
2173
|
-
zone_rrsets = []
|
|
2174
|
-
rrsets = MU::Cloud::AWS.route53(credentials: credentials).list_resource_record_sets(hosted_zone_id: mu_zone.id)
|
|
2175
|
-
rrsets.resource_record_sets.each{ |record|
|
|
2176
|
-
zone_rrsets << record
|
|
2177
|
-
}
|
|
1560
|
+
MU::MommaCat.lock(".cleanup-"+id)
|
|
1561
|
+
rescue Errno::ENOENT => e
|
|
1562
|
+
MU.log "No lock for terminating instance #{id} due to missing metadata", MU::DEBUG
|
|
1563
|
+
end
|
|
2178
1564
|
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
rrsets = MU::Cloud::AWS.route53(credentials: credentials).list_resource_record_sets(hosted_zone_id: mu_zone.id, start_record_name: rrsets.next_record_name, start_record_type: rrsets.next_record_type)
|
|
2182
|
-
rrsets.resource_record_sets.each{ |record|
|
|
2183
|
-
zone_rrsets << record
|
|
2184
|
-
}
|
|
2185
|
-
end
|
|
2186
|
-
end
|
|
2187
|
-
if !onlycloud and !mu_name.nil?
|
|
2188
|
-
# DNS cleanup is now done in MU::Cloud::DNSZone. Keeping this for now
|
|
2189
|
-
if !zone_rrsets.nil? and !zone_rrsets.empty?
|
|
2190
|
-
zone_rrsets.each { |rrset|
|
|
2191
|
-
if rrset.name.match(/^#{mu_name.downcase}\.server\.#{MU.myInstanceId}\.platform-mu/i)
|
|
2192
|
-
rrset.resource_records.each { |record|
|
|
2193
|
-
MU::Cloud::DNSZone.genericMuDNSEntry(name: mu_name, target: record.value, cloudclass: MU::Cloud::Server, delete: true)
|
|
2194
|
-
cleaned_dns = true
|
|
2195
|
-
}
|
|
2196
|
-
end
|
|
2197
|
-
}
|
|
2198
|
-
end
|
|
1565
|
+
ips, names = getAddresses(instance, region: region, credentials: credentials)
|
|
1566
|
+
targets = ips +names
|
|
2199
1567
|
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
1568
|
+
server_obj = MU::MommaCat.findStray(
|
|
1569
|
+
"AWS",
|
|
1570
|
+
"servers",
|
|
1571
|
+
region: region,
|
|
1572
|
+
deploy_id: deploy_id,
|
|
1573
|
+
cloud_id: id,
|
|
1574
|
+
mu_name: mu_name,
|
|
1575
|
+
dummy_ok: true
|
|
1576
|
+
).first
|
|
2205
1577
|
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
if tag.key == "Name"
|
|
2211
|
-
zone_rrsets.each { |rrset|
|
|
2212
|
-
if rrset.name.match(/^#{tag.value.downcase}\.server\.#{MU.myInstanceId}\.platform-mu/i)
|
|
2213
|
-
rrset.resource_records.each { |record|
|
|
2214
|
-
MU::Cloud::DNSZone.genericMuDNSEntry(name: tag.value, target: record.value, cloudclass: MU::Cloud::Server, delete: true) if !noop
|
|
2215
|
-
}
|
|
2216
|
-
end
|
|
2217
|
-
}
|
|
2218
|
-
end
|
|
2219
|
-
}
|
|
2220
|
-
end
|
|
2221
|
-
end
|
|
1578
|
+
if MU::Cloud::AWS.hosted? and !MU::Cloud::AWS.isGovCloud? and server_obj
|
|
1579
|
+
targets.each { |target|
|
|
1580
|
+
MU::Cloud::DNSZone.genericMuDNSEntry(name: server_obj.mu_name, target: target, cloudclass: MU::Cloud::Server, delete: true, noop: noop)
|
|
1581
|
+
}
|
|
2222
1582
|
end
|
|
2223
1583
|
|
|
2224
|
-
if
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
rescue ArgumentError
|
|
2230
|
-
# we're in a non-nagios environment and that's ok
|
|
2231
|
-
end
|
|
2232
|
-
end
|
|
2233
|
-
known_hosts_files.each { |known_hosts|
|
|
2234
|
-
next if !File.exist?(known_hosts)
|
|
2235
|
-
MU.log "Cleaning up #{ips} from #{known_hosts}"
|
|
2236
|
-
if !noop
|
|
2237
|
-
File.open(known_hosts, File::CREAT|File::RDWR, 0644) { |f|
|
|
2238
|
-
f.flock(File::LOCK_EX)
|
|
2239
|
-
newlines = Array.new
|
|
2240
|
-
f.readlines.each { |line|
|
|
2241
|
-
ip_match = false
|
|
2242
|
-
ips.each { |ip|
|
|
2243
|
-
if line.match(/(^|,| )#{ip}( |,)/)
|
|
2244
|
-
MU.log "Expunging #{ip} from #{known_hosts}"
|
|
2245
|
-
ip_match = true
|
|
2246
|
-
end
|
|
2247
|
-
}
|
|
2248
|
-
newlines << line if !ip_match
|
|
2249
|
-
}
|
|
2250
|
-
f.rewind
|
|
2251
|
-
f.truncate(0)
|
|
2252
|
-
f.puts(newlines)
|
|
2253
|
-
f.flush
|
|
2254
|
-
f.flock(File::LOCK_UN)
|
|
2255
|
-
}
|
|
2256
|
-
end
|
|
1584
|
+
if targets.size > 0 and !onlycloud
|
|
1585
|
+
MU::Master.removeInstanceFromEtcHosts(server_obj.mu_name) if !noop and server_obj
|
|
1586
|
+
targets.each { |target|
|
|
1587
|
+
next if !target.match(/^\d+\.\d+\.\d+\.\d+$/)
|
|
1588
|
+
MU::Master.removeIPFromSSHKnownHosts(target, noop: noop)
|
|
2257
1589
|
}
|
|
2258
1590
|
end
|
|
2259
1591
|
|
|
2260
|
-
|
|
1592
|
+
on_retry = Proc.new {
|
|
1593
|
+
instance = MU::Cloud::AWS.ec2(credentials: credentials, region: region).describe_instances(instance_ids: [instance.instance_id]).reservations.first.instances.first
|
|
1594
|
+
if instance.state.name == "terminated"
|
|
1595
|
+
MU.log "#{instance.instance_id}#{server_obj ? " ("+server_obj.mu_name+")" : ""} has already been terminated, skipping"
|
|
1596
|
+
MU::MommaCat.unlock(".cleanup-"+id)
|
|
1597
|
+
return
|
|
1598
|
+
end
|
|
1599
|
+
}
|
|
2261
1600
|
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
1601
|
+
loop_if = Proc.new {
|
|
1602
|
+
instance = MU::Cloud::AWS.ec2(credentials: credentials, region: region).describe_instances(instance_ids: [instance.instance_id]).reservations.first.instances.first
|
|
1603
|
+
instance.state.name != "terminated"
|
|
2265
1604
|
}
|
|
2266
1605
|
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
if !noop
|
|
2277
|
-
begin
|
|
2278
|
-
MU::Cloud::AWS.ec2(credentials: credentials, region: region).modify_instance_attribute(
|
|
2279
|
-
instance_id: instance.instance_id,
|
|
2280
|
-
disable_api_termination: {value: false}
|
|
2281
|
-
)
|
|
2282
|
-
MU::Cloud::AWS.ec2(credentials: credentials, region: region).terminate_instances(instance_ids: [instance.instance_id])
|
|
2283
|
-
# Small race window here with the state changing from under us
|
|
2284
|
-
rescue Aws::EC2::Errors::IncorrectInstanceState => e
|
|
2285
|
-
resp = MU::Cloud::AWS.ec2(credentials: credentials, region: region).describe_instances(instance_ids: [id])
|
|
2286
|
-
if !resp.nil? and !resp.reservations.nil? and !resp.reservations.first.nil?
|
|
2287
|
-
instance = resp.reservations.first.instances.first
|
|
2288
|
-
if !instance.nil? and instance.state.name != "terminated" and instance.state.name != "terminating"
|
|
2289
|
-
sleep 5
|
|
2290
|
-
retry
|
|
2291
|
-
end
|
|
2292
|
-
end
|
|
2293
|
-
rescue Aws::EC2::Errors::InternalError => e
|
|
2294
|
-
MU.log "Error #{e.inspect} while Terminating instance #{instance.instance_id} (#{name}), retrying", MU::WARN, details: e.inspect
|
|
2295
|
-
sleep 5
|
|
2296
|
-
retry
|
|
2297
|
-
end
|
|
2298
|
-
end
|
|
2299
|
-
end
|
|
2300
|
-
while instance.state.name != "terminated" and !noop
|
|
2301
|
-
sleep 30
|
|
2302
|
-
instance_response = MU::Cloud::AWS.ec2(credentials: credentials, region: region).describe_instances(instance_ids: [instance.instance_id])
|
|
2303
|
-
instance = instance_response.reservations.first.instances.first
|
|
2304
|
-
end
|
|
2305
|
-
MU.log "#{instance.instance_id} (#{name}) terminated" if !noop
|
|
1606
|
+
MU.log "Terminating #{instance.instance_id}#{server_obj ? " ("+server_obj.mu_name+")" : ""}"
|
|
1607
|
+
if !noop
|
|
1608
|
+
MU.retrier([Aws::EC2::Errors::IncorrectInstanceState, Aws::EC2::Errors::InternalError], wait: 30, max: 60, loop_if: loop_if, on_retry: on_retry) {
|
|
1609
|
+
MU::Cloud::AWS.ec2(credentials: credentials, region: region).modify_instance_attribute(
|
|
1610
|
+
instance_id: instance.instance_id,
|
|
1611
|
+
disable_api_termination: {value: false}
|
|
1612
|
+
)
|
|
1613
|
+
MU::Cloud::AWS.ec2(credentials: credentials, region: region).terminate_instances(instance_ids: [instance.instance_id])
|
|
1614
|
+
}
|
|
2306
1615
|
end
|
|
1616
|
+
|
|
1617
|
+
MU.log "#{instance.instance_id}#{server_obj ? " ("+server_obj.mu_name+")" : ""} terminated" if !noop
|
|
1618
|
+
begin
|
|
1619
|
+
MU::MommaCat.unlock(".cleanup-"+id)
|
|
1620
|
+
rescue Errno::ENOENT => e
|
|
1621
|
+
MU.log "No lock for terminating instance #{id} due to missing metadata", MU::DEBUG
|
|
1622
|
+
end
|
|
1623
|
+
|
|
2307
1624
|
end
|
|
2308
1625
|
|
|
2309
1626
|
# Return a BoK-style config hash describing a NAT instance. We use this
|
|
@@ -2360,26 +1677,7 @@ module MU
|
|
|
2360
1677
|
"type" => "object"
|
|
2361
1678
|
}
|
|
2362
1679
|
},
|
|
2363
|
-
"ingress_rules" =>
|
|
2364
|
-
"items" => {
|
|
2365
|
-
"properties" => {
|
|
2366
|
-
"sgs" => {
|
|
2367
|
-
"type" => "array",
|
|
2368
|
-
"items" => {
|
|
2369
|
-
"description" => "Other AWS Security Groups; resources that are associated with this group will have this rule applied to their traffic",
|
|
2370
|
-
"type" => "string"
|
|
2371
|
-
}
|
|
2372
|
-
},
|
|
2373
|
-
"lbs" => {
|
|
2374
|
-
"type" => "array",
|
|
2375
|
-
"items" => {
|
|
2376
|
-
"description" => "AWS Load Balancers which will have this rule applied to their traffic",
|
|
2377
|
-
"type" => "string"
|
|
2378
|
-
}
|
|
2379
|
-
}
|
|
2380
|
-
}
|
|
2381
|
-
}
|
|
2382
|
-
},
|
|
1680
|
+
"ingress_rules" => MU::Cloud.resourceClass("AWS", "FirewallRule").ingressRuleAddtlSchema,
|
|
2383
1681
|
"ssh_user" => {
|
|
2384
1682
|
"type" => "string",
|
|
2385
1683
|
"default" => "root",
|
|
@@ -2447,8 +1745,7 @@ module MU
|
|
|
2447
1745
|
|
|
2448
1746
|
MU::Cloud.availableClouds.each { |cloud|
|
|
2449
1747
|
next if cloud == "AWS"
|
|
2450
|
-
|
|
2451
|
-
foreign_types = (cloudbase.listInstanceTypes).values.first
|
|
1748
|
+
foreign_types = (MU::Cloud.cloudClass(cloud).listInstanceTypes).values.first
|
|
2452
1749
|
if foreign_types.size == 1
|
|
2453
1750
|
foreign_types = foreign_types.values.first
|
|
2454
1751
|
end
|
|
@@ -2479,6 +1776,45 @@ module MU
|
|
|
2479
1776
|
size
|
|
2480
1777
|
end
|
|
2481
1778
|
|
|
1779
|
+
# Boilerplate generation of an instance role
|
|
1780
|
+
# @param server [Hash]: The BoK-style config hash for a +Server+ or +ServerPool+
|
|
1781
|
+
# @param configurator [MU::Config]
|
|
1782
|
+
def self.generateStandardRole(server, configurator)
|
|
1783
|
+
role = {
|
|
1784
|
+
"name" => server["name"],
|
|
1785
|
+
"credentials" => server["credentials"],
|
|
1786
|
+
"can_assume" => [
|
|
1787
|
+
{
|
|
1788
|
+
"entity_id" => "ec2.amazonaws.com",
|
|
1789
|
+
"entity_type" => "service"
|
|
1790
|
+
}
|
|
1791
|
+
],
|
|
1792
|
+
"policies" => [
|
|
1793
|
+
{
|
|
1794
|
+
"name" => "MuSecrets",
|
|
1795
|
+
"permissions" => ["s3:GetObject"],
|
|
1796
|
+
"targets" => [
|
|
1797
|
+
{
|
|
1798
|
+
"identifier" => 'arn:'+(MU::Cloud::AWS.isGovCloud?(server['region']) ? "aws-us-gov" : "aws")+':s3:::'+MU::Cloud::AWS.adminBucketName(server['credentials'])+'/Mu_CA.pem'
|
|
1799
|
+
}
|
|
1800
|
+
]
|
|
1801
|
+
}
|
|
1802
|
+
]
|
|
1803
|
+
}
|
|
1804
|
+
if server['iam_policies']
|
|
1805
|
+
role['iam_policies'] = server['iam_policies'].dup
|
|
1806
|
+
end
|
|
1807
|
+
if server['canned_iam_policies']
|
|
1808
|
+
role['import'] = server['canned_iam_policies'].dup
|
|
1809
|
+
end
|
|
1810
|
+
if server['iam_role']
|
|
1811
|
+
# XXX maybe break this down into policies and add those?
|
|
1812
|
+
end
|
|
1813
|
+
|
|
1814
|
+
configurator.insertKitten(role, "roles")
|
|
1815
|
+
MU::Config.addDependency(server, server["name"], "role")
|
|
1816
|
+
end
|
|
1817
|
+
|
|
2482
1818
|
# Cloud-specific pre-processing of {MU::Config::BasketofKittens::servers}, bare and unvalidated.
|
|
2483
1819
|
# @param server [Hash]: The resource to process and validate
|
|
2484
1820
|
# @param configurator [MU::Config]: The overall deployment configurator of which this resource is a member
|
|
@@ -2499,43 +1835,7 @@ module MU
|
|
|
2499
1835
|
ok = false
|
|
2500
1836
|
end
|
|
2501
1837
|
else
|
|
2502
|
-
|
|
2503
|
-
"name" => server["name"],
|
|
2504
|
-
"credentials" => server["credentials"],
|
|
2505
|
-
"can_assume" => [
|
|
2506
|
-
{
|
|
2507
|
-
"entity_id" => "ec2.amazonaws.com",
|
|
2508
|
-
"entity_type" => "service"
|
|
2509
|
-
}
|
|
2510
|
-
],
|
|
2511
|
-
"policies" => [
|
|
2512
|
-
{
|
|
2513
|
-
"name" => "MuSecrets",
|
|
2514
|
-
"permissions" => ["s3:GetObject"],
|
|
2515
|
-
"targets" => [
|
|
2516
|
-
{
|
|
2517
|
-
"identifier" => 'arn:'+(MU::Cloud::AWS.isGovCloud?(server['region']) ? "aws-us-gov" : "aws")+':s3:::'+MU::Cloud::AWS.adminBucketName(server['credentials'])+'/Mu_CA.pem'
|
|
2518
|
-
}
|
|
2519
|
-
]
|
|
2520
|
-
}
|
|
2521
|
-
]
|
|
2522
|
-
}
|
|
2523
|
-
if server['iam_policies']
|
|
2524
|
-
role['iam_policies'] = server['iam_policies'].dup
|
|
2525
|
-
end
|
|
2526
|
-
if server['canned_iam_policies']
|
|
2527
|
-
role['import'] = server['canned_iam_policies'].dup
|
|
2528
|
-
end
|
|
2529
|
-
if server['iam_role']
|
|
2530
|
-
# XXX maybe break this down into policies and add those?
|
|
2531
|
-
end
|
|
2532
|
-
|
|
2533
|
-
configurator.insertKitten(role, "roles")
|
|
2534
|
-
server["dependencies"] ||= []
|
|
2535
|
-
server["dependencies"] << {
|
|
2536
|
-
"type" => "role",
|
|
2537
|
-
"name" => server["name"]
|
|
2538
|
-
}
|
|
1838
|
+
generateStandardRole(server, configurator)
|
|
2539
1839
|
end
|
|
2540
1840
|
if !server['create_image'].nil?
|
|
2541
1841
|
if server['create_image'].has_key?('copy_to_regions') and
|
|
@@ -2547,12 +1847,12 @@ module MU
|
|
|
2547
1847
|
end
|
|
2548
1848
|
end
|
|
2549
1849
|
|
|
2550
|
-
server['
|
|
1850
|
+
server['image_id'] ||= server['ami_id']
|
|
2551
1851
|
|
|
2552
|
-
if server['
|
|
1852
|
+
if server['image_id'].nil?
|
|
2553
1853
|
img_id = MU::Cloud.getStockImage("AWS", platform: server['platform'], region: server['region'])
|
|
2554
1854
|
if img_id
|
|
2555
|
-
server['
|
|
1855
|
+
server['image_id'] = configurator.getTail("server"+server['name']+"AMI", value: img_id, prettyname: "server"+server['name']+"AMI", cloudtype: "AWS::EC2::Image::Id")
|
|
2556
1856
|
else
|
|
2557
1857
|
MU.log "No AMI specified for #{server['name']} and no default available for platform #{server['platform']} in region #{server['region']}", MU::ERR, details: server
|
|
2558
1858
|
ok = false
|
|
@@ -2561,22 +1861,13 @@ module MU
|
|
|
2561
1861
|
|
|
2562
1862
|
if !server["loadbalancers"].nil?
|
|
2563
1863
|
server["loadbalancers"].each { |lb|
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
"name" => lb["concurrent_load_balancer"]
|
|
2568
|
-
}
|
|
1864
|
+
lb["name"] ||= lb["concurrent_load_balancer"]
|
|
1865
|
+
if lb["name"]
|
|
1866
|
+
MU::Config.addDependency(server, lb["name"], "loadbalancer")
|
|
2569
1867
|
end
|
|
2570
1868
|
}
|
|
2571
1869
|
end
|
|
2572
1870
|
|
|
2573
|
-
if !server["vpc"].nil?
|
|
2574
|
-
if server["vpc"]["subnet_name"].nil? and server["vpc"]["subnet_id"].nil? and server["vpc"]["subnet_pref"].nil?
|
|
2575
|
-
MU.log "A server VPC block must specify a target subnet", MU::ERR
|
|
2576
|
-
ok = false
|
|
2577
|
-
end
|
|
2578
|
-
end
|
|
2579
|
-
|
|
2580
1871
|
ok
|
|
2581
1872
|
end
|
|
2582
1873
|
|
|
@@ -2599,23 +1890,24 @@ module MU
|
|
|
2599
1890
|
# @param volume [OpenStruct]: The cloud provider's description of the volume.
|
|
2600
1891
|
# @param region [String]: The cloud provider region
|
|
2601
1892
|
# @return [void]
|
|
2602
|
-
def self.delete_volume(volume, noop, skipsnapshots, region: MU.curRegion, credentials: nil)
|
|
1893
|
+
def self.delete_volume(volume, noop, skipsnapshots, region: MU.curRegion, credentials: nil, deploy_id: MU.deploy_id)
|
|
2603
1894
|
if !volume.nil?
|
|
2604
1895
|
resp = MU::Cloud::AWS.ec2(region: region, credentials: credentials).describe_volumes(volume_ids: [volume.volume_id])
|
|
2605
1896
|
volume = resp.data.volumes.first
|
|
2606
1897
|
end
|
|
2607
|
-
name =
|
|
1898
|
+
name = nil
|
|
2608
1899
|
volume.tags.each { |tag|
|
|
2609
1900
|
name = tag.value if tag.key == "Name"
|
|
2610
1901
|
}
|
|
1902
|
+
name ||= volume.volume_id
|
|
2611
1903
|
|
|
2612
1904
|
MU.log("Deleting volume #{volume.volume_id} (#{name})")
|
|
2613
1905
|
if !noop
|
|
2614
1906
|
if !skipsnapshots
|
|
2615
1907
|
if !name.nil? and !name.empty?
|
|
2616
|
-
desc = "#{
|
|
1908
|
+
desc = "#{deploy_id}-MUfinal (#{name})"
|
|
2617
1909
|
else
|
|
2618
|
-
desc = "#{
|
|
1910
|
+
desc = "#{deploy_id}-MUfinal"
|
|
2619
1911
|
end
|
|
2620
1912
|
|
|
2621
1913
|
begin
|
|
@@ -2630,31 +1922,414 @@ module MU
|
|
|
2630
1922
|
end
|
|
2631
1923
|
end
|
|
2632
1924
|
|
|
2633
|
-
retries = 0
|
|
2634
1925
|
begin
|
|
2635
|
-
MU::
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
sleep 30
|
|
2639
|
-
retry
|
|
2640
|
-
rescue Aws::EC2::Errors::InvalidVolumeNotFound
|
|
2641
|
-
MU.log "Volume #{volume.volume_id} (#{name}) disappeared before I could remove it!", MU::WARN
|
|
1926
|
+
MU.retrier([Aws::EC2::Errors::IncorrectState, Aws::EC2::Errors::VolumeInUse], ignoreme: [Aws::EC2::Errors::InvalidVolumeNotFound], wait: 30, max: 10){
|
|
1927
|
+
MU::Cloud::AWS.ec2(region: region, credentials: credentials).delete_volume(volume_id: volume.volume_id)
|
|
1928
|
+
}
|
|
2642
1929
|
rescue Aws::EC2::Errors::VolumeInUse
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
1930
|
+
MU.log "Failed to delete #{name}", MU::ERR
|
|
1931
|
+
end
|
|
1932
|
+
|
|
1933
|
+
end
|
|
1934
|
+
end
|
|
1935
|
+
private_class_method :delete_volume
|
|
1936
|
+
|
|
1937
|
+
# Given some combination of a base image, BoK-configured storage, and
|
|
1938
|
+
# ephemeral devices, return the structure passed to EC2 to declare
|
|
1939
|
+
# block devicde mappings.
|
|
1940
|
+
# @param image_id [String]
|
|
1941
|
+
# @param storage [Array]
|
|
1942
|
+
# @param add_ephemeral [Boolean]
|
|
1943
|
+
# @param region [String]
|
|
1944
|
+
# @param credentials [String]
|
|
1945
|
+
def self.configureBlockDevices(image_id: nil, storage: nil, add_ephemeral: true, region: MU.myRegion, credentials: nil)
|
|
1946
|
+
ext_disks = {}
|
|
1947
|
+
|
|
1948
|
+
# Figure out which devices are embedded in the AMI already.
|
|
1949
|
+
if image_id
|
|
1950
|
+
image = MU::Cloud::AWS.ec2(region: region, credentials: credentials).describe_images(image_ids: [image_id]).images.first
|
|
1951
|
+
if !image.block_device_mappings.nil?
|
|
1952
|
+
image.block_device_mappings.each { |disk|
|
|
1953
|
+
if !disk.device_name.nil? and !disk.device_name.empty? and !disk.ebs.nil? and !disk.ebs.empty?
|
|
1954
|
+
ext_disks[disk.device_name] = MU.structToHash(disk.ebs)
|
|
1955
|
+
end
|
|
1956
|
+
}
|
|
1957
|
+
end
|
|
1958
|
+
end
|
|
1959
|
+
|
|
1960
|
+
configured_storage = []
|
|
1961
|
+
if storage
|
|
1962
|
+
storage.each { |vol|
|
|
1963
|
+
# Drop the "encrypted" flag if a snapshot for this device exists
|
|
1964
|
+
# in the AMI, even if they both agree about the value of said
|
|
1965
|
+
# flag. Apparently that's a thing now.
|
|
1966
|
+
if ext_disks.has_key?(vol["device"])
|
|
1967
|
+
if ext_disks[vol["device"]].has_key?(:snapshot_id)
|
|
1968
|
+
vol.delete("encrypted")
|
|
1969
|
+
end
|
|
1970
|
+
end
|
|
1971
|
+
mapping, _cfm_mapping = MU::Cloud::AWS::Server.convertBlockDeviceMapping(vol)
|
|
1972
|
+
configured_storage << mapping
|
|
1973
|
+
}
|
|
1974
|
+
end
|
|
1975
|
+
|
|
1976
|
+
configured_storage.concat(@ephemeral_mappings) if add_ephemeral
|
|
1977
|
+
|
|
1978
|
+
configured_storage
|
|
1979
|
+
end
|
|
1980
|
+
|
|
1981
|
+
# Return all of the IP addresses, public and private, from all of our
|
|
1982
|
+
# network interfaces.
|
|
1983
|
+
# @return [Array<String>]
|
|
1984
|
+
def listIPs
|
|
1985
|
+
MU::Cloud::AWS::Server.getAddresses(cloud_desc).first
|
|
1986
|
+
end
|
|
1987
|
+
|
|
1988
|
+
private
|
|
1989
|
+
|
|
1990
|
+
def bootstrapGroomer
|
|
1991
|
+
if (@config['groom'].nil? or @config['groom']) and !@groomer.haveBootstrapped?
|
|
1992
|
+
MU.retrier([BootstrapTempFail], wait: 45) {
|
|
1993
|
+
if windows?
|
|
1994
|
+
# kick off certificate generation early; WinRM will need it
|
|
1995
|
+
@deploy.nodeSSLCerts(self)
|
|
1996
|
+
@deploy.nodeSSLCerts(self, true) if @config.has_key?("basis")
|
|
1997
|
+
session = getWinRMSession(50, 60, reboot_on_problems: true)
|
|
1998
|
+
initialWinRMTasks(session)
|
|
1999
|
+
begin
|
|
2000
|
+
session.close
|
|
2001
|
+
rescue StandardError
|
|
2002
|
+
# session.close is allowed to fail- we're probably rebooting
|
|
2003
|
+
end
|
|
2651
2004
|
else
|
|
2652
|
-
|
|
2005
|
+
session = getSSHSession(40, 30)
|
|
2006
|
+
initialSSHTasks(session)
|
|
2007
|
+
end
|
|
2008
|
+
}
|
|
2009
|
+
end
|
|
2010
|
+
|
|
2011
|
+
# See if this node already exists in our config management. If it
|
|
2012
|
+
# does, we're done.
|
|
2013
|
+
|
|
2014
|
+
if MU.inGem?
|
|
2015
|
+
MU.log "Deploying from a gem, not grooming"
|
|
2016
|
+
elsif @config['groom'].nil? or @config['groom']
|
|
2017
|
+
if @groomer.haveBootstrapped?
|
|
2018
|
+
MU.log "Node #{@mu_name} has already been bootstrapped, skipping groomer setup.", MU::NOTICE
|
|
2019
|
+
else
|
|
2020
|
+
begin
|
|
2021
|
+
@groomer.bootstrap
|
|
2022
|
+
rescue MU::Groomer::RunError
|
|
2023
|
+
return false
|
|
2653
2024
|
end
|
|
2654
2025
|
end
|
|
2026
|
+
@groomer.saveDeployData
|
|
2027
|
+
end
|
|
2028
|
+
|
|
2029
|
+
true
|
|
2030
|
+
end
|
|
2031
|
+
|
|
2032
|
+
def saveCredentials(win_admin_password = nil)
|
|
2033
|
+
ec2config_password = nil
|
|
2034
|
+
sshd_password = nil
|
|
2035
|
+
if windows?
|
|
2036
|
+
if @config['use_cloud_provider_windows_password']
|
|
2037
|
+
win_admin_password ||= getWindowsAdminPassword
|
|
2038
|
+
elsif @config['windows_auth_vault'] and !@config['windows_auth_vault'].empty?
|
|
2039
|
+
if @config["windows_auth_vault"].has_key?("password_field")
|
|
2040
|
+
win_admin_password ||= @groomer.getSecret(
|
|
2041
|
+
vault: @config['windows_auth_vault']['vault'],
|
|
2042
|
+
item: @config['windows_auth_vault']['item'],
|
|
2043
|
+
field: @config["windows_auth_vault"]["password_field"]
|
|
2044
|
+
)
|
|
2045
|
+
else
|
|
2046
|
+
win_admin_password ||= getWindowsAdminPassword
|
|
2047
|
+
end
|
|
2048
|
+
|
|
2049
|
+
if @config["windows_auth_vault"].has_key?("ec2config_password_field")
|
|
2050
|
+
ec2config_password = @groomer.getSecret(
|
|
2051
|
+
vault: @config['windows_auth_vault']['vault'],
|
|
2052
|
+
item: @config['windows_auth_vault']['item'],
|
|
2053
|
+
field: @config["windows_auth_vault"]["ec2config_password_field"]
|
|
2054
|
+
)
|
|
2055
|
+
end
|
|
2056
|
+
|
|
2057
|
+
if @config["windows_auth_vault"].has_key?("sshd_password_field")
|
|
2058
|
+
sshd_password = @groomer.getSecret(
|
|
2059
|
+
vault: @config['windows_auth_vault']['vault'],
|
|
2060
|
+
item: @config['windows_auth_vault']['item'],
|
|
2061
|
+
field: @config["windows_auth_vault"]["sshd_password_field"]
|
|
2062
|
+
)
|
|
2063
|
+
end
|
|
2064
|
+
end
|
|
2065
|
+
|
|
2066
|
+
win_admin_password ||= MU.generateWindowsPassword
|
|
2067
|
+
ec2config_password ||= MU.generateWindowsPassword
|
|
2068
|
+
sshd_password ||= MU.generateWindowsPassword
|
|
2069
|
+
|
|
2070
|
+
# We're creating the vault here so when we run
|
|
2071
|
+
# MU::Cloud::Server.initialSSHTasks and we need to set the Windows
|
|
2072
|
+
# Admin password we can grab it from said vault.
|
|
2073
|
+
creds = {
|
|
2074
|
+
"username" => @config['windows_admin_username'],
|
|
2075
|
+
"password" => win_admin_password,
|
|
2076
|
+
"ec2config_username" => "ec2config",
|
|
2077
|
+
"ec2config_password" => ec2config_password,
|
|
2078
|
+
"sshd_username" => "sshd_service",
|
|
2079
|
+
"sshd_password" => sshd_password
|
|
2080
|
+
}
|
|
2081
|
+
@groomer.saveSecret(vault: @mu_name, item: "windows_credentials", data: creds, permissions: "name:#{@mu_name}")
|
|
2082
|
+
end
|
|
2083
|
+
end
|
|
2084
|
+
|
|
2085
|
+
def haveElasticIP?
|
|
2086
|
+
if !cloud_desc.public_ip_address.nil?
|
|
2087
|
+
begin
|
|
2088
|
+
resp = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_addresses(public_ips: [cloud_desc.public_ip_address])
|
|
2089
|
+
if resp.addresses.size > 0 and resp.addresses.first.instance_id == @cloud_id
|
|
2090
|
+
return true
|
|
2091
|
+
end
|
|
2092
|
+
rescue Aws::EC2::Errors::InvalidAddressNotFound
|
|
2093
|
+
# XXX this is ok to ignore, it means the public IP isn't Elastic
|
|
2094
|
+
end
|
|
2095
|
+
end
|
|
2096
|
+
|
|
2097
|
+
false
|
|
2098
|
+
end
|
|
2099
|
+
|
|
2100
|
+
def configureNetworking
|
|
2101
|
+
if !@config['static_ip'].nil?
|
|
2102
|
+
if !@config['static_ip']['ip'].nil?
|
|
2103
|
+
MU::Cloud::AWS::Server.associateElasticIp(@cloud_id, classic: @vpc.nil?, ip: @config['static_ip']['ip'])
|
|
2104
|
+
elsif !haveElasticIP?
|
|
2105
|
+
MU::Cloud::AWS::Server.associateElasticIp(@cloud_id, classic: @vpc.nil?)
|
|
2106
|
+
end
|
|
2107
|
+
end
|
|
2108
|
+
|
|
2109
|
+
if !@vpc.nil? and @config.has_key?("vpc")
|
|
2110
|
+
subnet = @vpc.getSubnet(cloud_id: cloud_desc.subnet_id)
|
|
2111
|
+
|
|
2112
|
+
_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'])
|
|
2114
|
+
raise MuError, "#{@mu_name} is in a private subnet (#{subnet}), but has no bastion host configured, and I have no other route to it"
|
|
2115
|
+
end
|
|
2116
|
+
|
|
2117
|
+
# If we've asked for additional subnets (and this @config is not a
|
|
2118
|
+
# member of a Server Pool, which has different semantics), create
|
|
2119
|
+
# extra interfaces to accomodate.
|
|
2120
|
+
if !@config['vpc']['subnets'].nil? and @config['basis'].nil?
|
|
2121
|
+
device_index = 1
|
|
2122
|
+
mySubnets.each { |s|
|
|
2123
|
+
next if s.cloud_id == cloud_desc.subnet_id
|
|
2124
|
+
|
|
2125
|
+
if cloud_desc.placement.availability_zone != s.az
|
|
2126
|
+
MU.log "Cannot create interface in subnet #{s.to_s} for #{@mu_name} due to AZ mismatch", MU::WARN
|
|
2127
|
+
next
|
|
2128
|
+
end
|
|
2129
|
+
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
|
|
2131
|
+
MU::Cloud::AWS.createStandardTags(
|
|
2132
|
+
iface.network_interface_id,
|
|
2133
|
+
region: @config['region'],
|
|
2134
|
+
credentials: @config['credentials'],
|
|
2135
|
+
optional: @config['optional_tags'],
|
|
2136
|
+
nametag: @mu_name+"-ETH"+device_index.to_s,
|
|
2137
|
+
othertags: @config['tags']
|
|
2138
|
+
)
|
|
2139
|
+
|
|
2140
|
+
MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).attach_network_interface(
|
|
2141
|
+
network_interface_id: iface.network_interface_id,
|
|
2142
|
+
instance_id: cloud_desc.instance_id,
|
|
2143
|
+
device_index: device_index
|
|
2144
|
+
)
|
|
2145
|
+
device_index = device_index + 1
|
|
2146
|
+
}
|
|
2147
|
+
cloud_desc(use_cache: false)
|
|
2148
|
+
end
|
|
2149
|
+
end
|
|
2150
|
+
|
|
2151
|
+
[:private_dns_name, :public_dns_name, :private_ip_address, :public_ip_address].each { |field|
|
|
2152
|
+
@config[field.to_s] = cloud_desc.send(field)
|
|
2153
|
+
}
|
|
2154
|
+
|
|
2155
|
+
if !@config['add_private_ips'].nil?
|
|
2156
|
+
cloud_desc.network_interfaces.each { |int|
|
|
2157
|
+
if int.private_ip_address == cloud_desc.private_ip_address and int.private_ip_addresses.size < (@config['add_private_ips'] + 1)
|
|
2158
|
+
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(
|
|
2160
|
+
network_interface_id: int.network_interface_id,
|
|
2161
|
+
secondary_private_ip_address_count: @config['add_private_ips'],
|
|
2162
|
+
allow_reassignment: false
|
|
2163
|
+
)
|
|
2164
|
+
end
|
|
2165
|
+
}
|
|
2166
|
+
end
|
|
2167
|
+
end
|
|
2168
|
+
|
|
2169
|
+
def tagVolumes
|
|
2170
|
+
volumes = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_volumes(filters: [name: "attachment.instance-id", values: [@cloud_id]])
|
|
2171
|
+
volumes.each { |vol|
|
|
2172
|
+
vol.volumes.each { |volume|
|
|
2173
|
+
volume.attachments.each { |attachment|
|
|
2174
|
+
MU::Cloud::AWS.createStandardTags(
|
|
2175
|
+
attachment.volume_id,
|
|
2176
|
+
region: @config['region'],
|
|
2177
|
+
credentials: @config['credentials'],
|
|
2178
|
+
optional: @config['optional_tags'],
|
|
2179
|
+
nametag: ["/dev/sda", "/dev/sda1"].include?(attachment.device) ? "ROOT-"+@mu_name : @mu_name+"-"+attachment.device.upcase,
|
|
2180
|
+
othertags: @config['tags']
|
|
2181
|
+
)
|
|
2182
|
+
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
2185
|
+
}
|
|
2186
|
+
end
|
|
2187
|
+
|
|
2188
|
+
# If we came up via AutoScale, the Alarm module won't have had our
|
|
2189
|
+
# instance ID to associate us with itself. So invoke that here.
|
|
2190
|
+
# XXX might be possible to do this with regular alarm resources and
|
|
2191
|
+
# dependencies now
|
|
2192
|
+
def setAlarms
|
|
2193
|
+
if !@config['basis'].nil? and @config["alarms"] and !@config["alarms"].empty?
|
|
2194
|
+
@config["alarms"].each { |alarm|
|
|
2195
|
+
alarm_obj = MU::MommaCat.findStray(
|
|
2196
|
+
"AWS",
|
|
2197
|
+
"alarms",
|
|
2198
|
+
region: @config["region"],
|
|
2199
|
+
deploy_id: @deploy.deploy_id,
|
|
2200
|
+
name: alarm['name']
|
|
2201
|
+
).first
|
|
2202
|
+
alarm["dimensions"] = [{:name => "InstanceId", :value => @cloud_id}]
|
|
2203
|
+
|
|
2204
|
+
if alarm["enable_notifications"]
|
|
2205
|
+
# XXX vile, this should be a sibling resource generated by the
|
|
2206
|
+
# parser
|
|
2207
|
+
topic_arn = MU::Cloud.resourceClass("AWS", "Notification").createTopic(alarm["notification_group"], region: @config["region"], credentials: @config['credentials'])
|
|
2208
|
+
MU::Cloud.resourceClass("AWS", "Notification").subscribe(topic_arn, alarm["notification_endpoint"], alarm["notification_type"], region: @config["region"], credentials: @config["credentials"])
|
|
2209
|
+
alarm["alarm_actions"] = [topic_arn]
|
|
2210
|
+
alarm["ok_actions"] = [topic_arn]
|
|
2211
|
+
end
|
|
2212
|
+
|
|
2213
|
+
alarm_name = alarm_obj ? alarm_obj.cloud_id : "#{@mu_name}-#{alarm['name']}".upcase
|
|
2214
|
+
|
|
2215
|
+
MU::Cloud.resourceClass("AWS", "Alarm").setAlarm(
|
|
2216
|
+
name: alarm_name,
|
|
2217
|
+
ok_actions: alarm["ok_actions"],
|
|
2218
|
+
alarm_actions: alarm["alarm_actions"],
|
|
2219
|
+
insufficient_data_actions: alarm["no_data_actions"],
|
|
2220
|
+
metric_name: alarm["metric_name"],
|
|
2221
|
+
namespace: alarm["namespace"],
|
|
2222
|
+
statistic: alarm["statistic"],
|
|
2223
|
+
dimensions: alarm["dimensions"],
|
|
2224
|
+
period: alarm["period"],
|
|
2225
|
+
unit: alarm["unit"],
|
|
2226
|
+
evaluation_periods: alarm["evaluation_periods"],
|
|
2227
|
+
threshold: alarm["threshold"],
|
|
2228
|
+
comparison_operator: alarm["comparison_operator"],
|
|
2229
|
+
region: @config["region"],
|
|
2230
|
+
credentials: @config['credentials']
|
|
2231
|
+
)
|
|
2232
|
+
}
|
|
2233
|
+
end
|
|
2234
|
+
end
|
|
2235
|
+
|
|
2236
|
+
# We have issues sometimes where our dns_records are pointing at the wrong node name and IP address.
|
|
2237
|
+
|
|
2238
|
+
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
|
|
2243
|
+
}
|
|
2244
|
+
MU.log "Adding S3 read permissions to #{@mu_name}'s IAM profile", MU::NOTICE, details: s3_objs
|
|
2245
|
+
role.cloudobj.injectPolicyTargets("MuSecrets", s3_objs)
|
|
2246
|
+
|
|
2247
|
+
@config['iam_role'] = role.mu_name
|
|
2248
|
+
role.cloudobj.createInstanceProfile
|
|
2249
|
+
|
|
2250
|
+
elsif @config['iam_role'].nil?
|
|
2251
|
+
raise MuError, "#{@mu_name} has generate_iam_role set to false, but no iam_role assigned."
|
|
2252
|
+
end
|
|
2253
|
+
|
|
2254
|
+
if !@config["iam_role"].nil?
|
|
2255
|
+
if arn
|
|
2256
|
+
return {arn: arn}
|
|
2257
|
+
else
|
|
2258
|
+
return {name: @config["iam_role"]}
|
|
2259
|
+
end
|
|
2260
|
+
end
|
|
2261
|
+
|
|
2262
|
+
nil
|
|
2263
|
+
end
|
|
2264
|
+
|
|
2265
|
+
def setDeleteOntermination(device, delete_on_termination = false)
|
|
2266
|
+
mappings = MU.structToHash(cloud_desc.block_device_mappings)
|
|
2267
|
+
mappings.each { |vol|
|
|
2268
|
+
if vol[:ebs]
|
|
2269
|
+
vol[:ebs].delete(:attach_time)
|
|
2270
|
+
vol[:ebs].delete(:status)
|
|
2271
|
+
end
|
|
2272
|
+
if vol[:device_name] == device
|
|
2273
|
+
if vol[:ebs][:delete_on_termination] != delete_on_termination
|
|
2274
|
+
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(
|
|
2277
|
+
instance_id: @cloud_id,
|
|
2278
|
+
block_device_mappings: mappings
|
|
2279
|
+
)
|
|
2280
|
+
end
|
|
2281
|
+
return true
|
|
2282
|
+
end
|
|
2283
|
+
}
|
|
2284
|
+
|
|
2285
|
+
false
|
|
2286
|
+
end
|
|
2287
|
+
|
|
2288
|
+
def createImage
|
|
2289
|
+
img_cfg = @config['create_image']
|
|
2290
|
+
# Scrub things that don't belong on an AMI
|
|
2291
|
+
session = windows? ? getWinRMSession : getSSHSession
|
|
2292
|
+
sudo = purgecmd = ""
|
|
2293
|
+
sudo = "sudo" if @config['ssh_user'] != "root"
|
|
2294
|
+
if windows?
|
|
2295
|
+
purgecmd = "rm -rf /cygdrive/c/mu_installed_chef"
|
|
2296
|
+
else
|
|
2297
|
+
purgecmd = "rm -rf /opt/mu_installed_chef"
|
|
2298
|
+
end
|
|
2299
|
+
if img_cfg['image_then_destroy']
|
|
2300
|
+
if windows?
|
|
2301
|
+
purgecmd = "rm -rf /cygdrive/c/chef/ /home/#{@config['windows_admin_username']}/.ssh/authorized_keys /home/Administrator/.ssh/authorized_keys /cygdrive/c/mu-installer-ran-updates /cygdrive/c/mu_installed_chef"
|
|
2302
|
+
# session.exec!("powershell -Command \"& {(Get-WmiObject -Class Win32_Product -Filter \"Name='UniversalForwarder'\").Uninstall()}\"")
|
|
2303
|
+
else
|
|
2304
|
+
purgecmd = "#{sudo} rm -rf /var/lib/cloud/instances/i-* /root/.ssh/authorized_keys /etc/ssh/ssh_host_*key* /etc/chef /etc/opscode/* /.mu-installer-ran-updates /var/chef /opt/mu_installed_chef /opt/chef ; #{sudo} sed -i 's/^HOSTNAME=.*//' /etc/sysconfig/network"
|
|
2305
|
+
end
|
|
2306
|
+
end
|
|
2307
|
+
if windows?
|
|
2308
|
+
session.run(purgecmd)
|
|
2309
|
+
else
|
|
2310
|
+
session.exec!(purgecmd)
|
|
2311
|
+
end
|
|
2312
|
+
session.close
|
|
2313
|
+
ami_ids = MU::Cloud::AWS::Server.createImage(
|
|
2314
|
+
name: @mu_name,
|
|
2315
|
+
instance_id: @cloud_id,
|
|
2316
|
+
storage: @config['storage'],
|
|
2317
|
+
exclude_storage: img_cfg['image_exclude_storage'],
|
|
2318
|
+
copy_to_regions: img_cfg['copy_to_regions'],
|
|
2319
|
+
make_public: img_cfg['public'],
|
|
2320
|
+
region: @config['region'],
|
|
2321
|
+
tags: @config['tags'],
|
|
2322
|
+
credentials: @config['credentials']
|
|
2323
|
+
)
|
|
2324
|
+
@deploy.notify("images", @config['name'], ami_ids)
|
|
2325
|
+
@config['image_created'] = true
|
|
2326
|
+
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'])
|
|
2330
|
+
destroy
|
|
2655
2331
|
end
|
|
2656
2332
|
end
|
|
2657
|
-
private_class_method :delete_volume
|
|
2658
2333
|
|
|
2659
2334
|
end #class
|
|
2660
2335
|
end #class
|