chef-provisioning-aws 1.2.1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
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