chef-provisioning-aws 1.2.1 → 1.3.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 (31) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +21 -8
  3. data/lib/chef/provider/aws_cache_cluster.rb +75 -0
  4. data/lib/chef/provider/aws_cache_replication_group.rb +49 -0
  5. data/lib/chef/provider/aws_cache_subnet_group.rb +60 -0
  6. data/lib/chef/provider/aws_instance.rb +4 -1
  7. data/lib/chef/provider/aws_key_pair.rb +1 -1
  8. data/lib/chef/provider/aws_network_acl.rb +131 -0
  9. data/lib/chef/provider/aws_security_group.rb +1 -1
  10. data/lib/chef/provider/aws_subnet.rb +14 -0
  11. data/lib/chef/provider/aws_vpc.rb +1 -0
  12. data/lib/chef/provisioning/aws_driver.rb +4 -0
  13. data/lib/chef/provisioning/aws_driver/aws_provider.rb +25 -0
  14. data/lib/chef/provisioning/aws_driver/aws_resource.rb +7 -2
  15. data/lib/chef/provisioning/aws_driver/driver.rb +59 -24
  16. data/lib/chef/provisioning/aws_driver/version.rb +1 -1
  17. data/lib/chef/resource/aws_cache_cluster.rb +37 -0
  18. data/lib/chef/resource/aws_cache_replication_group.rb +37 -0
  19. data/lib/chef/resource/aws_cache_subnet_group.rb +28 -0
  20. data/lib/chef/resource/aws_network_acl.rb +61 -0
  21. data/lib/chef/resource/aws_subnet.rb +9 -0
  22. data/spec/aws_support.rb +4 -1
  23. data/spec/aws_support/matchers/match_an_aws_object.rb +58 -0
  24. data/spec/integration/aws_cache_subnet_group_spec.rb +32 -0
  25. data/spec/integration/aws_key_pair_spec.rb +2 -2
  26. data/spec/integration/aws_network_acl_spec.rb +107 -0
  27. data/spec/integration/aws_security_group_spec.rb +16 -0
  28. data/spec/integration/aws_subnet_spec.rb +8 -11
  29. data/spec/integration/aws_tagged_items_spec.rb +1 -1
  30. data/spec/integration/aws_vpc_spec.rb +16 -0
  31. metadata +27 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 09d8cd1f113c14f6f206a03b49d89cae4d9294a6
4
- data.tar.gz: 0fbe0de96accf8f7cc030920f108f1a68df09ece
3
+ metadata.gz: 77fad1ecfeeb4dabe26c39612eb4c1bde17b8e1a
4
+ data.tar.gz: 643e9aa5dd7a32eff6c87e5882aeccfc72c971d4
5
5
  SHA512:
6
- metadata.gz: b5d93a2add78c31d46ced5badef87b3ead4da4fb203b2b00352f15e569e309afc1d2ccc87a2bf2c2f2468f5e02de64908144d0f577a983e7462b17170f2eae51
7
- data.tar.gz: 37d9e8b8c2461bcf26a8488127a3ab241c0e731bc7ad3e3dd98f4e892906d8bb8d8b94dc2d9a07da9bdca2841c2f642f65bbb319a64a15c1283bb2aa7f084f64
6
+ metadata.gz: 274e85302ec8c237728a247f79da29e3a7550ba1a8209fbd26e36fd5fe48f3043fe11cc62f393460790d61b77a78049048febc58bc64783c41ff057d80fd8bf2
7
+ data.tar.gz: 71cfa5429bec399ce37837713521e3653c8d0f4b10f30f2a726b48327fa40e774255b580e932e986cf0e870d7c532340cce360958ca36e6c3ff491866e00cb28
data/README.md CHANGED
@@ -2,11 +2,22 @@
2
2
 
3
3
  This README is a work in progress. Please add to it!
4
4
 
5
+ # Prerequesites
6
+
7
+ ## Credentials
8
+
9
+ AWS credentials should be specified in your `~/.aws/credentials` file as documented [here](http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html#cli-config-files). We support the use of profiles as well. If you do not specify a profile then we use the `default` profile.
10
+
11
+ You can specify a profile as the middle section of the semi-colon seperated driver url. For example, a driver url of `aws:staging:us-east-1` would use the profile `staging`.
12
+
5
13
  # Resources
6
14
 
7
15
  TODO: List out weird/unique things about resources here. We don't need to document every resource
8
16
  because users can look at the resource model.
9
17
 
18
+ TODO: document `aws_object` and `get_aws_object` and how you can get the aws object for a base
19
+ chef-provisioning resource like machine or load_balancer
20
+
10
21
  ## aws_vpc
11
22
 
12
23
  If you specify `internet_gateway true` the VPC will create and manage its own internet gateway.
@@ -170,12 +181,14 @@ machine_image 'my_image' do
170
181
  end
171
182
 
172
183
  ruby_block "look up machine_image object" do
173
- aws_object = Chef::Resource::AwsImage.get_aws_object(
174
- 'my_image',
175
- run_context: run_context,
176
- driver: run_context.chef_provisioning.current_driver,
177
- managed_entry_store: Chef::Provisioning.chef_managed_entry_store(self.chef_server)
178
- )
184
+ block do
185
+ aws_object = Chef::Resource::AwsImage.get_aws_object(
186
+ 'my_image',
187
+ run_context: run_context,
188
+ driver: run_context.chef_provisioning.current_driver,
189
+ managed_entry_store: Chef::Provisioning.chef_managed_entry_store(run_context.cheffish.current_chef_server)
190
+ )
191
+ end
179
192
  end
180
193
  ```
181
194
 
@@ -188,10 +201,10 @@ available using a `lazy` attribute modifier or in a `ruby_block`.
188
201
 
189
202
  # Running Integration Tests
190
203
 
191
- To run the integration tests execute `bundle exec rake integration`. If you have not set it up,
204
+ To run the integration tests execute `bundle exec rspec`. If you have not set it up,
192
205
  you should see an error message about a missing environment variable `AWS_TEST_DRIVER`. You can add
193
206
  this as a normal environment variable or set it for a single run with `AWS_TEST_DRIVER=aws::eu-west-1
194
- bundle exec rake integration`. The format should match what `with_driver` expects.
207
+ bundle exec rspec`. The format should match what `with_driver` expects.
195
208
 
196
209
  You will also need to have configured your `~/.aws/config` or environment variables with your
197
210
  AWS credentials.
@@ -0,0 +1,75 @@
1
+ require 'chef/provisioning/aws_driver/aws_provider'
2
+
3
+ class Chef::Provider::AwsCacheCluster < Chef::Provisioning::AWSDriver::AWSProvider
4
+
5
+ protected
6
+
7
+ def create_aws_object
8
+ converge_by "create new Elasticache Cluster #{new_resource.name} in #{region}" do
9
+ driver.create_cache_cluster(desired_options)
10
+ end
11
+ end
12
+
13
+ def update_aws_object(cache_cluster)
14
+ if update_required?(cache_cluster)
15
+ converge_by "update Elasticache Cluster #{new_resource.name} in #{region}" do
16
+ driver.modify_cache_cluster(
17
+ updatable_options(desired_options).merge(
18
+ cache_cluster_id: cache_cluster[:cache_cluster_id]
19
+ )
20
+ )
21
+ end
22
+ end
23
+ end
24
+
25
+ def destroy_aws_object(cache_cluster)
26
+ converge_by "delete Elasticache Cluster #{new_resource.name} in #{region}" do
27
+ driver.delete_cache_cluster(
28
+ cache_cluster_id: cache_cluster[:cache_cluster_id]
29
+ )
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def driver
36
+ new_resource.driver.elasticache
37
+ end
38
+
39
+ def desired_options
40
+ @desired_options ||= begin
41
+ options = {}
42
+ options[:cache_cluster_id] = new_resource.cluster_name
43
+ options[:num_cache_nodes] = new_resource.number_nodes
44
+ options[:cache_node_type] = new_resource.node_type
45
+ options[:engine] = new_resource.engine
46
+ options[:az_mode] = new_resource.az_mode if new_resource.az_mode
47
+ options[:preferred_availability_zone] =
48
+ new_resource.preferred_availability_zone if new_resource.preferred_availability_zone
49
+ options[:preferred_availability_zones] =
50
+ new_resource.preferred_availability_zones if new_resource.preferred_availability_zones
51
+ options[:engine_version] = new_resource.engine_version
52
+ options[:cache_subnet_group_name] =
53
+ new_resource.subnet_group_name if new_resource.subnet_group_name
54
+ options[:security_group_ids] = new_resource.security_groups
55
+ AWSResource.lookup_options(options, resource: new_resource)
56
+ end
57
+ end
58
+
59
+ def updatable_options(options)
60
+ updatable = [:security_groups, :num_cache_nodes, :engine_version]
61
+ options.delete_if { |option, _value| !updatable.include?(option) }
62
+ end
63
+
64
+ def update_required?(cache_cluster)
65
+ current_sg_ids = cache_cluster[:security_groups].map { |sg| sg[:security_group_id] }.sort
66
+
67
+ if desired_options[:security_group_ids].sort != current_sg_ids ||
68
+ desired_options[:num_cache_nodes] != cache_cluster[:num_cache_nodes] ||
69
+ desired_options[:engine_version] != cache_cluster[:engine_version]
70
+ true
71
+ else
72
+ false
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,49 @@
1
+ require 'chef/provisioning/aws_driver/aws_provider'
2
+
3
+ class Chef::Provider::AwsCacheReplicationGroup < Chef::Provisioning::AWSDriver::AWSProvider
4
+
5
+ protected
6
+
7
+ def create_aws_object
8
+ converge_by "create new Elasticache Replication Group #{new_resource.name} in #{region}" do
9
+ driver.create_replication_group(desired_options)
10
+ end
11
+ end
12
+
13
+ def update_aws_object(cache_replication_group)
14
+ Chef::Log.warn('Updating Elasticache Replication Groups is currently unsupported')
15
+ end
16
+
17
+ def destroy_aws_object(cache_replication_group)
18
+ converge_by "delete Elasticache Replication group #{new_resource.name} in #{region}" do
19
+ driver.delete_replication_group(
20
+ replication_group_id: cache_replication_group[:replication_group_id]
21
+ )
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def driver
28
+ new_resource.driver.elasticache
29
+ end
30
+
31
+ def desired_options
32
+ @desired_options ||= begin
33
+ options = {}
34
+ options[:replication_group_id] = new_resource.group_name
35
+ options[:replication_group_description] = new_resource.description
36
+ options[:automatic_failover_enabled] = new_resource.automatic_failover
37
+ options[:num_cache_clusters] = new_resource.number_cache_clusters
38
+ options[:cache_node_type] = new_resource.node_type
39
+ options[:engine] = new_resource.engine
40
+ options[:engine_version] = new_resource.engine_version
41
+ options[:preferred_cache_cluster_a_zs] =
42
+ new_resource.preferred_availability_zones if new_resource.preferred_availability_zones
43
+ options[:cache_subnet_group_name] =
44
+ new_resource.subnet_group_name if new_resource.subnet_group_name
45
+ options[:security_group_ids] = new_resource.security_groups
46
+ AWSResource.lookup_options(options, resource: new_resource)
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,60 @@
1
+ require 'chef/provisioning/aws_driver/aws_provider'
2
+
3
+ class Chef::Provider::AwsCacheSubnetGroup < Chef::Provisioning::AWSDriver::AWSProvider
4
+
5
+ protected
6
+
7
+ def create_aws_object
8
+ converge_by "create new Elasticache Subnet Group #{new_resource.name} in #{region}" do
9
+ driver.create_cache_subnet_group(desired_options)
10
+ end
11
+ end
12
+
13
+ def update_aws_object(cache_subnet_group)
14
+ if update_required?(cache_subnet_group)
15
+ converge_by "update Elasticache Subnet Group #{new_resource.name} in #{region}" do
16
+ driver.modify_cache_subnet_group(desired_options)
17
+ end
18
+ end
19
+ end
20
+
21
+ def destroy_aws_object(cache_subnet_group)
22
+ converge_by "delete Elasticache Subnet Group #{new_resource.name} in #{region}" do
23
+ driver.delete_cache_subnet_group(
24
+ cache_subnet_group_name: cache_subnet_group[:cache_subnet_group_name]
25
+ )
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def driver
32
+ new_resource.driver.elasticache
33
+ end
34
+
35
+ def update_cache_subnet_group
36
+ new_resource.driver.elasticache.modify_cache_subnet_group(desired_options)
37
+ end
38
+
39
+ def desired_options
40
+ @desired_options ||= begin
41
+ options = {}
42
+ options[:cache_subnet_group_name] = new_resource.group_name
43
+ options[:cache_subnet_group_description] = new_resource.description
44
+ options[:subnet_ids] = new_resource.subnets
45
+ AWSResource.lookup_options(options, resource: new_resource)
46
+ end
47
+ end
48
+
49
+ def update_required?(cache_subnet_group)
50
+ current_subnet_ids = cache_subnet_group[:subnets]
51
+ .map { |subnet| subnet[:subnet_identifier] }.sort
52
+ current_description = cache_subnet_group[:cache_subnet_group_description]
53
+ if new_resource.description != current_description ||
54
+ desired_options[:subnet_ids].sort != current_subnet_ids
55
+ true
56
+ else
57
+ false
58
+ end
59
+ end
60
+ end
@@ -6,7 +6,10 @@ class Chef::Provider::AwsInstance < Chef::Provisioning::AWSDriver::AWSProvider
6
6
  def update_aws_object(instance); end
7
7
 
8
8
  def destroy_aws_object(instance)
9
- converge_by "delete instance #{new_resource} in VPC #{instance.vpc.id} in #{region}" do
9
+ message = "delete instance #{new_resource}"
10
+ message += " in VPC #{instance.vpc.id}" unless instance.vpc.nil?
11
+ message += " in #{region}"
12
+ converge_by message do
10
13
  instance.delete
11
14
  end
12
15
  converge_by "waited until instance #{new_resource} is :terminated" do
@@ -168,7 +168,7 @@ class Chef::Provider::AwsKeyPair < Chef::Provisioning::AWSDriver::AWSProvider
168
168
 
169
169
  current_key_pair = new_resource.aws_object
170
170
  if current_key_pair
171
- @current_fingerprint = current_key_pair ? current_key_pair.fingerprint : nil
171
+ @current_fingerprint = current_key_pair.fingerprint
172
172
  else
173
173
  current_resource.action :destroy
174
174
  end
@@ -0,0 +1,131 @@
1
+ require 'chef/provisioning/aws_driver/aws_provider'
2
+ require 'chef/resource/aws_vpc'
3
+ require 'retryable'
4
+
5
+ class Chef::Provider::AwsNetworkAcl < Chef::Provisioning::AWSDriver::AWSProvider
6
+ def action_create
7
+ network_acl = super
8
+
9
+ apply_rules(network_acl)
10
+ end
11
+
12
+ protected
13
+
14
+ def create_aws_object
15
+ converge_by "create new Network ACL #{new_resource.name} in #{region}" do
16
+ options = {}
17
+ options[:vpc] = new_resource.vpc if new_resource.vpc
18
+ options = AWSResource.lookup_options(options, resource: new_resource)
19
+
20
+ Chef::Log.debug("VPC: #{options[:vpc]}")
21
+
22
+ network_acl = new_resource.driver.ec2.network_acls.create(options)
23
+ Retryable.retryable(:tries => 15, :sleep => 1, :on => AWS::EC2::Errors::InvalidNetworkAclID::NotFound) do
24
+ network_acl.tags['Name'] = new_resource.name
25
+ end
26
+ network_acl
27
+ end
28
+ end
29
+
30
+ def update_aws_object(network_acl)
31
+ if !new_resource.vpc.nil?
32
+ desired_vpc = Chef::Resource::AwsVpc.get_aws_object_id(new_resource.vpc, resource: new_resource)
33
+ if desired_vpc != network_acl.vpc_id
34
+ raise "Network ACL VPC cannot be changed after being created! Desired VPC for #{new_resource.to_s} was #{new_resource.vpc} (#{desired_vpc}) and actual VPC is #{network_acl.vpc_id}"
35
+ end
36
+ end
37
+ end
38
+
39
+ def destroy_aws_object(network_acl)
40
+ # TODO if purging, do we need to destory the linked subnets?
41
+ converge_by "delete #{new_resource.to_s} in #{region}" do
42
+ network_acl.delete
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def apply_rules(network_acl)
49
+ current_rules = network_acl.entries.map { |entry| entry_to_hash(entry) }
50
+ inbound_rules = new_resource.inbound_rules
51
+ outbound_rules = new_resource.outbound_rules
52
+ # AWS requires a deny all rule at the end. Delete here so we don't
53
+ # try to compare.
54
+ current_rules.delete_if { |rule| rule[:rule_number] == 32767 }
55
+
56
+ current_inbound_rules = current_rules.select { |rule| rule[:egress] == false }
57
+ # If inbound_rules is nil, leave rules alone. If empty array, delete all.
58
+ if inbound_rules
59
+ desired_inbound_rules = inbound_rules.map { |rule| rule[:egress] = false; rule }
60
+ compare_and_apply_rules(network_acl, :ingress, current_inbound_rules, desired_inbound_rules)
61
+ end
62
+
63
+ current_outbound_rules = current_rules.select { |rule| rule[:egress] == true }
64
+ if outbound_rules
65
+ desired_outbound_rules = outbound_rules.map { |rule| rule[:egress] = true; rule }
66
+ compare_and_apply_rules(network_acl, :egress, current_outbound_rules, desired_outbound_rules)
67
+ end
68
+ end
69
+
70
+ def compare_and_apply_rules(network_acl, direction, current_rules, desired_rules)
71
+ replace_rules = []
72
+
73
+ # Get the desired rules in a comparable state
74
+ desired_rules.clone.each do |desired_rule|
75
+ matching_rule = current_rules.select { |r| r[:rule_number] == desired_rule[:rule_number]}.first
76
+ if matching_rule
77
+ # Anything unhandled will be removed
78
+ current_rules.delete(matching_rule)
79
+ # Anything unhandled will be added
80
+ desired_rules.delete(desired_rule)
81
+
82
+ if matching_rule.merge(desired_rule) != matching_rule
83
+ # Replace anything with a matching rule number but different attributes
84
+ replace_rules << desired_rule
85
+ end
86
+ end
87
+ end
88
+
89
+ unless replace_rules.empty? && desired_rules.empty? && current_rules.empty?
90
+ action_handler.report_progress "update Network ACL #{new_resource.name} #{direction.to_s} rules"
91
+ replace_rules(network_acl, replace_rules)
92
+ add_rules(network_acl, desired_rules)
93
+ remove_rules(network_acl, current_rules)
94
+ end
95
+ end
96
+
97
+ def replace_rules(network_acl, rules)
98
+ rules.each do |rule|
99
+ action_handler.report_progress " update #{rule_direction(rule)} rule #{rule[:rule_number]}"
100
+ network_acl.replace_entry(rule)
101
+ end
102
+ end
103
+
104
+ def add_rules(network_acl, rules)
105
+ rules.each do |rule|
106
+ action_handler.report_progress " add #{rule_direction(rule)} rule #{rule[:rule_number]}"
107
+ network_acl.create_entry(rule)
108
+ end
109
+ end
110
+
111
+ def remove_rules(network_acl, rules)
112
+ rules.each do |rule|
113
+ action_handler.report_progress " remove #{rule_direction(rule)} rule #{rule[:rule_number]}"
114
+ network_acl.delete_entry(rule_direction(rule).to_sym, rule[:rule_number])
115
+ end
116
+ end
117
+
118
+ def rule_direction(rule)
119
+ rule[:egress] == true ? 'egress' : 'ingress'
120
+ end
121
+
122
+ def entry_to_hash(entry)
123
+ options = [
124
+ :rule_number, :action, :protocol, :cidr_block, :egress,
125
+ :port_range, :icmp_code, :icmp_type
126
+ ]
127
+ entry_hash = {}
128
+ options.each { |option| entry_hash.merge!(option => entry.send(option.to_sym)) }
129
+ entry_hash
130
+ end
131
+ end
@@ -148,7 +148,7 @@ class Chef::Provider::AwsSecurityGroup < Chef::Provisioning::AWSDriver::AWSProvi
148
148
  actual_rules = {}
149
149
  actual_rules_list.each do |rule|
150
150
  port_range = {
151
- port_range: rule[:from_port] ? rule[:from_port]..rule[:to_port] : nil,
151
+ port_range: rule[:from_port] ? rule[:from_port]..rule[:to_port] : -1..-1,
152
152
  protocol: rule[:ip_protocol].to_s.to_sym
153
153
  }
154
154
  add_rule(actual_rules, [ port_range ], rule[:groups]) if rule[:groups]
@@ -15,6 +15,8 @@ class Chef::Provider::AwsSubnet < Chef::Provisioning::AWSDriver::AWSProvider
15
15
  if new_resource.route_table != nil
16
16
  update_route_table(subnet)
17
17
  end
18
+
19
+ update_network_acl(subnet)
18
20
  end
19
21
 
20
22
  protected
@@ -124,4 +126,16 @@ class Chef::Provider::AwsSubnet < Chef::Provisioning::AWSDriver::AWSProvider
124
126
  end
125
127
  end
126
128
  end
129
+
130
+ def update_network_acl(subnet)
131
+ if new_resource.network_acl
132
+ network_acl_id =
133
+ AWSResource.lookup_options({ network_acl: new_resource.network_acl }, resource: new_resource)[:network_acl]
134
+ if subnet.network_acl.id != network_acl_id
135
+ converge_by "update network acl of subnet #{new_resource.name} to #{new_resource.network_acl}" do
136
+ subnet.network_acl = network_acl_id
137
+ end
138
+ end
139
+ end
140
+ end
127
141
  end