chef-provisioning-aws 1.4.1 → 1.5.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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +8 -0
  3. data/README.md +26 -39
  4. data/Rakefile +13 -5
  5. data/lib/chef/provider/aws_iam_instance_profile.rb +60 -0
  6. data/lib/chef/provider/aws_iam_role.rb +98 -0
  7. data/lib/chef/provider/aws_image.rb +1 -1
  8. data/lib/chef/provider/aws_internet_gateway.rb +75 -0
  9. data/lib/chef/provider/aws_route_table.rb +3 -2
  10. data/lib/chef/provider/aws_s3_bucket.rb +4 -1
  11. data/lib/chef/provider/aws_security_group.rb +1 -1
  12. data/lib/chef/provider/aws_vpc.rb +50 -45
  13. data/lib/chef/provisioning/aws_driver.rb +22 -1
  14. data/lib/chef/provisioning/aws_driver/aws_provider.rb +13 -5
  15. data/lib/chef/provisioning/aws_driver/aws_resource.rb +173 -165
  16. data/lib/chef/provisioning/aws_driver/credentials.rb +12 -0
  17. data/lib/chef/provisioning/aws_driver/driver.rb +82 -37
  18. data/lib/chef/provisioning/aws_driver/super_lwrp.rb +56 -43
  19. data/lib/chef/provisioning/aws_driver/version.rb +1 -1
  20. data/lib/chef/resource/aws_dhcp_options.rb +1 -1
  21. data/lib/chef/resource/aws_ebs_volume.rb +1 -1
  22. data/lib/chef/resource/aws_eip_address.rb +1 -1
  23. data/lib/chef/resource/aws_iam_instance_profile.rb +33 -0
  24. data/lib/chef/resource/aws_iam_role.rb +55 -0
  25. data/lib/chef/resource/aws_image.rb +1 -1
  26. data/lib/chef/resource/aws_instance.rb +1 -1
  27. data/lib/chef/resource/aws_internet_gateway.rb +36 -6
  28. data/lib/chef/resource/aws_load_balancer.rb +1 -1
  29. data/lib/chef/resource/aws_network_acl.rb +1 -1
  30. data/lib/chef/resource/aws_network_interface.rb +1 -1
  31. data/lib/chef/resource/aws_route53_hosted_zone.rb +261 -0
  32. data/lib/chef/resource/aws_route53_record_set.rb +162 -0
  33. data/lib/chef/resource/aws_route_table.rb +1 -1
  34. data/lib/chef/resource/aws_security_group.rb +1 -1
  35. data/lib/chef/resource/aws_sns_topic.rb +1 -1
  36. data/lib/chef/resource/aws_subnet.rb +1 -1
  37. data/lib/chef/resource/aws_vpc.rb +1 -1
  38. data/lib/chef/resource/aws_vpc_peering_connection.rb +1 -1
  39. data/spec/aws_support.rb +11 -13
  40. data/spec/aws_support/matchers/create_an_aws_object.rb +7 -1
  41. data/spec/aws_support/matchers/have_aws_object_tags.rb +1 -1
  42. data/spec/aws_support/matchers/match_an_aws_object.rb +7 -1
  43. data/spec/aws_support/matchers/update_an_aws_object.rb +8 -2
  44. data/spec/integration/aws_eip_address_spec.rb +74 -0
  45. data/spec/integration/aws_iam_instance_profile_spec.rb +159 -0
  46. data/spec/integration/aws_iam_role_spec.rb +177 -0
  47. data/spec/integration/aws_internet_gateway_spec.rb +161 -0
  48. data/spec/integration/aws_network_interface_spec.rb +3 -4
  49. data/spec/integration/aws_route53_hosted_zone_spec.rb +522 -0
  50. data/spec/integration/aws_route_table_spec.rb +52 -4
  51. data/spec/integration/aws_s3_bucket_spec.rb +1 -1
  52. data/spec/integration/load_balancer_spec.rb +303 -8
  53. data/spec/integration/machine_batch_spec.rb +1 -0
  54. data/spec/integration/machine_image_spec.rb +32 -17
  55. data/spec/integration/machine_spec.rb +11 -29
  56. data/spec/unit/chef/provisioning/aws_driver/driver_spec.rb +0 -1
  57. data/spec/unit/chef/provisioning/aws_driver/route53_spec.rb +105 -0
  58. metadata +48 -6
@@ -91,7 +91,7 @@ class Chef::Resource::AwsRouteTable < Chef::Provisioning::AWSDriver::AWSResource
91
91
  attribute :ignore_route_targets, kind_of: [ String, Array ], default: [],
92
92
  coerce: proc { |v| [v].flatten }
93
93
 
94
- attribute :route_table_id, kind_of: String, aws_id_attribute: true, lazy_default: proc {
94
+ attribute :route_table_id, kind_of: String, aws_id_attribute: true, default: lazy {
95
95
  name =~ /^rtb-[a-f0-9]{8}$/ ? name : nil
96
96
  }
97
97
 
@@ -49,7 +49,7 @@ class Chef::Resource::AwsSecurityGroup < Chef::Provisioning::AWSDriver::AWSResou
49
49
  attribute :inbound_rules, kind_of: [ Array, Hash ]
50
50
  attribute :outbound_rules, kind_of: [ Array, Hash ]
51
51
 
52
- attribute :security_group_id, kind_of: String, aws_id_attribute: true, lazy_default: proc {
52
+ attribute :security_group_id, kind_of: String, aws_id_attribute: true, default: lazy {
53
53
  name =~ /^sg-[a-f0-9]{8}$/ ? name : nil
54
54
  }
55
55
 
@@ -4,7 +4,7 @@ class Chef::Resource::AwsSnsTopic < Chef::Provisioning::AWSDriver::AWSResource
4
4
  aws_sdk_type AWS::SNS::Topic
5
5
 
6
6
  attribute :name, kind_of: String, name_attribute: true
7
- attribute :arn, kind_of: String, lazy_default: proc { driver.build_arn(service: 'sns', resource: name) }
7
+ attribute :arn, kind_of: String, default: lazy { driver.build_arn(service: 'sns', resource: name) }
8
8
 
9
9
  def aws_object
10
10
  result = driver.sns.topics[arn]
@@ -86,7 +86,7 @@ class Chef::Resource::AwsSubnet < Chef::Provisioning::AWSDriver::AWSResourceWith
86
86
  #
87
87
  attribute :network_acl, kind_of: [ String, AwsNetworkAcl, AWS::EC2::NetworkACL ]
88
88
 
89
- attribute :subnet_id, kind_of: String, aws_id_attribute: true, lazy_default: proc {
89
+ attribute :subnet_id, kind_of: String, aws_id_attribute: true, default: lazy {
90
90
  name =~ /^subnet-[a-f0-9]{8}$/ ? name : nil
91
91
  }
92
92
 
@@ -128,7 +128,7 @@ class Chef::Resource::AwsVpc < Chef::Provisioning::AWSDriver::AWSResourceWithEnt
128
128
  #
129
129
  attribute :enable_dns_hostnames, equal_to: [ true, false ]
130
130
 
131
- attribute :vpc_id, kind_of: String, aws_id_attribute: true, lazy_default: proc {
131
+ attribute :vpc_id, kind_of: String, aws_id_attribute: true, default: lazy {
132
132
  name =~ /^vpc-[a-f0-9]{8}$/ ? name : nil
133
133
  }
134
134
 
@@ -54,7 +54,7 @@ class Chef::Resource::AwsVpcPeeringConnection < Chef::Provisioning::AWSDriver::A
54
54
  #
55
55
  attribute :peer_owner_id, kind_of: String
56
56
 
57
- attribute :vpc_peering_connection_id, kind_of: String, aws_id_attribute: true, lazy_default: proc {
57
+ attribute :vpc_peering_connection_id, kind_of: String, aws_id_attribute: true, default: lazy {
58
58
  name =~ /^pcx-[a-f0-9]{8}$/ ? name : nil
59
59
  }
60
60
 
@@ -23,7 +23,7 @@ module AWSSupport
23
23
  require 'aws'
24
24
  require 'aws_support/deep_matcher/matchable_object'
25
25
  require 'aws_support/deep_matcher/matchable_array'
26
- DeepMatcher::MatchableObject.matchable_classes << proc { |o| o.class.name =~ /^(AWS|Aws)::(EC2|ELB|IAM|S3|RDS|CloudSearch)($|::)/ }
26
+ DeepMatcher::MatchableObject.matchable_classes << proc { |o| o.class.name =~ /^(AWS|Aws)::(EC2|ELB|IAM|S3|RDS|CloudSearch|Route53)($|::)/ }
27
27
  DeepMatcher::MatchableArray.matchable_classes << AWS::Core::Data::List
28
28
 
29
29
  def purge_all
@@ -44,7 +44,7 @@ module AWSSupport
44
44
 
45
45
  def setup_public_vpc
46
46
  aws_vpc 'test_vpc' do
47
- cidr_block '10.0.0.0/24'
47
+ cidr_block '10.0.0.0/16'
48
48
  internet_gateway true
49
49
  enable_dns_hostnames true
50
50
  main_routes '0.0.0.0/0' => :internet_gateway
@@ -72,9 +72,12 @@ module AWSSupport
72
72
  outbound_rules [ 22, 80 ] => '0.0.0.0/0'
73
73
  end
74
74
 
75
+ azs = driver.ec2_client.describe_availability_zones.availability_zones.map {|r| r.zone_name}
75
76
  aws_subnet 'test_public_subnet' do
76
77
  vpc 'test_vpc'
78
+ cidr_block '10.0.0.0/24'
77
79
  map_public_ip_on_launch true
80
+ availability_zone azs.first
78
81
  end
79
82
  end
80
83
 
@@ -96,11 +99,6 @@ module AWSSupport
96
99
  aws_driver = Chef::Provisioning.driver_for_url(ENV['AWS_TEST_DRIVER'])
97
100
  when_the_repository "exists #{description ? "and #{description}" : ""}", *tags, &context_block
98
101
  else
99
- # warn <<EOM
100
- # --------------------------------------------------------------------------------------------------------------------------
101
- # AWS_TEST_DRIVER not set ... cannot run AWS test. Set AWS_TEST_DRIVER=aws or aws:profile:region to run tests that hit AWS.
102
- # --------------------------------------------------------------------------------------------------------------------------
103
- # EOM
104
102
  skip "AWS_TEST_DRIVER not set ... cannot run AWS tests. Set AWS_TEST_DRIVER=aws or aws:profile:region to run tests that hit AWS." do
105
103
  context description, *tags, &context_block
106
104
  end
@@ -202,11 +200,11 @@ module AWSSupport
202
200
  Chef::Provisioning::AWSDriver::Resources.constants.each do |resource_class|
203
201
  resource_class = Chef::Provisioning::AWSDriver::Resources.const_get(resource_class)
204
202
  resource_name = resource_class.resource_name
205
- define_method("update_an_#{resource_name}") do |name, expected_updates={}|
206
- AWSSupport::Matchers::UpdateAnAWSObject.new(self, resource_class, name, expected_updates)
203
+ define_method("update_an_#{resource_name}") do |name, expected_updates={}, &block|
204
+ AWSSupport::Matchers::UpdateAnAWSObject.new(self, resource_class, name, expected_updates, block)
207
205
  end
208
- define_method("create_an_#{resource_name}") do |name, expected_values={}|
209
- AWSSupport::Matchers::CreateAnAWSObject.new(self, resource_class, name, expected_values)
206
+ define_method("create_an_#{resource_name}") do |name, expected_values={}, &block|
207
+ AWSSupport::Matchers::CreateAnAWSObject.new(self, resource_class, name, expected_values, block)
210
208
  end
211
209
  define_method("have_#{resource_name}_tags") do |name, expected_tags={}|
212
210
  AWSSupport::Matchers::HaveAWSObjectTags.new(self, resource_class, name, expected_tags)
@@ -214,8 +212,8 @@ module AWSSupport
214
212
  define_method("destroy_an_#{resource_name}") do |name, expected_values={}|
215
213
  AWSSupport::Matchers::DestroyAnAWSObject.new(self, resource_class, name)
216
214
  end
217
- define_method("match_an_#{resource_name}") do |name, expected_values={}|
218
- AWSSupport::Matchers::MatchAnAWSObject.new(self, resource_class, name, expected_values)
215
+ define_method("match_an_#{resource_name}") do |name, expected_values={}, &block|
216
+ AWSSupport::Matchers::MatchAnAWSObject.new(self, resource_class, name, expected_values, block)
219
217
  end
220
218
  end
221
219
 
@@ -8,17 +8,21 @@ module AWSSupport
8
8
  include RSpec::Matchers::Composable
9
9
  include AWSSupport::DeepMatcher
10
10
 
11
- def initialize(example, resource_class, name, expected_values)
11
+ # @param custom_matcher [Block] A block with 1 argument that will be provided the aws_obect
12
+ def initialize(example, resource_class, name, expected_values, custom_matcher)
12
13
  @example = example
13
14
  @resource_class = resource_class
14
15
  @name = name
15
16
  @expected_values = expected_values
17
+ @custom_matcher = custom_matcher
16
18
  end
17
19
 
18
20
  attr_reader :example
19
21
  attr_reader :resource_class
20
22
  attr_reader :name
21
23
  attr_reader :expected_values
24
+ attr_reader :custom_matcher
25
+
22
26
  def resource_name
23
27
  @resource_class.resource_name
24
28
  end
@@ -46,6 +50,8 @@ module AWSSupport
46
50
  resource.managed_entry_store Chef::Provisioning.chef_managed_entry_store
47
51
  aws_object = resource.aws_object
48
52
 
53
+ example.instance_exec aws_object, &custom_matcher if custom_matcher
54
+
49
55
  # Check existence
50
56
  if aws_object.nil?
51
57
  differences << "#{resource_name}[#{name}] was not created!"
@@ -27,7 +27,7 @@ module AWSSupport
27
27
  differences = []
28
28
 
29
29
  # Check for object existence and properties
30
- resource = resource_class.new(name, nil)
30
+ resource = resource_class.new(name, recipe.client.run_context)
31
31
  resource.driver example.driver
32
32
  resource.managed_entry_store Chef::Provisioning.chef_managed_entry_store
33
33
  @aws_object = resource.aws_object
@@ -13,17 +13,21 @@ module AWSSupport
13
13
  include RSpec::Matchers::Composable
14
14
  include AWSSupport::DeepMatcher
15
15
 
16
- def initialize(example, resource_class, name, expected_values)
16
+ # @param custom_matcher [Block] A block with 1 argument that will be provided the aws_obect
17
+ def initialize(example, resource_class, name, expected_values, custom_matcher)
17
18
  @example = example
18
19
  @resource_class = resource_class
19
20
  @name = name
20
21
  @expected_values = expected_values
22
+ @custom_matcher = custom_matcher
21
23
  end
22
24
 
23
25
  attr_reader :example
24
26
  attr_reader :resource_class
25
27
  attr_reader :name
26
28
  attr_reader :expected_values
29
+ attr_reader :custom_matcher
30
+
27
31
  def resource_name
28
32
  @resource_class.resource_name
29
33
  end
@@ -44,6 +48,8 @@ module AWSSupport
44
48
  resource.managed_entry_store Chef::Provisioning.chef_managed_entry_store
45
49
  aws_object = resource.aws_object
46
50
 
51
+ example.instance_exec aws_object, &custom_matcher if custom_matcher
52
+
47
53
  # Check existence
48
54
  if aws_object.nil?
49
55
  differences << "#{resource_name}[#{name}] AWS object did not exist!"
@@ -10,11 +10,13 @@ module AWSSupport
10
10
 
11
11
  require 'chef/provisioning'
12
12
 
13
- def initialize(example, resource_class, name, expected_updates)
13
+ # @param custom_matcher [Block] A block with 1 argument that will be provided the aws_obect
14
+ def initialize(example, resource_class, name, expected_updates, custom_matcher)
14
15
  @example = example
15
16
  @resource_class = resource_class
16
17
  @name = name
17
18
  @expected_updates = expected_updates
19
+ @custom_matcher = custom_matcher
18
20
 
19
21
  # Grab the "before" value
20
22
  resource = resource_class.new(name, nil)
@@ -27,10 +29,12 @@ module AWSSupport
27
29
  attr_reader :resource_class
28
30
  attr_reader :name
29
31
  attr_reader :expected_updates
32
+ attr_reader :custom_matcher
33
+ attr_reader :had_initial_value
34
+
30
35
  def resource_name
31
36
  @resource_class.resource_name
32
37
  end
33
- attr_reader :had_initial_value
34
38
 
35
39
  def match_failure_messages(recipe)
36
40
  differences = []
@@ -56,6 +60,8 @@ module AWSSupport
56
60
  resource.managed_entry_store Chef::Provisioning.chef_managed_entry_store
57
61
  aws_object = resource.aws_object
58
62
 
63
+ example.instance_exec aws_object, &custom_matcher if custom_matcher
64
+
59
65
  # Check to see if properties have the expected values
60
66
  differences += match_values_failure_messages(expected_updates, aws_object, "#{resource_name}[#{name}]")
61
67
 
@@ -0,0 +1,74 @@
1
+ require 'spec_helper'
2
+
3
+ describe Chef::Resource::AwsEipAddress do
4
+ extend AWSSupport
5
+
6
+ when_the_chef_12_server "exists", organization: 'foo', server_scope: :context do
7
+ with_aws "when connected to AWS" do
8
+
9
+ it "aws_eip_address 'test_eip' creates an elastic ip" do
10
+ expect_recipe {
11
+ aws_eip_address "test_eip"
12
+ }.to create_an_aws_eip_address('test_eip',
13
+ ).and be_idempotent
14
+ end
15
+
16
+ describe 'action :delete' do
17
+ with_converge {
18
+ aws_eip_address "test_eip"
19
+ }
20
+ it "deletes the elastic ip" do
21
+ # TODO all the `with_*` and `expect_*` methods from Cheffish
22
+ # automatically converge the block - we don't want to do that,
23
+ # we want to let the `destroy_an*` matcher do that
24
+ r = recipe {
25
+ aws_eip_address "test_eip" do
26
+ action :destroy
27
+ end
28
+ }
29
+ expect(r).to destroy_an_aws_eip_address('test_eip'
30
+ ).and be_idempotent
31
+ end
32
+ end
33
+
34
+ context "with existing machines", :super_slow do
35
+ purge_all
36
+ setup_public_vpc
37
+
38
+ machine 'test_machine' do
39
+ machine_options bootstrap_options: {
40
+ subnet_id: 'test_public_subnet',
41
+ key_name: 'test_key_pair'
42
+ }
43
+ action :ready # The box has to be online for AWS to accept it as routable
44
+ end
45
+
46
+ it "associates an EIP with a machine" do
47
+ test_machine_aws_obj = nil
48
+ expect_recipe {
49
+ ruby_block 'look up test machine' do
50
+ block do
51
+ test_machine_aws_obj = Chef::Resource::AwsInstance.get_aws_object(
52
+ 'test_machine',
53
+ run_context: run_context,
54
+ driver: run_context.chef_provisioning.current_driver,
55
+ managed_entry_store: Chef::Provisioning.chef_managed_entry_store(run_context.cheffish.current_chef_server)
56
+ )
57
+ end
58
+ end
59
+ }
60
+
61
+ expect_recipe {
62
+ aws_eip_address "test_eip" do
63
+ associate_to_vpc true
64
+ machine "test_machine"
65
+ end
66
+ }.to create_an_aws_eip_address('test_eip',
67
+ instance_id: test_machine_aws_obj.id
68
+ ).and be_idempotent
69
+ end
70
+
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,159 @@
1
+ require 'spec_helper'
2
+ require 'securerandom'
3
+
4
+ def mk_role_name
5
+ name_postfix = SecureRandom.hex(8)
6
+ "chef_provisioning_test_iam_role_#{name_postfix}"
7
+ end
8
+
9
+ def ec2_principal
10
+ <<EOF
11
+ {
12
+ "Version": "2012-10-17",
13
+ "Statement": [
14
+ {
15
+ "Action": "sts:AssumeRole",
16
+ "Principal": {
17
+ "Service": "ec2.amazonaws.com"
18
+ },
19
+ "Effect": "Allow",
20
+ "Sid": ""
21
+ }
22
+ ]
23
+ }
24
+ EOF
25
+ end
26
+
27
+ describe Chef::Resource::AwsIamRole do
28
+ extend AWSSupport
29
+
30
+ when_the_chef_12_server "exists", organization: "foo", server_scope: :context do
31
+ with_aws "when connected to AWS" do
32
+
33
+ let(:instance_name) {
34
+ name_postfix = SecureRandom.hex(8)
35
+ "cp_test_iam_instance_profile_#{name_postfix}"
36
+ }
37
+
38
+ it "creates an aws_iam_instance_profile with minimum attributes" do
39
+ expect_recipe {
40
+ aws_iam_instance_profile instance_name do
41
+ path "/"
42
+ end
43
+ }.to create_an_aws_iam_instance_profile(instance_name,
44
+ path: "/"
45
+ ).and be_idempotent
46
+ end
47
+
48
+ context "with an existing aws_iam_role" do
49
+ let(:role_name) {
50
+ name_postfix = SecureRandom.hex(8)
51
+ "cp_test_iam_role_#{name_postfix}"
52
+ }
53
+
54
+ # See aws_iam_role_spec.rb for explanation
55
+ before(:each) do
56
+ converge {
57
+ aws_iam_role role_name do
58
+ path "/"
59
+ assume_role_policy_document ec2_principal
60
+ end
61
+ }
62
+ end
63
+
64
+ after(:each) do
65
+ converge {
66
+ aws_iam_role role_name do
67
+ action :destroy
68
+ end
69
+ }
70
+ end
71
+
72
+ it "creates an aws_iam_instance_profile with maximum attributes" do
73
+ expect_recipe {
74
+ aws_iam_instance_profile instance_name do
75
+ path "/"
76
+ role role_name
77
+ end
78
+ }.to create_an_aws_iam_instance_profile(instance_name,
79
+ path: "/",
80
+ roles: [{name: role_name}]
81
+ ).and be_idempotent
82
+ end
83
+
84
+ context "with an existing aws_iam_instance_profile with an attached role" do
85
+ before(:each) do
86
+ converge {
87
+ aws_iam_instance_profile instance_name do
88
+ path "/"
89
+ role role_name
90
+ end
91
+ }
92
+ end
93
+
94
+ after(:each) do
95
+ converge {
96
+ aws_iam_instance_profile instance_name do
97
+ action :destroy
98
+ end
99
+ }
100
+ end
101
+
102
+ it "removes the relationship when the role is deleted" do
103
+ expect_recipe {
104
+ aws_iam_role role_name do
105
+ action :destroy
106
+ end
107
+ }.to match_an_aws_iam_instance_profile(instance_name,
108
+ roles: []
109
+ ).and be_idempotent
110
+ end
111
+
112
+ it "removes the relationship when the instance_profile is deleted" do
113
+ expect_recipe {
114
+ aws_iam_instance_profile instance_name do
115
+ action :destroy
116
+ end
117
+ }.to match_an_aws_iam_role(role_name,
118
+ instance_profiles: []
119
+ ).and be_idempotent
120
+ end
121
+
122
+ context "with a second aws_iam_role" do
123
+ before(:each) do
124
+ converge {
125
+ aws_iam_role "#{role_name}2" do
126
+ path "/"
127
+ assume_role_policy_document ec2_principal
128
+ end
129
+ }
130
+ end
131
+
132
+ after(:each) do
133
+ converge {
134
+ aws_iam_instance_profile instance_name do
135
+ action :destroy
136
+ end
137
+ }
138
+ end
139
+
140
+ it "updates the attached role" do
141
+ expect_recipe {
142
+ aws_iam_instance_profile instance_name do
143
+ path "/"
144
+ role "#{role_name}2"
145
+ end
146
+ }.to update_an_aws_iam_instance_profile(instance_name,
147
+ path: "/",
148
+ roles: [{name: "#{role_name}2"}]
149
+ ).and be_idempotent
150
+ end
151
+ end
152
+
153
+ end
154
+ end
155
+
156
+ end
157
+
158
+ end
159
+ end