chef-provisioning-aws 1.3.1 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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)
|