chef-provisioning-aws 1.3.1 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +70 -69
- data/Rakefile +22 -2
- data/lib/chef/provider/aws_auto_scaling_group.rb +3 -2
- data/lib/chef/provider/aws_cache_cluster.rb +3 -2
- data/lib/chef/provider/aws_cache_replication_group.rb +5 -4
- data/lib/chef/provider/aws_cache_subnet_group.rb +5 -4
- data/lib/chef/provider/aws_cloudsearch_domain.rb +163 -0
- data/lib/chef/provider/aws_dhcp_options.rb +9 -6
- data/lib/chef/provider/aws_ebs_volume.rb +7 -3
- data/lib/chef/provider/aws_eip_address.rb +8 -7
- data/lib/chef/provider/aws_image.rb +8 -3
- data/lib/chef/provider/aws_instance.rb +14 -2
- data/lib/chef/provider/aws_key_pair.rb +2 -1
- data/lib/chef/provider/aws_launch_configuration.rb +4 -2
- data/lib/chef/provider/aws_load_balancer.rb +18 -0
- data/lib/chef/provider/aws_network_acl.rb +6 -2
- data/lib/chef/provider/aws_network_interface.rb +11 -24
- data/lib/chef/provider/aws_rds_instance.rb +66 -0
- data/lib/chef/provider/aws_rds_subnet_group.rb +89 -0
- data/lib/chef/provider/aws_route_table.rb +42 -23
- data/lib/chef/provider/aws_s3_bucket.rb +32 -8
- data/lib/chef/provider/aws_security_group.rb +11 -4
- data/lib/chef/provider/aws_server_certificate.rb +23 -0
- data/lib/chef/provider/aws_sns_topic.rb +4 -3
- data/lib/chef/provider/aws_sqs_queue.rb +3 -2
- data/lib/chef/provider/aws_subnet.rb +10 -7
- data/lib/chef/provider/aws_vpc.rb +54 -21
- data/lib/chef/provider/aws_vpc_peering_connection.rb +88 -0
- data/lib/chef/provisioning/aws_driver.rb +8 -0
- data/lib/chef/provisioning/aws_driver/aws_provider.rb +45 -76
- data/lib/chef/provisioning/aws_driver/aws_rds_resource.rb +11 -0
- data/lib/chef/provisioning/aws_driver/aws_resource.rb +14 -2
- data/lib/chef/provisioning/aws_driver/aws_resource_with_entry.rb +2 -8
- data/lib/chef/provisioning/aws_driver/aws_taggable.rb +18 -0
- data/lib/chef/provisioning/aws_driver/aws_tagger.rb +61 -0
- data/lib/chef/provisioning/aws_driver/credentials2.rb +51 -0
- data/lib/chef/provisioning/aws_driver/driver.rb +214 -162
- data/lib/chef/provisioning/aws_driver/tagging_strategy/ec2.rb +64 -0
- data/lib/chef/provisioning/aws_driver/tagging_strategy/elb.rb +39 -0
- data/lib/chef/provisioning/aws_driver/tagging_strategy/rds.rb +92 -0
- data/lib/chef/provisioning/aws_driver/tagging_strategy/s3.rb +41 -0
- data/lib/chef/provisioning/aws_driver/version.rb +1 -1
- data/lib/chef/resource/aws_cache_cluster.rb +1 -2
- data/lib/chef/resource/aws_cloudsearch_domain.rb +46 -0
- data/lib/chef/resource/aws_dhcp_options.rb +2 -0
- data/lib/chef/resource/aws_ebs_volume.rb +3 -1
- data/lib/chef/resource/aws_eip_address.rb +0 -3
- data/lib/chef/resource/aws_image.rb +3 -0
- data/lib/chef/resource/aws_instance.rb +7 -2
- data/lib/chef/resource/aws_internet_gateway.rb +2 -0
- data/lib/chef/resource/aws_load_balancer.rb +3 -0
- data/lib/chef/resource/aws_network_acl.rb +2 -0
- data/lib/chef/resource/aws_network_interface.rb +3 -1
- data/lib/chef/resource/aws_rds_instance.rb +42 -0
- data/lib/chef/resource/aws_rds_subnet_group.rb +29 -0
- data/lib/chef/resource/aws_route_table.rb +7 -5
- data/lib/chef/resource/aws_s3_bucket.rb +3 -0
- data/lib/chef/resource/aws_security_group.rb +2 -7
- data/lib/chef/resource/aws_server_certificate.rb +21 -0
- data/lib/chef/resource/aws_subnet.rb +2 -0
- data/lib/chef/resource/aws_vpc.rb +4 -1
- data/lib/chef/resource/aws_vpc_peering_connection.rb +73 -0
- data/spec/acceptance/aws_ebs_volume/nodes/ettores-mbp.lan.json +3 -0
- data/spec/aws_support.rb +25 -8
- data/spec/aws_support/aws_resource_run_wrapper.rb +5 -1
- data/spec/aws_support/deep_matcher/match_values_failure_messages.rb +19 -0
- data/spec/aws_support/matchers/create_an_aws_object.rb +1 -1
- data/spec/aws_support/matchers/destroy_an_aws_object.rb +1 -1
- data/spec/aws_support/matchers/have_aws_object_tags.rb +9 -15
- data/spec/aws_support/matchers/match_an_aws_object.rb +1 -1
- data/spec/aws_support/matchers/update_an_aws_object.rb +1 -1
- data/spec/integration/aws_cloudsearch_domain_spec.rb +31 -0
- data/spec/integration/aws_dhcp_options_spec.rb +73 -0
- data/spec/integration/aws_ebs_volume_spec.rb +97 -0
- data/spec/integration/aws_network_acl_spec.rb +51 -0
- data/spec/integration/aws_network_interface_spec.rb +89 -0
- data/spec/integration/aws_rds_instance_spec.rb +150 -0
- data/spec/integration/aws_rds_subnet_group_spec.rb +105 -0
- data/spec/integration/aws_route_table_spec.rb +94 -7
- data/spec/integration/aws_s3_bucket_spec.rb +88 -0
- data/spec/integration/aws_security_group_spec.rb +47 -0
- data/spec/integration/aws_server_certificate_spec.rb +24 -0
- data/spec/integration/aws_subnet_spec.rb +51 -2
- data/spec/integration/aws_vpc_peering_connection_spec.rb +99 -0
- data/spec/integration/aws_vpc_spec.rb +73 -0
- data/spec/integration/load_balancer_spec.rb +101 -0
- data/spec/integration/machine_image_spec.rb +61 -6
- data/spec/integration/machine_spec.rb +26 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/unit/{aws_driver → chef/provisioning/aws_driver}/credentials_spec.rb +0 -0
- data/spec/unit/chef/provisioning/aws_driver/driver_spec.rb +88 -0
- metadata +63 -20
- data/spec/integration/aws_tagged_items_spec.rb +0 -166
@@ -0,0 +1,51 @@
|
|
1
|
+
require "aws-sdk"
|
2
|
+
require "aws-sdk-core/credentials"
|
3
|
+
require "aws-sdk-core/shared_credentials"
|
4
|
+
require "aws-sdk-core/instance_profile_credentials"
|
5
|
+
require "aws-sdk-core/assume_role_credentials"
|
6
|
+
|
7
|
+
class Chef
|
8
|
+
module Provisioning
|
9
|
+
module AWSDriver
|
10
|
+
|
11
|
+
class LoadCredentialsError < RuntimeError; end
|
12
|
+
|
13
|
+
# Loads the credentials for the AWS SDK V2
|
14
|
+
# Attempts to load credentials in the order specified at http://docs.aws.amazon.com/sdkforruby/api/index.html#Configuration
|
15
|
+
class Credentials2
|
16
|
+
|
17
|
+
attr_reader :profile_name
|
18
|
+
|
19
|
+
# @param [Hash] options
|
20
|
+
# @option options [String] :profile_name (ENV["AWS_DEFAULT_PROFILE"]) The profile name to use
|
21
|
+
# when loading the config from '~/.aws/credentials'. This can be nil.
|
22
|
+
def initialize(options = {})
|
23
|
+
@profile_name = options[:profile_name] || ENV["AWS_DEFAULT_PROFILE"]
|
24
|
+
end
|
25
|
+
|
26
|
+
# Try to load the credentials from an ordered list of sources and return the first one that
|
27
|
+
# can be loaded successfully.
|
28
|
+
def get_credentials
|
29
|
+
shared_creds = ::Aws::SharedCredentials.new(:profile_name => profile_name)
|
30
|
+
instance_profile_creds = ::Aws::InstanceProfileCredentials.new(:retries => 1)
|
31
|
+
|
32
|
+
if ENV["AWS_ACCESS_KEY_ID"] && ENV["AWS_SECRET_ACCESS_KEY"]
|
33
|
+
creds = ::Aws::Credentials.new(
|
34
|
+
ENV["AWS_ACCESS_KEY_ID"],
|
35
|
+
ENV["AWS_SECRET_ACCESS_KEY"],
|
36
|
+
ENV["AWS_SESSION_TOKEN"]
|
37
|
+
)
|
38
|
+
elsif shared_creds.set?
|
39
|
+
creds = shared_creds
|
40
|
+
elsif instance_profile_creds.set?
|
41
|
+
creds = instance_profile_creds
|
42
|
+
else
|
43
|
+
raise LoadCredentialsError.new("Could not load credentials from the environment variables, the .aws/credentials file or the metadata service")
|
44
|
+
end
|
45
|
+
creds
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -11,21 +11,50 @@ require 'chef/provisioning/machine/windows_machine'
|
|
11
11
|
require 'chef/provisioning/machine/unix_machine'
|
12
12
|
require 'chef/provisioning/machine_spec'
|
13
13
|
|
14
|
-
require 'chef/resource/aws_key_pair'
|
15
|
-
require 'chef/resource/aws_instance'
|
16
|
-
require 'chef/resource/aws_image'
|
17
|
-
require 'chef/resource/aws_load_balancer'
|
18
14
|
require 'chef/provisioning/aws_driver/aws_resource'
|
15
|
+
require 'chef/provisioning/aws_driver/tagging_strategy/ec2'
|
16
|
+
require 'chef/provisioning/aws_driver/tagging_strategy/elb'
|
19
17
|
require 'chef/provisioning/aws_driver/version'
|
20
18
|
require 'chef/provisioning/aws_driver/credentials'
|
19
|
+
require 'chef/provisioning/aws_driver/credentials2'
|
20
|
+
require 'chef/provisioning/aws_driver/aws_tagger'
|
21
21
|
|
22
22
|
require 'yaml'
|
23
23
|
require 'aws-sdk-v1'
|
24
|
+
require 'aws-sdk'
|
24
25
|
require 'retryable'
|
25
26
|
require 'ubuntu_ami'
|
26
27
|
|
27
28
|
# loads the entire aws-sdk
|
28
29
|
AWS.eager_autoload!
|
30
|
+
AWS_V2_SERVICES = {"EC2" => "ec2", "S3" => "s3", "ElasticLoadBalancing" => "elb"}
|
31
|
+
Aws.eager_autoload!(:services => AWS_V2_SERVICES.keys)
|
32
|
+
|
33
|
+
# Need to load the resources after the SDK because `aws_sdk_types` can mess
|
34
|
+
# up AWS loading if they are loaded too early
|
35
|
+
require 'chef/resource/aws_key_pair'
|
36
|
+
require 'chef/resource/aws_instance'
|
37
|
+
require 'chef/resource/aws_image'
|
38
|
+
require 'chef/resource/aws_load_balancer'
|
39
|
+
|
40
|
+
# We add the appropriate attributes to the base resources for tagging support
|
41
|
+
class Chef
|
42
|
+
class Resource
|
43
|
+
class Machine
|
44
|
+
include Chef::Provisioning::AWSDriver::AWSTaggable
|
45
|
+
end
|
46
|
+
class MachineImage
|
47
|
+
include Chef::Provisioning::AWSDriver::AWSTaggable
|
48
|
+
end
|
49
|
+
class LoadBalancer
|
50
|
+
include Chef::Provisioning::AWSDriver::AWSTaggable
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
Chef::Provider::Machine.additional_machine_option_keys << :aws_tags
|
56
|
+
Chef::Provider::MachineImage.additional_image_option_keys << :aws_tags
|
57
|
+
Chef::Provider::LoadBalancer.additional_lb_option_keys << :aws_tags
|
29
58
|
|
30
59
|
class Chef
|
31
60
|
module Provisioning
|
@@ -62,6 +91,20 @@ module AWSDriver
|
|
62
91
|
session_token: credentials[:aws_session_token] || nil,
|
63
92
|
logger: Chef::Log.logger
|
64
93
|
)
|
94
|
+
|
95
|
+
# TODO document how users could add something to the Aws.config themselves if they want to
|
96
|
+
# Right now we are supporting both V1 and V2, so we create 2 config sets
|
97
|
+
credentials2 = Credentials2.new(:profile_name => profile_name)
|
98
|
+
Chef::Config.chef_provisioning ||= {}
|
99
|
+
::Aws.config.update(
|
100
|
+
credentials: credentials2.get_credentials,
|
101
|
+
region: region || ENV["AWS_DEFAULT_REGION"] || credentials[:region],
|
102
|
+
# TODO when we get rid of V1 replace the credentials class with something that knows how
|
103
|
+
# to read ~/.aws/config
|
104
|
+
:http_proxy => credentials[:proxy_uri] || nil,
|
105
|
+
logger: Chef::Log.logger,
|
106
|
+
retry_limit: Chef::Config.chef_provisioning[:aws_retry_limit] || 5
|
107
|
+
)
|
65
108
|
end
|
66
109
|
|
67
110
|
def self.canonicalize_url(driver_url, config)
|
@@ -70,10 +113,13 @@ module AWSDriver
|
|
70
113
|
|
71
114
|
# Load balancer methods
|
72
115
|
def allocate_load_balancer(action_handler, lb_spec, lb_options, machine_specs)
|
73
|
-
lb_options =
|
74
|
-
|
116
|
+
lb_options = (lb_options || {}).to_h
|
117
|
+
lb_options = AWSResource.lookup_options(lb_options, managed_entry_store: lb_spec.managed_entry_store, driver: self)
|
118
|
+
# We delete the attributes and a health check here because they are not valid in the create call
|
75
119
|
# and must be applied afterward
|
76
120
|
lb_attributes = lb_options.delete(:attributes)
|
121
|
+
lb_aws_tags = lb_options.delete(:aws_tags)
|
122
|
+
health_check = lb_options.delete(:health_check)
|
77
123
|
|
78
124
|
old_elb = nil
|
79
125
|
actual_elb = load_balancer_for(lb_spec)
|
@@ -93,12 +139,8 @@ module AWSDriver
|
|
93
139
|
updates << " with security groups #{lb_options[:security_groups]}" if lb_options[:security_groups]
|
94
140
|
updates << " with tags #{lb_options[:aws_tags]}" if lb_options[:aws_tags]
|
95
141
|
|
96
|
-
|
97
|
-
lb_aws_tags = lb_options[:aws_tags]
|
98
|
-
lb_options.delete(:aws_tags)
|
99
142
|
action_handler.perform_action updates do
|
100
143
|
actual_elb = elb.load_balancers.create(lb_spec.name, lb_options)
|
101
|
-
lb_options[:aws_tags] = lb_aws_tags
|
102
144
|
|
103
145
|
lb_spec.reference = {
|
104
146
|
'driver_version' => Chef::Provisioning::AWSDriver::VERSION,
|
@@ -281,34 +323,7 @@ module AWSDriver
|
|
281
323
|
end
|
282
324
|
end
|
283
325
|
|
284
|
-
|
285
|
-
read_tags_block = lambda {|aws_object|
|
286
|
-
resp = elb.client.describe_tags load_balancer_names: [aws_object.name]
|
287
|
-
tags = {}
|
288
|
-
resp.data[:tag_descriptions] && resp.data[:tag_descriptions].each do |td|
|
289
|
-
td[:tags].each do |t|
|
290
|
-
tags[t[:key]] = t[:value]
|
291
|
-
end
|
292
|
-
end
|
293
|
-
tags
|
294
|
-
}
|
295
|
-
|
296
|
-
set_tags_block = lambda {|aws_object, desired_tags|
|
297
|
-
aws_form_tags = []
|
298
|
-
desired_tags.each do |k, v|
|
299
|
-
aws_form_tags << {key: k, value: v}
|
300
|
-
end
|
301
|
-
elb.client.add_tags load_balancer_names: [aws_object.name], tags: aws_form_tags
|
302
|
-
}
|
303
|
-
|
304
|
-
delete_tags_block=lambda {|aws_object, tags_to_delete|
|
305
|
-
aws_form_tags = []
|
306
|
-
tags_to_delete.each do |k, v|
|
307
|
-
aws_form_tags << {key: k}
|
308
|
-
end
|
309
|
-
elb.client.remove_tags load_balancer_names: [aws_object.name], tags: aws_form_tags
|
310
|
-
}
|
311
|
-
converge_tags(actual_elb, lb_options[:aws_tags], action_handler, read_tags_block, set_tags_block, delete_tags_block)
|
326
|
+
converge_elb_tags(actual_elb, lb_aws_tags, action_handler)
|
312
327
|
|
313
328
|
# Update load balancer attributes
|
314
329
|
if lb_attributes
|
@@ -325,12 +340,26 @@ module AWSDriver
|
|
325
340
|
end
|
326
341
|
end
|
327
342
|
|
343
|
+
# Update the load balancer health check, as above
|
344
|
+
if health_check
|
345
|
+
current = elb.client.describe_load_balancers(load_balancer_names: [actual_elb.name])[:load_balancer_descriptions][0][:health_check]
|
346
|
+
desired = deep_merge!(health_check, Marshal.load(Marshal.dump(current)))
|
347
|
+
if current != desired
|
348
|
+
perform_action.call(" updating health check to #{desired.inspect}") do
|
349
|
+
elb.client.configure_health_check(
|
350
|
+
load_balancer_name: actual_elb.name,
|
351
|
+
health_check: desired.to_hash
|
352
|
+
)
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
328
357
|
# Update instance list, but only if there are machines specified
|
329
358
|
if machine_specs
|
330
|
-
|
359
|
+
assigned_instance_ids = actual_elb.instances.map { |i| i.instance_id }
|
331
360
|
|
332
|
-
instances_to_add = machine_specs.select { |s| !
|
333
|
-
instance_ids_to_remove =
|
361
|
+
instances_to_add = machine_specs.select { |s| !assigned_instance_ids.include?(s.reference['instance_id']) }
|
362
|
+
instance_ids_to_remove = assigned_instance_ids - machine_specs.map { |s| s.reference['instance_id'] }
|
334
363
|
|
335
364
|
if instances_to_add.size > 0
|
336
365
|
perform_action.call(" add machines #{instances_to_add.map { |s| s.name }.join(', ')}") do
|
@@ -410,6 +439,8 @@ module AWSDriver
|
|
410
439
|
# Image methods
|
411
440
|
def allocate_image(action_handler, image_spec, image_options, machine_spec, machine_options)
|
412
441
|
actual_image = image_for(image_spec)
|
442
|
+
image_options = (image_options || {}).to_h.dup
|
443
|
+
machine_options = (machine_options || {}).to_h.dup
|
413
444
|
aws_tags = image_options.delete(:aws_tags) || {}
|
414
445
|
if actual_image.nil? || !actual_image.exists? || actual_image.state == :failed
|
415
446
|
action_handler.perform_action "Create image #{image_spec.name} from machine #{machine_spec.name} with options #{image_options.inspect}" do
|
@@ -421,13 +452,14 @@ module AWSDriver
|
|
421
452
|
image_spec.reference = {
|
422
453
|
'driver_version' => Chef::Provisioning::AWSDriver::VERSION,
|
423
454
|
'image_id' => actual_image.id,
|
424
|
-
'allocated_at' => Time.now.to_i
|
455
|
+
'allocated_at' => Time.now.to_i,
|
456
|
+
'from-instance' => image_options[:instance_id]
|
425
457
|
}
|
426
458
|
image_spec.driver_url = driver_url
|
427
459
|
end
|
428
460
|
end
|
429
|
-
aws_tags['
|
430
|
-
|
461
|
+
aws_tags['from-instance'] = image_options[:instance_id] if image_options[:instance_id]
|
462
|
+
converge_ec2_tags(actual_image, aws_tags, action_handler)
|
431
463
|
end
|
432
464
|
|
433
465
|
def ready_image(action_handler, image_spec, image_options)
|
@@ -435,11 +467,13 @@ module AWSDriver
|
|
435
467
|
if actual_image.nil? || !actual_image.exists?
|
436
468
|
raise 'Cannot ready an image that does not exist'
|
437
469
|
else
|
470
|
+
image_options = (image_options || {}).to_h.dup
|
471
|
+
aws_tags = image_options.delete(:aws_tags) || {}
|
472
|
+
aws_tags['from-instance'] = image_spec.reference['from-instance'] if image_spec.reference['from-instance']
|
473
|
+
converge_ec2_tags(actual_image, aws_tags, action_handler)
|
438
474
|
if actual_image.state != :available
|
439
475
|
action_handler.report_progress 'Waiting for image to be ready ...'
|
440
476
|
wait_until_ready_image(action_handler, image_spec, actual_image)
|
441
|
-
else
|
442
|
-
action_handler.report_progress "Image #{image_spec.name} is ready!"
|
443
477
|
end
|
444
478
|
end
|
445
479
|
end
|
@@ -477,38 +511,16 @@ EOD
|
|
477
511
|
|
478
512
|
# Machine methods
|
479
513
|
def allocate_machine(action_handler, machine_spec, machine_options)
|
480
|
-
|
514
|
+
instance = instance_for(machine_spec)
|
481
515
|
bootstrap_options = bootstrap_options_for(action_handler, machine_spec, machine_options)
|
482
516
|
|
483
|
-
if
|
484
|
-
|
517
|
+
if instance == nil || !instance.exists? || instance.state.name == "terminated"
|
485
518
|
action_handler.perform_action "Create #{machine_spec.name} with AMI #{bootstrap_options[:image_id]} in #{aws_config.region}" do
|
486
519
|
Chef::Log.debug "Creating instance with bootstrap options #{bootstrap_options}"
|
487
|
-
|
488
|
-
actual_instance = ec2.instances.create(bootstrap_options.to_hash)
|
489
|
-
# Make sure the instance is ready to be tagged
|
490
|
-
wait_until_taggable(actual_instance)
|
491
|
-
|
492
|
-
# TODO add other tags identifying user / node url (same as fog)
|
493
|
-
actual_instance.tags['Name'] = machine_spec.name
|
494
|
-
actual_instance.source_dest_check = machine_options[:source_dest_check] if machine_options.has_key?(:source_dest_check)
|
495
|
-
machine_spec.reference = {
|
496
|
-
'driver_version' => Chef::Provisioning::AWSDriver::VERSION,
|
497
|
-
'allocated_at' => Time.now.utc.to_s,
|
498
|
-
'host_node' => action_handler.host_node,
|
499
|
-
'image_id' => bootstrap_options[:image_id],
|
500
|
-
'instance_id' => actual_instance.id
|
501
|
-
}
|
502
|
-
machine_spec.driver_url = driver_url
|
503
|
-
machine_spec.reference['key_name'] = bootstrap_options[:key_name] if bootstrap_options[:key_name]
|
504
|
-
%w(is_windows ssh_username sudo use_private_ip_for_ssh ssh_gateway).each do |key|
|
505
|
-
machine_spec.reference[key] = machine_options[key.to_sym] if machine_options[key.to_sym]
|
506
|
-
end
|
520
|
+
instance = create_instance_and_reference(bootstrap_options, action_handler, machine_spec, machine_options)
|
507
521
|
end
|
508
522
|
end
|
509
|
-
|
510
|
-
# we have to update the tags here in driver.rb instead of the providers
|
511
|
-
converge_tags(actual_instance, machine_options[:aws_tags], action_handler)
|
523
|
+
converge_ec2_tags(instance, machine_options[:aws_tags], action_handler)
|
512
524
|
end
|
513
525
|
|
514
526
|
def allocate_machines(action_handler, specs_and_options, parallelizer)
|
@@ -520,14 +532,15 @@ EOD
|
|
520
532
|
|
521
533
|
def ready_machine(action_handler, machine_spec, machine_options)
|
522
534
|
instance = instance_for(machine_spec)
|
535
|
+
converge_ec2_tags(instance, machine_options[:aws_tags], action_handler)
|
523
536
|
|
524
537
|
if instance.nil?
|
525
538
|
raise "Machine #{machine_spec.name} does not have an instance associated with it, or instance does not exist."
|
526
539
|
end
|
527
540
|
|
528
|
-
if instance.
|
529
|
-
wait_until_machine(action_handler, machine_spec, instance) { instance.
|
530
|
-
if instance.
|
541
|
+
if instance.state.name != "running"
|
542
|
+
wait_until_machine(action_handler, machine_spec, "finish stopping", instance) { instance.state.name != "stopping" }
|
543
|
+
if instance.state.name == "stopped"
|
531
544
|
action_handler.perform_action "Start #{machine_spec.name} (#{machine_spec.reference['instance_id']}) in #{aws_config.region} ..." do
|
532
545
|
instance.start
|
533
546
|
end
|
@@ -563,10 +576,30 @@ EOD
|
|
563
576
|
strategy.cleanup_convergence(action_handler, machine_spec)
|
564
577
|
end
|
565
578
|
|
579
|
+
def cloudsearch(api_version="20130101")
|
580
|
+
@cloudsearch ||= {}
|
581
|
+
@cloudsearch[api_version] ||= AWS::CloudSearch::Client.const_get("V#{api_version}").new
|
582
|
+
@cloudsearch[api_version]
|
583
|
+
end
|
584
|
+
|
566
585
|
def ec2
|
567
586
|
@ec2 ||= AWS::EC2.new(config: aws_config)
|
568
587
|
end
|
569
588
|
|
589
|
+
AWS_V2_SERVICES.each do |load_name, short_name|
|
590
|
+
class_eval <<-META
|
591
|
+
|
592
|
+
def #{short_name}_client
|
593
|
+
@#{short_name}_client ||= ::Aws::#{load_name}::Client.new
|
594
|
+
end
|
595
|
+
|
596
|
+
def #{short_name}_resource
|
597
|
+
@#{short_name}_resource ||= ::Aws::#{load_name}::Resource.new(#{short_name}_client)
|
598
|
+
end
|
599
|
+
|
600
|
+
META
|
601
|
+
end
|
602
|
+
|
570
603
|
def elb
|
571
604
|
@elb ||= AWS::ELB.new(config: aws_config)
|
572
605
|
end
|
@@ -579,10 +612,18 @@ EOD
|
|
579
612
|
@iam ||= AWS::IAM.new(config: aws_config)
|
580
613
|
end
|
581
614
|
|
615
|
+
def rds
|
616
|
+
@rds ||= AWS::RDS.new(config: aws_config)
|
617
|
+
end
|
618
|
+
|
582
619
|
def s3
|
583
620
|
@s3 ||= AWS::S3.new(config: aws_config)
|
584
621
|
end
|
585
622
|
|
623
|
+
def rds
|
624
|
+
@rds ||= AWS::RDS.new(config: aws_config)
|
625
|
+
end
|
626
|
+
|
586
627
|
def sns
|
587
628
|
@sns ||= AWS::SNS.new(config: aws_config)
|
588
629
|
end
|
@@ -645,8 +686,10 @@ EOD
|
|
645
686
|
|
646
687
|
def bootstrap_options_for(action_handler, machine_spec, machine_options)
|
647
688
|
bootstrap_options = (machine_options[:bootstrap_options] || {}).to_h.dup
|
689
|
+
# These are hardcoded for now - only 1 machine at a time
|
690
|
+
bootstrap_options[:min_count] = bootstrap_options[:max_count] = 1
|
648
691
|
bootstrap_options[:instance_type] ||= default_instance_type
|
649
|
-
image_id = bootstrap_options[:image_id] || machine_options[:image_id] || default_ami_for_region(aws_config.region)
|
692
|
+
image_id = machine_options[:from_image] || bootstrap_options[:image_id] || machine_options[:image_id] || default_ami_for_region(aws_config.region)
|
650
693
|
bootstrap_options[:image_id] = image_id
|
651
694
|
if !bootstrap_options[:key_name]
|
652
695
|
Chef::Log.debug('No key specified, generating a default one...')
|
@@ -834,10 +877,19 @@ EOD
|
|
834
877
|
end
|
835
878
|
|
836
879
|
def determine_remote_host(machine_spec, instance)
|
880
|
+
transport_address_location = (machine_spec.reference['transport_address_location'] || :none).to_sym
|
837
881
|
if machine_spec.reference['use_private_ip_for_ssh']
|
882
|
+
# The machine_spec has the old config key, lets update it - a successful chef converge will save the machine_spec
|
883
|
+
# TODO in 2.0 get rid of this update
|
884
|
+
machine_spec.reference.delete('use_private_ip_for_ssh')
|
885
|
+
machine_spec.reference['transport_address_location'] = :private_ip
|
838
886
|
instance.private_ip_address
|
839
|
-
elsif
|
840
|
-
|
887
|
+
elsif transport_address_location == :private_ip
|
888
|
+
instance.private_ip_address
|
889
|
+
elsif transport_address_location == :dns
|
890
|
+
instance.dns_name
|
891
|
+
elsif !instance.public_ip_address && instance.private_ip_address
|
892
|
+
Chef::Log.warn("Server #{machine_spec.name} has no public ip address. Using private ip '#{instance.private_ip_address}'. Set machine_options ':transport_address_location => :private_ip' if this will always be the case ...")
|
841
893
|
instance.private_ip_address
|
842
894
|
elsif instance.public_ip_address
|
843
895
|
instance.public_ip_address
|
@@ -924,28 +976,31 @@ EOD
|
|
924
976
|
end
|
925
977
|
|
926
978
|
def wait_until_ready_machine(action_handler, machine_spec, instance=nil)
|
927
|
-
wait_until_machine(action_handler, machine_spec, instance) { instance
|
979
|
+
wait_until_machine(action_handler, machine_spec, "be ready", instance) { |instance|
|
980
|
+
instance.state.name == "running"
|
981
|
+
}
|
928
982
|
end
|
929
983
|
|
930
|
-
def wait_until_machine(action_handler, machine_spec, instance=nil, &block)
|
984
|
+
def wait_until_machine(action_handler, machine_spec, output_msg, instance=nil, &block)
|
931
985
|
instance ||= instance_for(machine_spec)
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
raise "Image #{instance.id} did not become ready within #{max_wait_time} seconds"
|
986
|
+
# TODO make these configurable from Chef::Config
|
987
|
+
max_attempts = 12
|
988
|
+
delay = 10
|
989
|
+
log_progress = Proc.new do |attempts, response|
|
990
|
+
action_handler.report_progress "been waiting #{delay*attempts}/#{delay*max_attempts} -- sleeping #{delay} seconds for #{machine_spec.name} (#{instance.id} on #{driver_url}) to #{output_msg} ..."
|
991
|
+
end
|
992
|
+
if action_handler.should_perform_actions
|
993
|
+
no_wait = yield(instance)
|
994
|
+
unless no_wait
|
995
|
+
action_handler.report_progress "waiting for #{machine_spec.name} (#{instance.id} on #{driver_url}) to #{output_msg} ..."
|
996
|
+
instance.wait_until(:max_attempts => max_attempts, :delay => delay, before_wait: log_progress) do |instance|
|
997
|
+
yield(instance)
|
945
998
|
end
|
946
|
-
action_handler.report_progress "#{machine_spec.name} is now ready"
|
947
999
|
end
|
948
1000
|
end
|
1001
|
+
# We need an instance.reload here because the `wait_until` does not reload the instance it is called on,
|
1002
|
+
# only the instance that is passed to the block
|
1003
|
+
instance.reload
|
949
1004
|
end
|
950
1005
|
|
951
1006
|
def wait_for_transport(action_handler, machine_spec, machine_options)
|
@@ -996,18 +1051,20 @@ EOD
|
|
996
1051
|
default_key_name
|
997
1052
|
end
|
998
1053
|
|
999
|
-
def create_servers(action_handler, specs_and_options, parallelizer
|
1054
|
+
def create_servers(action_handler, specs_and_options, parallelizer)
|
1000
1055
|
specs_and_servers = instances_for(specs_and_options.keys)
|
1001
1056
|
|
1002
1057
|
by_bootstrap_options = {}
|
1003
1058
|
specs_and_options.each do |machine_spec, machine_options|
|
1004
|
-
|
1005
|
-
if
|
1006
|
-
if
|
1007
|
-
Chef::Log.warn "Machine #{machine_spec.name} (#{
|
1059
|
+
instance = specs_and_servers[machine_spec]
|
1060
|
+
if instance
|
1061
|
+
if instance.state.name == "terminated"
|
1062
|
+
Chef::Log.warn "Machine #{machine_spec.name} (#{instance.id}) is terminated. Recreating ..."
|
1008
1063
|
else
|
1009
|
-
|
1010
|
-
|
1064
|
+
# Even though the instance has been created the tags could be incorrect if it
|
1065
|
+
# was created before tags were introduced
|
1066
|
+
converge_ec2_tags(instance, machine_options[:aws_tags], action_handler)
|
1067
|
+
yield machine_spec, instance if block_given?
|
1011
1068
|
next
|
1012
1069
|
end
|
1013
1070
|
elsif machine_spec.reference
|
@@ -1031,30 +1088,20 @@ EOD
|
|
1031
1088
|
action_handler.report_progress description
|
1032
1089
|
if action_handler.should_perform_actions
|
1033
1090
|
# Actually create the servers
|
1034
|
-
|
1091
|
+
parallelizer.parallelize(1.upto(machine_specs.size)) do |i|
|
1035
1092
|
|
1036
1093
|
# Assign each one to a machine spec
|
1037
1094
|
machine_spec = machine_specs.pop
|
1038
1095
|
machine_options = specs_and_options[machine_spec]
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
'instance_id' => instance.id
|
1045
|
-
}
|
1046
|
-
machine_spec.driver_url = driver_url
|
1047
|
-
instance.tags['Name'] = machine_spec.name
|
1048
|
-
instance.source_dest_check = machine_options[:source_dest_check] if machine_options.has_key?(:source_dest_check)
|
1049
|
-
converge_tags(instance, machine_options[:aws_tags], action_handler)
|
1050
|
-
machine_spec.reference['key_name'] = bootstrap_options[:key_name] if bootstrap_options[:key_name]
|
1051
|
-
%w(is_windows ssh_username sudo use_private_ip_for_ssh ssh_gateway).each do |key|
|
1052
|
-
machine_spec.reference[key] = machine_options[key.to_sym] if machine_options[key.to_sym]
|
1053
|
-
end
|
1096
|
+
|
1097
|
+
clean_bootstrap_options = Marshal.load(Marshal.dump(bootstrap_options))
|
1098
|
+
instance = create_instance_and_reference(clean_bootstrap_options, action_handler, machine_spec, machine_options)
|
1099
|
+
converge_ec2_tags(instance, machine_options[:aws_tags], action_handler)
|
1100
|
+
|
1054
1101
|
action_handler.performed_action "machine #{machine_spec.name} created as #{instance.id} on #{driver_url}"
|
1055
1102
|
|
1056
1103
|
yield machine_spec, instance if block_given?
|
1057
|
-
end
|
1104
|
+
end.to_a
|
1058
1105
|
|
1059
1106
|
if machine_specs.size > 0
|
1060
1107
|
raise "Not all machines were created by create_servers"
|
@@ -1063,58 +1110,63 @@ EOD
|
|
1063
1110
|
end.to_a
|
1064
1111
|
end
|
1065
1112
|
|
1066
|
-
def
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1113
|
+
def converge_ec2_tags(aws_object, tags, action_handler)
|
1114
|
+
ec2_strategy = Chef::Provisioning::AWSDriver::TaggingStrategy::EC2.new(
|
1115
|
+
ec2_client,
|
1116
|
+
aws_object.id,
|
1117
|
+
tags
|
1118
|
+
)
|
1119
|
+
aws_tagger = Chef::Provisioning::AWSDriver::AWSTagger.new(ec2_strategy, action_handler)
|
1120
|
+
aws_tagger.converge_tags
|
1121
|
+
end
|
1071
1122
|
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
1123
|
+
def converge_elb_tags(aws_object, tags, action_handler)
|
1124
|
+
elb_strategy = Chef::Provisioning::AWSDriver::TaggingStrategy::ELB.new(
|
1125
|
+
elb_client,
|
1126
|
+
aws_object.name,
|
1127
|
+
tags
|
1128
|
+
)
|
1129
|
+
aws_tagger = Chef::Provisioning::AWSDriver::AWSTagger.new(elb_strategy, action_handler)
|
1130
|
+
aws_tagger.converge_tags
|
1075
1131
|
end
|
1076
1132
|
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1085
|
-
set_tags_block=lambda {|aws_object, desired_tags| aws_object.tags.set(desired_tags) },
|
1086
|
-
delete_tags_block=lambda {|aws_object, tags_to_delete| aws_object.tags.delete(*tags_to_delete) }
|
1087
|
-
)
|
1088
|
-
# If aws_tags were not provided we exit
|
1089
|
-
if desired_tags.nil?
|
1090
|
-
Chef::Log.debug "aws_tags not provided, nothing to converge"
|
1091
|
-
return
|
1133
|
+
def create_instance_and_reference(bootstrap_options, action_handler, machine_spec, machine_options)
|
1134
|
+
instance = ec2_resource.create_instances(bootstrap_options.to_hash)[0]
|
1135
|
+
# Make sure the instance is ready to be tagged
|
1136
|
+
instance.wait_until_exists
|
1137
|
+
|
1138
|
+
# Sometimes tagging fails even though the instance 'exists'
|
1139
|
+
Retryable.retryable(:tries => 12, :sleep => 5, :on => [::Aws::EC2::Errors::InvalidInstanceIDNotFound]) do
|
1140
|
+
instance.create_tags({tags: [{key: "Name", value: machine_spec.name}]})
|
1092
1141
|
end
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
# We don't want to delete `Name`, just all other tags
|
1100
|
-
tags_to_delete.delete('Name')
|
1101
|
-
|
1102
|
-
unless tags_to_update.empty?
|
1103
|
-
action_handler.perform_action "applying tags #{tags_to_update}" do
|
1104
|
-
set_tags_block.call(aws_object, tags_to_update)
|
1105
|
-
end
|
1142
|
+
if machine_options.has_key?(:source_dest_check)
|
1143
|
+
instance.modify_attribute({
|
1144
|
+
source_dest_check: {
|
1145
|
+
value: machine_options[:source_dest_check]
|
1146
|
+
}
|
1147
|
+
})
|
1106
1148
|
end
|
1107
|
-
|
1108
|
-
|
1109
|
-
|
1149
|
+
machine_spec.reference = {
|
1150
|
+
'driver_version' => Chef::Provisioning::AWSDriver::VERSION,
|
1151
|
+
'allocated_at' => Time.now.utc.to_s,
|
1152
|
+
'host_node' => action_handler.host_node,
|
1153
|
+
'image_id' => bootstrap_options[:image_id],
|
1154
|
+
'instance_id' => instance.id
|
1155
|
+
}
|
1156
|
+
machine_spec.driver_url = driver_url
|
1157
|
+
machine_spec.reference['key_name'] = bootstrap_options[:key_name] if bootstrap_options[:key_name]
|
1158
|
+
# TODO 2.0 We no longer support `use_private_ip_for_ssh`, only `transport_address_location`
|
1159
|
+
if machine_options[:use_private_ip_for_ssh]
|
1160
|
+
unless @transport_address_location_warned
|
1161
|
+
Chef::Log.warn("The machine_option ':use_private_ip_for_ssh' has been deprecated, use ':transport_address_location'")
|
1162
|
+
@transport_address_location_warned = true
|
1110
1163
|
end
|
1164
|
+
machine_options = Cheffish::MergedConfig.new(machine_options, {:transport_address_location => :private_ip})
|
1111
1165
|
end
|
1112
|
-
|
1113
|
-
|
1114
|
-
def wait_until_taggable(instance)
|
1115
|
-
Retryable.retryable(:tries => 12, :sleep => 5, :on => [AWS::EC2::Errors::InvalidInstanceID::NotFound, TimeoutError]) do
|
1116
|
-
raise TimeoutError unless instance.status == :pending || instance.status == :running
|
1166
|
+
%w(is_windows ssh_username sudo transport_address_location ssh_gateway).each do |key|
|
1167
|
+
machine_spec.reference[key] = machine_options[key.to_sym] if machine_options[key.to_sym]
|
1117
1168
|
end
|
1169
|
+
instance
|
1118
1170
|
end
|
1119
1171
|
|
1120
1172
|
def get_listeners(listeners)
|