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.
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