chef-provisioning-aws 1.1.1 → 1.2.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.
@@ -1,13 +1,23 @@
1
- require 'chef/provisioning/aws_driver/aws_resource_with_entry'
1
+ require 'chef/provisioning/aws_driver/aws_resource'
2
2
  require 'chef/resource/aws_vpc'
3
+ require 'chef/provisioning/aws_driver/exceptions'
3
4
 
4
- class Chef::Resource::AwsSecurityGroup < Chef::Provisioning::AWSDriver::AWSResourceWithEntry
5
- aws_sdk_type AWS::EC2::SecurityGroup
5
+ class Chef::Resource::AwsSecurityGroup < Chef::Provisioning::AWSDriver::AWSResource
6
+ aws_sdk_type AWS::EC2::SecurityGroup,
7
+ id: :id,
8
+ option_names: [:security_group, :security_group_id, :security_group_name]
6
9
 
7
10
  attribute :name, kind_of: String, name_attribute: true
8
11
  attribute :vpc, kind_of: [ String, AwsVpc, AWS::EC2::VPC ]
9
12
  attribute :description, kind_of: String
10
13
 
14
+ # This should be a hash of tags to apply to the AWS object
15
+ # TODO this is duplicated from AWSResourceWithEntry
16
+ #
17
+ # @param aws_tags [Hash] Should be a hash of keys & values to add. Keys and values
18
+ # can be provided as symbols or strings, but will be stored in AWS as strings.
19
+ attribute :aws_tags, kind_of: Hash
20
+
11
21
  #
12
22
  # Accepts rules in the format:
13
23
  # [
@@ -49,8 +59,22 @@ class Chef::Resource::AwsSecurityGroup < Chef::Provisioning::AWSDriver::AWSResou
49
59
  }
50
60
 
51
61
  def aws_object
52
- driver, id = get_driver_and_id
53
- result = driver.ec2.security_groups[id] if id
62
+ if security_group_id
63
+ result = driver.ec2.security_groups[security_group_id]
64
+ else
65
+ # Names are unique within a VPC. Try to search by name and narroy by VPC, if
66
+ # provided
67
+ if vpc
68
+ vpc_object = Chef::Resource::AwsVpc.get_aws_object(vpc, resource: self)
69
+ results = vpc_object.security_groups.filter('group-name', name).to_a
70
+ else
71
+ results = driver.ec2.security_groups.filter('group-name', name).to_a
72
+ end
73
+ if results.size >= 2
74
+ raise ::Chef::Provisioning::AWSDriver::Exceptions::MultipleSecurityGroupError.new(name, results)
75
+ end
76
+ result = results.first
77
+ end
54
78
  result && result.exists? ? result : nil
55
79
  end
56
80
  end
@@ -13,6 +13,7 @@ module AWSSupport
13
13
  require 'aws_support/matchers/create_an_aws_object'
14
14
  require 'aws_support/matchers/update_an_aws_object'
15
15
  require 'aws_support/matchers/destroy_an_aws_object'
16
+ require 'aws_support/matchers/have_aws_object_tags'
16
17
  require 'aws_support/delayed_stream'
17
18
  require 'chef/provisioning/aws_driver/resources'
18
19
  require 'aws_support/aws_resource_run_wrapper'
@@ -21,7 +22,7 @@ module AWSSupport
21
22
  require 'aws'
22
23
  require 'aws_support/deep_matcher/matchable_object'
23
24
  require 'aws_support/deep_matcher/matchable_array'
24
- DeepMatcher::MatchableObject.matchable_classes << proc { |o| o.class.name =~ /^AWS::EC2($|::)/ }
25
+ DeepMatcher::MatchableObject.matchable_classes << proc { |o| o.class.name =~ /^AWS::(EC2|ELB)($|::)/ }
25
26
  DeepMatcher::MatchableArray.matchable_classes << AWS::Core::Data::List
26
27
 
27
28
  def purge_all
@@ -189,6 +190,9 @@ module AWSSupport
189
190
  define_method("create_an_#{resource_name}") do |name, expected_values={}|
190
191
  AWSSupport::Matchers::CreateAnAWSObject.new(self, resource_class, name, expected_values)
191
192
  end
193
+ define_method("have_#{resource_name}_tags") do |name, expected_tags={}|
194
+ AWSSupport::Matchers::HaveAWSObjectTags.new(self, resource_class, name, expected_tags)
195
+ end
192
196
  define_method("destroy_an_#{resource_name}") do |name, expected_values={}|
193
197
  AWSSupport::Matchers::DestroyAnAWSObject.new(self, resource_class, name)
194
198
  end
@@ -0,0 +1,63 @@
1
+ require 'rspec/matchers'
2
+ require 'chef/provisioning'
3
+ require 'aws_support/deep_matcher'
4
+
5
+ module AWSSupport
6
+ module Matchers
7
+ class HaveAWSObjectTags
8
+ include RSpec::Matchers::Composable
9
+ include AWSSupport::DeepMatcher
10
+
11
+ def initialize(example, resource_class, name, expected_tags)
12
+ @example = example
13
+ @resource_class = resource_class
14
+ @name = name
15
+ @expected_tags = expected_tags
16
+ end
17
+
18
+ attr_reader :example
19
+ attr_reader :resource_class
20
+ attr_reader :name
21
+ attr_reader :expected_tags
22
+ def resource_name
23
+ @resource_class.resource_name
24
+ end
25
+
26
+ def match_failure_messages(recipe)
27
+ differences = []
28
+
29
+ # Check for object existence and properties
30
+ resource = resource_class.new(name, nil)
31
+ resource.driver example.driver
32
+ resource.managed_entry_store Chef::Provisioning.chef_managed_entry_store
33
+ @aws_object = resource.aws_object
34
+
35
+ # Check existence
36
+ if @aws_object.nil?
37
+ differences << "#{resource_name}[#{name}] did not exist!"
38
+ else
39
+ differences += match_hashes_failure_messages(expected_tags, aws_object_tags, "#{resource_name}[#{name}]")
40
+ end
41
+
42
+ differences
43
+ end
44
+
45
+ private
46
+
47
+ def aws_object_tags
48
+ if @aws_object.is_a? AWS::ELB::LoadBalancer
49
+ resp = @example.driver.elb.client.describe_tags load_balancer_names: [@aws_object.name]
50
+ tags = {}
51
+ resp.data[:tag_descriptions] && resp.data[:tag_descriptions].each do |td|
52
+ td[:tags].each do |t|
53
+ tags[t[:key]] = t[:value]
54
+ end
55
+ end
56
+ tags
57
+ else
58
+ @aws_object.tags.to_h
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -8,10 +8,7 @@ describe Chef::Resource::AwsEbsVolume do
8
8
 
9
9
  it "aws_ebs_volume 'test_volume' creates an ebs volume" do
10
10
  expect_recipe {
11
- aws_ebs_volume "test_volume" do
12
- availability_zone 'a'
13
- size 8
14
- end
11
+ aws_ebs_volume "test_volume"
15
12
  }.to create_an_aws_ebs_volume('test_volume',
16
13
  :size => 8
17
14
  ).and be_idempotent
@@ -25,12 +22,16 @@ describe Chef::Resource::AwsEbsVolume do
25
22
  end
26
23
  }
27
24
  it "deletes the ebs volume" do
28
- expect_recipe {
25
+ # TODO all the `with_*` and `expect_*` methods from Cheffish
26
+ # automatically converge the block - we don't want to do that,
27
+ # we want to let the `destroy_an*` matcher do that
28
+ r = recipe {
29
29
  aws_ebs_volume "test_volume" do
30
30
  action :destroy
31
31
  end
32
- }.to destroy_an_aws_ebs_volume('test_volume')
33
- .and be_idempotent
32
+ }
33
+ expect(r).to destroy_an_aws_ebs_volume('test_volume'
34
+ ).and be_idempotent
34
35
  end
35
36
  end
36
37
 
@@ -38,7 +39,6 @@ describe Chef::Resource::AwsEbsVolume do
38
39
  expect_recipe {
39
40
  aws_ebs_volume "test_volume_az" do
40
41
  availability_zone "#{driver.aws_config.region}a"
41
- size 8
42
42
  end
43
43
  }.to create_an_aws_ebs_volume('test_volume_az')
44
44
  .and be_idempotent
@@ -35,6 +35,39 @@ describe Chef::Resource::AwsRouteTable do
35
35
  ]
36
36
  ).and be_idempotent
37
37
  end
38
+
39
+ it "ignores routes whose target matches ignore_route_targets" do
40
+ eni = nil
41
+ expect_recipe {
42
+ aws_subnet 'test_subnet' do
43
+ vpc 'test_vpc'
44
+ end
45
+
46
+ eni = aws_network_interface 'test_network_interface' do
47
+ subnet 'test_subnet'
48
+ end
49
+
50
+ aws_route_table 'test_route_table' do
51
+ vpc 'test_vpc'
52
+ routes(
53
+ '0.0.0.0/0' => :internet_gateway,
54
+ '172.31.0.0/16' => eni
55
+ )
56
+ end
57
+
58
+ aws_route_table 'test_route_table' do
59
+ vpc 'test_vpc'
60
+ routes '0.0.0.0/0' => :internet_gateway
61
+ ignore_route_targets ['^eni-']
62
+ end
63
+ }.to create_an_aws_route_table('test_route_table',
64
+ routes: [
65
+ { destination_cidr_block: '10.0.0.0/24', 'target.id' => 'local', state: :active },
66
+ { destination_cidr_block: '172.31.0.0/16', 'target.id' => eni.aws_object.id, state: :blackhole },
67
+ { destination_cidr_block: '0.0.0.0/0', 'target.id' => test_vpc.aws_object.internet_gateway.id, state: :active },
68
+ ]
69
+ ).and be_idempotent
70
+ end
38
71
  end
39
72
  end
40
73
  end
@@ -1,4 +1,6 @@
1
1
  require 'spec_helper'
2
+ require 'chef/resource/aws_security_group'
3
+ require 'chef/provisioning/aws_driver/exceptions'
2
4
 
3
5
  describe Chef::Resource::AwsSecurityGroup do
4
6
  extend AWSSupport
@@ -18,6 +20,42 @@ describe Chef::Resource::AwsSecurityGroup do
18
20
  ).and be_idempotent
19
21
  end
20
22
 
23
+ it "can reference a security group by name or id" do
24
+ expect_recipe {
25
+ sg = aws_security_group 'test_sg'
26
+ sg.run_action(:create)
27
+ id = sg.aws_object.id
28
+ aws_security_group id do
29
+ inbound_rules '0.0.0.0/0' => 22
30
+ end
31
+ aws_security_group 'test_sg' do
32
+ security_group_id id
33
+ outbound_rules 22 => '0.0.0.0/0'
34
+ end
35
+ }.to create_an_aws_security_group('test_sg',
36
+ description: 'test_sg',
37
+ vpc_id: default_vpc.id,
38
+ ip_permissions_list: [
39
+ { groups: [], ip_ranges: [{cidr_ip: "0.0.0.0/0"}], ip_protocol: "tcp", from_port: 22, to_port: 22},
40
+ ],
41
+ ip_permissions_list_egress: [
42
+ {groups: [], ip_ranges: [{cidr_ip: "0.0.0.0/0"}], ip_protocol: "tcp", from_port: 22, to_port: 22 }
43
+ ]
44
+
45
+ ).and be_idempotent
46
+ end
47
+
48
+ it "raises an error trying to reference a security group by an unknown id" do
49
+ expect_converge {
50
+ aws_security_group 'sg-12345678'
51
+ }.to raise_error(RuntimeError, /Chef::Resource::AwsSecurityGroup\[sg-12345678\] does not exist!/)
52
+ expect_converge {
53
+ aws_security_group 'test_sg' do
54
+ security_group_id 'sg-12345678'
55
+ end
56
+ }.to raise_error(RuntimeError, /Chef::Resource::AwsSecurityGroup\[sg-12345678\] does not exist!/)
57
+ end
58
+
21
59
  end
22
60
 
23
61
  with_aws "in a VPC" do
@@ -53,5 +91,53 @@ describe Chef::Resource::AwsSecurityGroup do
53
91
  ).and be_idempotent
54
92
  end
55
93
  end
94
+
95
+ with_aws "when narrowing from multiple VPCs" do
96
+ aws_vpc 'test_vpc1' do
97
+ cidr_block '10.0.0.0/24'
98
+ end
99
+ aws_vpc 'test_vpc2' do
100
+ cidr_block '10.0.0.0/24'
101
+ end
102
+ aws_security_group 'test_sg' do
103
+ vpc 'test_vpc1'
104
+ end
105
+ aws_security_group 'test_sg' do
106
+ vpc 'test_vpc2'
107
+ end
108
+
109
+ # We need to manually delete these because the auto-delete
110
+ # won't specify VPC
111
+ after(:context) do
112
+ converge {
113
+ aws_security_group 'test_sg' do
114
+ vpc 'test_vpc1'
115
+ action :destroy
116
+ end
117
+ aws_security_group 'test_sg' do
118
+ vpc 'test_vpc2'
119
+ action :destroy
120
+ end
121
+ }
122
+ end
123
+
124
+ it "raises an error if it finds multiple security groups" do
125
+ expect_converge {
126
+ r = aws_security_group 'test_sg'
127
+ r.aws_object
128
+ }.to raise_error(::Chef::Provisioning::AWSDriver::Exceptions::MultipleSecurityGroupError)
129
+ end
130
+
131
+ it "correctly returns the security group when vpc is specified" do
132
+ aws_obj = nil
133
+ expect_converge {
134
+ r = aws_security_group 'test_sg' do
135
+ vpc 'test_vpc1'
136
+ end
137
+ aws_obj = r.aws_object
138
+ }.to_not raise_error
139
+ expect(aws_obj.vpc.tags['Name']).to eq('test_vpc1')
140
+ end
141
+ end
56
142
  end
57
143
  end
@@ -0,0 +1,160 @@
1
+ require 'spec_helper'
2
+
3
+ describe "AWS Tagged Items" 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
+ it "aws_ebs_volume 'test_volume' created with default Name tag" do
9
+ expect_recipe {
10
+ aws_ebs_volume "test_volume"
11
+ }.to create_an_aws_ebs_volume('test_volume'
12
+ ).and have_aws_ebs_volume_tags('test_volume',
13
+ { 'Name' => 'test_volume' }
14
+ ).and be_idempotent
15
+ end
16
+
17
+ it "aws_ebs_volume 'test_volume' tags are updated" do
18
+ expect_recipe {
19
+ aws_ebs_volume "test_volume_a" do
20
+ aws_tags :byebye => 'true'
21
+ end
22
+ }.to create_an_aws_ebs_volume('test_volume_a'
23
+ ).and have_aws_ebs_volume_tags('test_volume_a',
24
+ { 'Name' => 'test_volume_a',
25
+ 'byebye' => 'true'
26
+ }
27
+ ).and be_idempotent
28
+
29
+ expect_recipe {
30
+ aws_ebs_volume "test_volume_a" do
31
+ aws_tags 'Name' => 'test_volume_b', :project => 'X'
32
+ end
33
+ }.to update_an_aws_ebs_volume('test_volume_a'
34
+ ).and have_aws_ebs_volume_tags('test_volume_a',
35
+ { 'Name' => 'test_volume_b',
36
+ 'project' => 'X'
37
+ }
38
+ ).and be_idempotent
39
+ end
40
+
41
+ it "aws_ebs_volume 'test_volume' tags are not changed when not updated" do
42
+ expect_recipe {
43
+ aws_ebs_volume "test_volume_c" do
44
+ aws_tags :byebye => 'true'
45
+ end
46
+ }.to create_an_aws_ebs_volume('test_volume_c'
47
+ ).and have_aws_ebs_volume_tags('test_volume_c',
48
+ { 'Name' => 'test_volume_c',
49
+ 'byebye' => 'true'
50
+ }
51
+ ).and be_idempotent
52
+
53
+ expect_recipe {
54
+ aws_ebs_volume "test_volume_c"
55
+ }.to have_aws_ebs_volume_tags('test_volume_c',
56
+ { 'Name' => 'test_volume_c',
57
+ 'byebye' => 'true'
58
+ }
59
+ ).and be_idempotent
60
+ end
61
+
62
+
63
+ it "aws_ebs_volume 'test_volume' created with new Name tag" do
64
+ expect_recipe {
65
+ aws_ebs_volume "test_volume_2" do
66
+ aws_tags :Name => 'test_volume_new'
67
+ end
68
+ }.to create_an_aws_ebs_volume('test_volume_2'
69
+ ).and have_aws_ebs_volume_tags('test_volume_2',
70
+ { 'Name' => 'test_volume_new' }
71
+ ).and be_idempotent
72
+ end
73
+
74
+ it "aws_ebs_volume 'test_volume' created with custom tag" do
75
+ expect_recipe {
76
+ aws_ebs_volume "test_volume_3" do
77
+ aws_tags :project => 'aws-provisioning'
78
+ end
79
+ }.to create_an_aws_ebs_volume('test_volume_3'
80
+ ).and have_aws_ebs_volume_tags('test_volume_3',
81
+ { 'Name' => 'test_volume_3',
82
+ 'project' => 'aws-provisioning'
83
+ }
84
+ ).and be_idempotent
85
+ end
86
+
87
+ it "aws_instance 'test_instance' created with custom tag", :super_slow do
88
+ expect_recipe {
89
+ machine 'test_instance' do
90
+ action :allocate
91
+ end
92
+ }.to create_an_aws_instance('test_instance')
93
+
94
+ expect_recipe {
95
+ aws_instance "test_instance" do
96
+ aws_tags :project => 'FUN'
97
+ end
98
+ }.to update_an_aws_instance('test_instance'
99
+ ).and have_aws_instance_tags('test_instance',
100
+ { 'Name' => 'test_instance',
101
+ 'project' => 'FUN'
102
+ }
103
+ ).and be_idempotent
104
+ end
105
+
106
+ it "machine 'test_machine' created using machine_options aws_tag", :super_slow do
107
+ expect_recipe {
108
+ machine 'test_machine' do
109
+ machine_options :aws_tags => { :mach_opt_sym => 'value', 'mach_opt_str' => 'value' }
110
+ action :allocate
111
+ end
112
+ }.to create_an_aws_instance('test_machine'
113
+ ).and have_aws_instance_tags('test_machine',
114
+ { 'Name' => 'test_machine',
115
+ 'mach_opt_sym' => 'value',
116
+ 'mach_opt_str' => 'value'
117
+ }
118
+ ).and be_idempotent
119
+ end
120
+
121
+ it "machine 'test_machine_2' created using default with_machine_options aws_tag", :super_slow do
122
+ expect_recipe {
123
+ with_machine_options :aws_tags => { :default => 'value1' }
124
+
125
+ machine 'test_machine_2' do
126
+ action :allocate
127
+ end
128
+ }.to create_an_aws_instance('test_machine_2'
129
+ ).and have_aws_instance_tags('test_machine_2',
130
+ { 'Name' => 'test_machine_2',
131
+ 'default' => 'value1'
132
+ }
133
+ ).and be_idempotent
134
+ end
135
+
136
+ it "load balancer 'lbtest' tagged with load_balancer_options" do
137
+ expect_recipe {
138
+ load_balancer 'lbtest' do
139
+ load_balancer_options :aws_tags => { :marco => 'polo', 'happyhappy' => 'joyjoy' },
140
+ :availability_zones => ["#{driver.aws_config.region}a", "#{driver.aws_config.region}b"] # TODO should enchance to accept letter AZs
141
+ end
142
+ }.to create_an_aws_load_balancer('lbtest'
143
+ ).and have_aws_load_balancer_tags('lbtest',
144
+ { 'marco' => 'polo',
145
+ 'happyhappy' => 'joyjoy'
146
+ }
147
+ ).and be_idempotent
148
+ expect_recipe {
149
+ load_balancer 'lbtest' do
150
+ load_balancer_options :aws_tags => { :default => 'value1' }
151
+ end
152
+ }.to update_an_aws_load_balancer('lbtest'
153
+ ).and have_aws_load_balancer_tags('lbtest',
154
+ { 'default' => 'value1' }
155
+ ).and be_idempotent
156
+ end
157
+
158
+ end
159
+ end
160
+ end