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.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +70 -69
  3. data/Rakefile +22 -2
  4. data/lib/chef/provider/aws_auto_scaling_group.rb +3 -2
  5. data/lib/chef/provider/aws_cache_cluster.rb +3 -2
  6. data/lib/chef/provider/aws_cache_replication_group.rb +5 -4
  7. data/lib/chef/provider/aws_cache_subnet_group.rb +5 -4
  8. data/lib/chef/provider/aws_cloudsearch_domain.rb +163 -0
  9. data/lib/chef/provider/aws_dhcp_options.rb +9 -6
  10. data/lib/chef/provider/aws_ebs_volume.rb +7 -3
  11. data/lib/chef/provider/aws_eip_address.rb +8 -7
  12. data/lib/chef/provider/aws_image.rb +8 -3
  13. data/lib/chef/provider/aws_instance.rb +14 -2
  14. data/lib/chef/provider/aws_key_pair.rb +2 -1
  15. data/lib/chef/provider/aws_launch_configuration.rb +4 -2
  16. data/lib/chef/provider/aws_load_balancer.rb +18 -0
  17. data/lib/chef/provider/aws_network_acl.rb +6 -2
  18. data/lib/chef/provider/aws_network_interface.rb +11 -24
  19. data/lib/chef/provider/aws_rds_instance.rb +66 -0
  20. data/lib/chef/provider/aws_rds_subnet_group.rb +89 -0
  21. data/lib/chef/provider/aws_route_table.rb +42 -23
  22. data/lib/chef/provider/aws_s3_bucket.rb +32 -8
  23. data/lib/chef/provider/aws_security_group.rb +11 -4
  24. data/lib/chef/provider/aws_server_certificate.rb +23 -0
  25. data/lib/chef/provider/aws_sns_topic.rb +4 -3
  26. data/lib/chef/provider/aws_sqs_queue.rb +3 -2
  27. data/lib/chef/provider/aws_subnet.rb +10 -7
  28. data/lib/chef/provider/aws_vpc.rb +54 -21
  29. data/lib/chef/provider/aws_vpc_peering_connection.rb +88 -0
  30. data/lib/chef/provisioning/aws_driver.rb +8 -0
  31. data/lib/chef/provisioning/aws_driver/aws_provider.rb +45 -76
  32. data/lib/chef/provisioning/aws_driver/aws_rds_resource.rb +11 -0
  33. data/lib/chef/provisioning/aws_driver/aws_resource.rb +14 -2
  34. data/lib/chef/provisioning/aws_driver/aws_resource_with_entry.rb +2 -8
  35. data/lib/chef/provisioning/aws_driver/aws_taggable.rb +18 -0
  36. data/lib/chef/provisioning/aws_driver/aws_tagger.rb +61 -0
  37. data/lib/chef/provisioning/aws_driver/credentials2.rb +51 -0
  38. data/lib/chef/provisioning/aws_driver/driver.rb +214 -162
  39. data/lib/chef/provisioning/aws_driver/tagging_strategy/ec2.rb +64 -0
  40. data/lib/chef/provisioning/aws_driver/tagging_strategy/elb.rb +39 -0
  41. data/lib/chef/provisioning/aws_driver/tagging_strategy/rds.rb +92 -0
  42. data/lib/chef/provisioning/aws_driver/tagging_strategy/s3.rb +41 -0
  43. data/lib/chef/provisioning/aws_driver/version.rb +1 -1
  44. data/lib/chef/resource/aws_cache_cluster.rb +1 -2
  45. data/lib/chef/resource/aws_cloudsearch_domain.rb +46 -0
  46. data/lib/chef/resource/aws_dhcp_options.rb +2 -0
  47. data/lib/chef/resource/aws_ebs_volume.rb +3 -1
  48. data/lib/chef/resource/aws_eip_address.rb +0 -3
  49. data/lib/chef/resource/aws_image.rb +3 -0
  50. data/lib/chef/resource/aws_instance.rb +7 -2
  51. data/lib/chef/resource/aws_internet_gateway.rb +2 -0
  52. data/lib/chef/resource/aws_load_balancer.rb +3 -0
  53. data/lib/chef/resource/aws_network_acl.rb +2 -0
  54. data/lib/chef/resource/aws_network_interface.rb +3 -1
  55. data/lib/chef/resource/aws_rds_instance.rb +42 -0
  56. data/lib/chef/resource/aws_rds_subnet_group.rb +29 -0
  57. data/lib/chef/resource/aws_route_table.rb +7 -5
  58. data/lib/chef/resource/aws_s3_bucket.rb +3 -0
  59. data/lib/chef/resource/aws_security_group.rb +2 -7
  60. data/lib/chef/resource/aws_server_certificate.rb +21 -0
  61. data/lib/chef/resource/aws_subnet.rb +2 -0
  62. data/lib/chef/resource/aws_vpc.rb +4 -1
  63. data/lib/chef/resource/aws_vpc_peering_connection.rb +73 -0
  64. data/spec/acceptance/aws_ebs_volume/nodes/ettores-mbp.lan.json +3 -0
  65. data/spec/aws_support.rb +25 -8
  66. data/spec/aws_support/aws_resource_run_wrapper.rb +5 -1
  67. data/spec/aws_support/deep_matcher/match_values_failure_messages.rb +19 -0
  68. data/spec/aws_support/matchers/create_an_aws_object.rb +1 -1
  69. data/spec/aws_support/matchers/destroy_an_aws_object.rb +1 -1
  70. data/spec/aws_support/matchers/have_aws_object_tags.rb +9 -15
  71. data/spec/aws_support/matchers/match_an_aws_object.rb +1 -1
  72. data/spec/aws_support/matchers/update_an_aws_object.rb +1 -1
  73. data/spec/integration/aws_cloudsearch_domain_spec.rb +31 -0
  74. data/spec/integration/aws_dhcp_options_spec.rb +73 -0
  75. data/spec/integration/aws_ebs_volume_spec.rb +97 -0
  76. data/spec/integration/aws_network_acl_spec.rb +51 -0
  77. data/spec/integration/aws_network_interface_spec.rb +89 -0
  78. data/spec/integration/aws_rds_instance_spec.rb +150 -0
  79. data/spec/integration/aws_rds_subnet_group_spec.rb +105 -0
  80. data/spec/integration/aws_route_table_spec.rb +94 -7
  81. data/spec/integration/aws_s3_bucket_spec.rb +88 -0
  82. data/spec/integration/aws_security_group_spec.rb +47 -0
  83. data/spec/integration/aws_server_certificate_spec.rb +24 -0
  84. data/spec/integration/aws_subnet_spec.rb +51 -2
  85. data/spec/integration/aws_vpc_peering_connection_spec.rb +99 -0
  86. data/spec/integration/aws_vpc_spec.rb +73 -0
  87. data/spec/integration/load_balancer_spec.rb +101 -0
  88. data/spec/integration/machine_image_spec.rb +61 -6
  89. data/spec/integration/machine_spec.rb +26 -0
  90. data/spec/spec_helper.rb +3 -0
  91. data/spec/unit/{aws_driver → chef/provisioning/aws_driver}/credentials_spec.rb +0 -0
  92. data/spec/unit/chef/provisioning/aws_driver/driver_spec.rb +88 -0
  93. metadata +63 -20
  94. 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
- acceptable_errors = [acceptable_errors].flatten
276
- expected_status = [expected_status].flatten
277
- current_status = aws_object.status
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
- Retryable.retryable(:tries => tries, :sleep => sleep) do |retries, exception|
280
- action_handler.report_progress "waited #{retries*sleep}/#{tries*sleep}s for #{aws_object.id} status to change to #{expected_status.inspect}..."
281
- begin
282
- current_status = aws_object.status
283
- unless expected_status.include?(current_status)
284
- raise StatusTimeoutError.new(aws_object, current_status, expected_status)
285
- end
286
- rescue *acceptable_errors
287
- end
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 expected_state
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 expected_state [Symbol,Array<Symbol>] Final state(s) to look for
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 wait_for_state(aws_object, expected_states, acceptable_errors = [], tries=60, sleep=5)
300
- acceptable_errors = [acceptable_errors].flatten
301
- expected_states = [expected_states].flatten
302
- current_state = aws_object.state
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 #{expected_states.inspect}..."
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
- current_state = aws_object.state
308
- unless expected_states.include?(current_state)
309
- raise StatusTimeoutError.new(aws_object, current_state, expected_states)
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, 10].min }, :on => retry_on, &block)
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.dsl_name
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.resource_name, name, action_handler)
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.resource_name, name)
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