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,88 @@
|
|
1
|
+
require 'chef/provisioning/aws_driver/aws_provider'
|
2
|
+
require 'retryable'
|
3
|
+
|
4
|
+
class Chef::Provider::AwsVpcPeeringConnection < Chef::Provisioning::AWSDriver::AWSProvider
|
5
|
+
provides :aws_vpc_peering_connection
|
6
|
+
|
7
|
+
def action_create
|
8
|
+
vpc_peering_connection = super
|
9
|
+
accept_connection(vpc_peering_connection, new_resource)
|
10
|
+
end
|
11
|
+
|
12
|
+
def action_accept
|
13
|
+
existing_vpc_peering_connection = new_resource.aws_object
|
14
|
+
accept_connection(existing_vpc_peering_connection, new_resource)
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def create_aws_object
|
20
|
+
if new_resource.vpc.nil?
|
21
|
+
raise "VCP peering connection create action for '#{new_resource.name}' requires the 'vpc' attribute."
|
22
|
+
elsif new_resource.peer_vpc.nil?
|
23
|
+
raise "VCP peering connection create action for '#{new_resource.name}' requires the 'peer_vpc' attribute."
|
24
|
+
end
|
25
|
+
|
26
|
+
options = {}
|
27
|
+
options[:vpc_id] = new_resource.vpc
|
28
|
+
options[:peer_vpc_id] = new_resource.peer_vpc
|
29
|
+
options[:peer_owner_id] = new_resource.peer_owner_id unless new_resource.peer_owner_id.nil?
|
30
|
+
options = AWSResource.lookup_options(options, resource: new_resource)
|
31
|
+
|
32
|
+
ec2_resource = new_resource.driver.ec2_resource
|
33
|
+
vpc = ec2_resource.vpc(options[:vpc_id])
|
34
|
+
|
35
|
+
converge_by "create peering connection #{new_resource.name} in VPC #{new_resource.vpc} (#{vpc.id}) and region #{region}" do
|
36
|
+
vpc_peering_connection = vpc.request_vpc_peering_connection(options)
|
37
|
+
|
38
|
+
retry_with_backoff(::Aws::EC2::Errors::ServiceError) do
|
39
|
+
ec2_resource.create_tags({
|
40
|
+
:resources => [vpc_peering_connection.id],
|
41
|
+
:tags => [
|
42
|
+
{
|
43
|
+
:key => "Name",
|
44
|
+
:value => new_resource.name
|
45
|
+
}
|
46
|
+
]
|
47
|
+
})
|
48
|
+
end
|
49
|
+
vpc_peering_connection
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def update_aws_object(vpc_peering_connection)
|
54
|
+
vpc_id = vpc_peering_connection.requester_vpc_info.vpc_id
|
55
|
+
peer_vpc_id = vpc_peering_connection.accepter_vpc_info.vpc_id
|
56
|
+
peer_owner_id = vpc_peering_connection.accepter_vpc_info.owner_id
|
57
|
+
|
58
|
+
desired_vpc_id = Chef::Resource::AwsVpc.get_aws_object_id(new_resource.vpc, resource: new_resource)
|
59
|
+
desired_peer_vpc_id = Chef::Resource::AwsVpc.get_aws_object_id(new_resource.peer_vpc, resource: new_resource)
|
60
|
+
desired_peer_owner_id = new_resource.peer_owner_id
|
61
|
+
|
62
|
+
if desired_vpc_id && vpc_id != desired_vpc_id
|
63
|
+
raise "VCP peering connection requester vpc cannot be changed after being created! Desired requester vpc id for #{new_resource.name} (#{vpc_peering_connection.id}) was \"#{desired_vpc_id}\" and actual id is \"#{vpc_id}\""
|
64
|
+
end
|
65
|
+
if desired_peer_vpc_id && peer_vpc_id != desired_peer_vpc_id
|
66
|
+
raise "VCP peering connection accepter vpc cannot be changed after being created! Desired accepter vpc id for #{new_resource.name} (#{vpc_peering_connection.id}) was \"#{desired_peer_vpc_id}\" and actual id is \"#{peer_vpc_id}\""
|
67
|
+
end
|
68
|
+
if desired_peer_owner_id && peer_owner_id != desired_peer_owner_id
|
69
|
+
raise "VCP peering connection accepter owner id vpc cannot be changed after being created! Desired accepter vpc owner id for #{new_resource.name} (#{vpc_peering_connection.id}) was \"#{desired_peer_owner_id}\" and actual owner id is \"#{peer_owner_id}\""
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def destroy_aws_object(vpc_peering_connection)
|
74
|
+
converge_by "delete #{new_resource.to_s} in #{region}" do
|
75
|
+
unless ['deleted', 'failed', 'deleting'].include? vpc_peering_connection.status.code
|
76
|
+
vpc_peering_connection.delete
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def accept_connection(vpc_peering_connection, new_resource)
|
84
|
+
if new_resource.peer_owner_id.nil? or new_resource.peer_owner_id == new_resource.driver.account_id
|
85
|
+
vpc_peering_connection.accept
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -5,20 +5,28 @@ require "chef/resource/aws_auto_scaling_group"
|
|
5
5
|
require "chef/resource/aws_cache_cluster"
|
6
6
|
require "chef/resource/aws_cache_replication_group"
|
7
7
|
require "chef/resource/aws_cache_subnet_group"
|
8
|
+
require "chef/resource/aws_cloudsearch_domain"
|
8
9
|
require "chef/resource/aws_dhcp_options"
|
9
10
|
require "chef/resource/aws_ebs_volume"
|
10
11
|
require "chef/resource/aws_eip_address"
|
11
12
|
require "chef/resource/aws_image"
|
12
13
|
require "chef/resource/aws_instance"
|
13
14
|
require "chef/resource/aws_internet_gateway"
|
15
|
+
require "chef/resource/aws_key_pair"
|
14
16
|
require "chef/resource/aws_launch_configuration"
|
15
17
|
require "chef/resource/aws_load_balancer"
|
16
18
|
require "chef/resource/aws_network_acl"
|
17
19
|
require "chef/resource/aws_network_interface"
|
18
20
|
require "chef/resource/aws_route_table"
|
21
|
+
require "chef/resource/aws_rds_instance"
|
22
|
+
require "chef/resource/aws_rds_subnet_group"
|
23
|
+
require "chef/resource/aws_route_table"
|
19
24
|
require "chef/resource/aws_s3_bucket"
|
20
25
|
require "chef/resource/aws_security_group"
|
26
|
+
require "chef/resource/aws_server_certificate"
|
21
27
|
require "chef/resource/aws_sns_topic"
|
22
28
|
require "chef/resource/aws_sqs_queue"
|
23
29
|
require "chef/resource/aws_subnet"
|
24
30
|
require "chef/resource/aws_vpc"
|
31
|
+
require "chef/resource/aws_vpc_peering_connection"
|
32
|
+
|
@@ -3,6 +3,8 @@ require 'chef/provisioning/aws_driver/aws_resource'
|
|
3
3
|
require 'chef/provisioning/aws_driver/aws_resource_with_entry'
|
4
4
|
require 'chef/provisioning/chef_managed_entry_store'
|
5
5
|
require 'chef/provisioning/chef_provider_action_handler'
|
6
|
+
# Enough providers will require this that we put it in here
|
7
|
+
require 'chef/provisioning/aws_driver/tagging_strategy/ec2'
|
6
8
|
require 'retryable'
|
7
9
|
|
8
10
|
module Chef::Provisioning::AWSDriver
|
@@ -121,8 +123,6 @@ class AWSProvider < Chef::Provider::LWRPBase
|
|
121
123
|
aws_object = create_aws_object
|
122
124
|
end
|
123
125
|
|
124
|
-
converge_tags(aws_object)
|
125
|
-
|
126
126
|
#
|
127
127
|
# Associate the managed entry with the AWS object
|
128
128
|
#
|
@@ -130,6 +130,12 @@ class AWSProvider < Chef::Provider::LWRPBase
|
|
130
130
|
new_resource.save_managed_entry(aws_object, action_handler, existing_entry: entry)
|
131
131
|
end
|
132
132
|
|
133
|
+
# This has to be after the managed entry save so the `aws_object` lookup
|
134
|
+
# from the resource succeeds
|
135
|
+
if respond_to?(:converge_tags)
|
136
|
+
converge_tags
|
137
|
+
end
|
138
|
+
|
133
139
|
aws_object
|
134
140
|
end
|
135
141
|
|
@@ -223,90 +229,53 @@ class AWSProvider < Chef::Provider::LWRPBase
|
|
223
229
|
raise NotImplementedError, :destroy_aws_object
|
224
230
|
end
|
225
231
|
|
226
|
-
# Update AWS resource tags
|
227
|
-
#
|
228
|
-
# AWS resources which include the TaggedItem Module
|
229
|
-
# will have an 'aws_tags' attribute available.
|
230
|
-
# The 'aws_tags' Hash will apply all the tags within
|
231
|
-
# the hash, and remove existing tags not included within
|
232
|
-
# the hash. The 'Name' tag will not removed. The 'Name'
|
233
|
-
# tag can still be updated in the hash.
|
234
|
-
#
|
235
|
-
# @param aws_object Aws SDK Object to update tags
|
236
|
-
#
|
237
|
-
def converge_tags(aws_object)
|
238
|
-
return unless new_resource.respond_to?(:aws_tags)
|
239
|
-
desired_tags = new_resource.aws_tags
|
240
|
-
# If aws_tags were not provided we exit
|
241
|
-
if desired_tags.nil?
|
242
|
-
Chef::Log.debug "aws_tags not provided, nothing to converge"
|
243
|
-
return
|
244
|
-
end
|
245
|
-
current_tags = aws_object.tags.to_h
|
246
|
-
# AWS always returns tags as strings, and we don't want to overwrite a
|
247
|
-
# tag-as-string with the same tag-as-symbol
|
248
|
-
desired_tags = Hash[desired_tags.map {|k, v| [k.to_s, v.to_s] }]
|
249
|
-
tags_to_update = desired_tags.reject {|k,v| current_tags[k] == v}
|
250
|
-
tags_to_delete = current_tags.keys - desired_tags.keys
|
251
|
-
# We don't want to delete `Name`, just all other tags
|
252
|
-
tags_to_delete.delete('Name')
|
253
|
-
|
254
|
-
unless tags_to_update.empty?
|
255
|
-
converge_by "applying tags #{tags_to_update}" do
|
256
|
-
aws_object.tags.set(tags_to_update)
|
257
|
-
end
|
258
|
-
end
|
259
|
-
unless tags_to_delete.empty?
|
260
|
-
converge_by "deleting tags #{tags_to_delete.inspect}" do
|
261
|
-
aws_object.tags.delete(*tags_to_delete)
|
262
|
-
end
|
263
|
-
end
|
264
|
-
end
|
265
|
-
|
266
|
-
# Wait until aws_object obtains one of expected_status
|
267
|
-
#
|
268
|
-
# @param aws_object Aws SDK Object to check status on
|
269
|
-
# @param expected_status [Symbol,Array<Symbol>] Final status(s) to look for
|
270
|
-
# @param acceptable_errors [Exception,Array<Exception>] Acceptable errors that are caught and squelched
|
271
|
-
# @param tries [Integer] Number of times to check status
|
272
|
-
# @param sleep [Integer] Time to wait between checking status
|
273
|
-
#
|
274
232
|
def wait_for_status(aws_object, expected_status, acceptable_errors = [], tries=60, sleep=5)
|
275
|
-
|
276
|
-
|
277
|
-
|
233
|
+
wait_for(
|
234
|
+
aws_object: aws_object,
|
235
|
+
query_method: :status,
|
236
|
+
expected_responses: expected_status,
|
237
|
+
acceptable_errors: acceptable_errors,
|
238
|
+
tries: tries,
|
239
|
+
sleep: sleep
|
240
|
+
)
|
241
|
+
end
|
278
242
|
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
end
|
243
|
+
def wait_for_state(aws_object, expected_states, acceptable_errors = [], tries=60, sleep=5)
|
244
|
+
wait_for(
|
245
|
+
aws_object: aws_object,
|
246
|
+
query_method: :state,
|
247
|
+
expected_responses: expected_states,
|
248
|
+
acceptable_errors: acceptable_errors,
|
249
|
+
tries: tries,
|
250
|
+
sleep: sleep
|
251
|
+
)
|
289
252
|
end
|
290
253
|
|
291
|
-
# Wait until aws_object obtains one of
|
254
|
+
# Wait until aws_object obtains one of expected_responses
|
292
255
|
#
|
293
256
|
# @param aws_object Aws SDK Object to check state on
|
294
|
-
# @param
|
257
|
+
# @param query_method Method to call on aws_object to get current state
|
258
|
+
# @param expected_responses [Symbol,Array<Symbol>] Final state(s) to look for
|
295
259
|
# @param acceptable_errors [Exception,Array<Exception>] Acceptable errors that are caught and squelched
|
296
|
-
# @param tries [Integer] Number of times to check state
|
297
|
-
# @param sleep [Integer] Time to wait between checking states
|
260
|
+
# @param tries [Integer] Number of times to check state, defaults to 60
|
261
|
+
# @param sleep [Integer] Time to wait between checking states, defaults to 5
|
298
262
|
#
|
299
|
-
def
|
300
|
-
|
301
|
-
|
302
|
-
|
263
|
+
def wait_for(opts={})
|
264
|
+
aws_object = opts[:aws_object]
|
265
|
+
query_method = opts[:query_method]
|
266
|
+
expected_responses = [opts[:expected_responses]].flatten
|
267
|
+
acceptable_errors = [opts[:acceptable_errors] || []].flatten
|
268
|
+
tries = opts[:tries] || 60
|
269
|
+
sleep = opts[:sleep] || 5
|
303
270
|
|
304
271
|
Retryable.retryable(:tries => tries, :sleep => sleep) do |retries, exception|
|
305
|
-
action_handler.report_progress "waited #{retries*sleep}/#{tries*sleep}s for #{aws_object.id} state to change to #{
|
272
|
+
action_handler.report_progress "waited #{retries*sleep}/#{tries*sleep}s for #{aws_object.id} state to change to #{expected_responses.inspect}..."
|
273
|
+
Chef::Log.debug("Current exception is #{exception.inspect}")
|
306
274
|
begin
|
307
|
-
|
308
|
-
|
309
|
-
|
275
|
+
current_response = aws_object.send(query_method)
|
276
|
+
Chef::Log.debug("Current response from [#{query_method}] is #{current_response}")
|
277
|
+
unless expected_responses.include?(current_response)
|
278
|
+
raise StatusTimeoutError.new(aws_object, current_response, expected_responses)
|
310
279
|
end
|
311
280
|
rescue *acceptable_errors
|
312
281
|
end
|
@@ -317,7 +286,7 @@ class AWSProvider < Chef::Provider::LWRPBase
|
|
317
286
|
# @param retry_on [Exception] An exception to retry on, defaults to RuntimeError
|
318
287
|
#
|
319
288
|
def retry_with_backoff(retry_on = RuntimeError, &block)
|
320
|
-
Retryable.retryable(:tries => 10, :sleep => lambda { |n| [2**n,
|
289
|
+
Retryable.retryable(:tries => 10, :sleep => lambda { |n| [2**n, 16].min }, :on => retry_on, &block)
|
321
290
|
end
|
322
291
|
|
323
292
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require_relative 'aws_resource'
|
2
|
+
|
3
|
+
module Chef::Provisioning::AWSDriver
|
4
|
+
class AWSRDSResource < AWSResource
|
5
|
+
|
6
|
+
def rds_tagging_type
|
7
|
+
raise "You must add the RDS resource type lookup from http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_Tagging.html#USER_Tagging.ARN"
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'aws'
|
2
2
|
require 'chef/provisioning/aws_driver/super_lwrp'
|
3
3
|
require 'chef/provisioning/chef_managed_entry_store'
|
4
|
+
# Enough resources will eventually require this that we put 1 require in here
|
5
|
+
require 'chef/provisioning/aws_driver/aws_taggable'
|
4
6
|
|
5
7
|
# Common AWS resource - contains metadata that all AWS resources will need
|
6
8
|
module Chef::Provisioning::AWSDriver
|
@@ -59,12 +61,21 @@ class AWSResource < Chef::Provisioning::AWSDriver::SuperLWRP
|
|
59
61
|
lazy_default: proc { Chef::Provisioning::ChefManagedEntryStore.new(chef_server) }
|
60
62
|
|
61
63
|
#
|
62
|
-
# Get the current AWS object.
|
64
|
+
# Get the current AWS object. This should return the aws_object even if it has
|
65
|
+
# a status like 'deleted' or 'inactive'. If there is an aws_object, we return it.
|
66
|
+
# Callers will need to check the status if they care.
|
63
67
|
#
|
64
68
|
def aws_object
|
65
69
|
raise NotImplementedError, :aws_object
|
66
70
|
end
|
67
71
|
|
72
|
+
def aws_object_id
|
73
|
+
@aws_object_id ||= begin
|
74
|
+
o = aws_object
|
75
|
+
o.public_send(self.class.aws_sdk_class_id) if o
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
68
79
|
#
|
69
80
|
# Look up an AWS options list, translating standard names using the appropriate
|
70
81
|
# classes.
|
@@ -110,6 +121,7 @@ class AWSResource < Chef::Provisioning::AWSDriver::SuperLWRP
|
|
110
121
|
resource.driver driver if driver
|
111
122
|
resource.managed_entry_store managed_entry_store if managed_entry_store
|
112
123
|
end
|
124
|
+
|
113
125
|
result = resource.aws_object
|
114
126
|
if required && result.nil?
|
115
127
|
raise "#{self}[#{value}] does not exist!"
|
@@ -132,7 +144,7 @@ class AWSResource < Chef::Provisioning::AWSDriver::SuperLWRP
|
|
132
144
|
load_provider: true,
|
133
145
|
id: :name,
|
134
146
|
aws_id_prefix: nil)
|
135
|
-
self.resource_name = self.
|
147
|
+
self.resource_name = convert_to_snake_case(self.name.split('::')[-1])
|
136
148
|
@aws_sdk_class = sdk_class
|
137
149
|
@aws_sdk_class_id = id
|
138
150
|
@aws_id_prefix = aws_id_prefix
|
@@ -4,12 +4,6 @@ require 'chef/provisioning/aws_driver/resources'
|
|
4
4
|
# Common AWS resource - contains metadata that all AWS resources will need
|
5
5
|
class Chef::Provisioning::AWSDriver::AWSResourceWithEntry < Chef::Provisioning::AWSDriver::AWSResource
|
6
6
|
|
7
|
-
# This should be a hash of tags to apply to the AWS object
|
8
|
-
#
|
9
|
-
# @param aws_tags [Hash] Should be a hash of keys & values to add. Keys and values
|
10
|
-
# can be provided as symbols or strings, but will be stored in AWS as strings.
|
11
|
-
attribute :aws_tags, kind_of: Hash
|
12
|
-
|
13
7
|
#
|
14
8
|
# Dissociate the ID of this object from Chef.
|
15
9
|
#
|
@@ -19,7 +13,7 @@ class Chef::Provisioning::AWSDriver::AWSResourceWithEntry < Chef::Provisioning::
|
|
19
13
|
#
|
20
14
|
def delete_managed_entry(action_handler)
|
21
15
|
if should_have_managed_entry?
|
22
|
-
managed_entry_store.delete(self.class.
|
16
|
+
managed_entry_store.delete(self.class.managed_entry_type, name, action_handler)
|
23
17
|
end
|
24
18
|
end
|
25
19
|
|
@@ -37,7 +31,7 @@ class Chef::Provisioning::AWSDriver::AWSResourceWithEntry < Chef::Provisioning::
|
|
37
31
|
def save_managed_entry(aws_object, action_handler, existing_entry: nil)
|
38
32
|
if should_have_managed_entry?
|
39
33
|
managed_entry = existing_entry ||
|
40
|
-
managed_entry_store.new_entry(self.class.
|
34
|
+
managed_entry_store.new_entry(self.class.managed_entry_type, name)
|
41
35
|
updated = update_managed_entry(aws_object, managed_entry)
|
42
36
|
if updated || !existing_entry
|
43
37
|
managed_entry.save(action_handler)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Chef::Provisioning::AWSDriver
|
2
|
+
# This module is meant to be included in a resource that is taggable
|
3
|
+
# This will add the appropriate attribute that can be converged by the provider
|
4
|
+
# TODO it would be nice to not have two seperate modules (taggable/tagger)
|
5
|
+
# and just have the provider decorate the resource or vice versa. Complicated
|
6
|
+
# by resources <-> providers being many-to-many.
|
7
|
+
module AWSTaggable
|
8
|
+
|
9
|
+
def self.included(klass)
|
10
|
+
# This should be a hash of tags to apply to the AWS object
|
11
|
+
#
|
12
|
+
# @param aws_tags [Hash] Should be a hash of keys & values to add. Keys and values
|
13
|
+
# can be provided as symbols or strings, but will be stored in AWS as strings.
|
14
|
+
klass.attribute :aws_tags, kind_of: Hash
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'retryable'
|
2
|
+
|
3
|
+
module Chef::Provisioning::AWSDriver
|
4
|
+
# Include this module on a class or instance that is responsible for tagging
|
5
|
+
# itself. Fill in the hook methods so it knows how to tag itself.
|
6
|
+
class AWSTagger
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
attr_reader :action_handler
|
10
|
+
|
11
|
+
def initialize(tagging_strategy, action_handler)
|
12
|
+
@tagging_strategy = tagging_strategy
|
13
|
+
@action_handler = action_handler
|
14
|
+
end
|
15
|
+
|
16
|
+
def_delegators :@tagging_strategy, :desired_tags, :current_tags, :set_tags, :delete_tags
|
17
|
+
|
18
|
+
def converge_tags
|
19
|
+
if desired_tags.nil?
|
20
|
+
Chef::Log.debug "aws_tags not provided, nothing to converge"
|
21
|
+
return
|
22
|
+
end
|
23
|
+
|
24
|
+
# Duplication and normalization
|
25
|
+
# ::Aws::EC2::Errors::InvalidParameterValue: Tag value cannot be null. Use empty string instead.
|
26
|
+
n_desired_tags = Hash[desired_tags.map {|k,v| [k.to_s, v.to_s]}]
|
27
|
+
n_current_tags = Hash[current_tags.map {|k,v| [k.to_s, v.to_s]}]
|
28
|
+
|
29
|
+
tags_to_set = n_desired_tags.reject {|k,v| n_current_tags[k] && n_current_tags[k] == v}
|
30
|
+
tags_to_delete = n_current_tags.keys - n_desired_tags.keys
|
31
|
+
# We don't want to delete `Name`, just all other tags
|
32
|
+
# Tag keys and values are case sensitive - `Name` is special because it
|
33
|
+
# shows as the name in the console
|
34
|
+
tags_to_delete.delete('Name')
|
35
|
+
|
36
|
+
# Tagging frequently fails so we retry with an exponential backoff, a maximum of 10 seconds
|
37
|
+
Retryable.retryable(
|
38
|
+
:tries => 20,
|
39
|
+
:sleep => lambda { |n| [2**n, 10].min },
|
40
|
+
:on => [AWS::Errors::Base, Aws::Errors::ServiceError,]
|
41
|
+
) do |retries, exception|
|
42
|
+
if retries > 0
|
43
|
+
Chef::Log.info "Retrying the tagging, previous try failed with #{exception.inspect}"
|
44
|
+
end
|
45
|
+
unless tags_to_set.empty?
|
46
|
+
action_handler.perform_action "creating tags #{tags_to_set}" do
|
47
|
+
set_tags(tags_to_set)
|
48
|
+
end
|
49
|
+
tags_to_set = []
|
50
|
+
end
|
51
|
+
unless tags_to_delete.empty?
|
52
|
+
action_handler.perform_action "deleting tags #{tags_to_delete}" do
|
53
|
+
delete_tags(tags_to_delete)
|
54
|
+
end
|
55
|
+
tags_to_delete = []
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|