kitchen-ec2 0.8.0 → 0.9.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.
@@ -21,6 +21,6 @@ module Kitchen
21
21
  module Driver
22
22
 
23
23
  # Version string for EC2 Test Kitchen driver
24
- EC2_VERSION = '0.8.0'
24
+ EC2_VERSION = "0.9.0"
25
25
  end
26
26
  end
@@ -0,0 +1,119 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Author:: Tyler Ball (<tball@chef.io>)
4
+ #
5
+ # Copyright (C) 2015, Fletcher Nichol
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ require "kitchen/driver/aws/client"
20
+ require "climate_control"
21
+
22
+ describe Kitchen::Driver::Aws::Client do
23
+ describe "::get_credentials" do
24
+ let(:shared) { instance_double(Aws::SharedCredentials) }
25
+ let(:iam) { instance_double(Aws::InstanceProfileCredentials) }
26
+
27
+ before do
28
+ expect(Aws::SharedCredentials).to \
29
+ receive(:new).with(:profile_name => "profile").and_return(shared)
30
+ end
31
+
32
+ # nothing else is set, so we default to this
33
+ it "loads IAM credentials last" do
34
+ expect(shared).to receive(:loadable?).and_return(false)
35
+ expect(Aws::InstanceProfileCredentials).to receive(:new).and_return(iam)
36
+ expect(Kitchen::Driver::Aws::Client.get_credentials("profile", nil, nil, nil)).to eq(iam)
37
+ end
38
+
39
+ it "loads shared credentials second to last" do
40
+ expect(shared).to receive(:loadable?).and_return(true)
41
+ expect(Kitchen::Driver::Aws::Client.get_credentials("profile", nil, nil, nil)).to eq(shared)
42
+ end
43
+
44
+ it "loads shared credentials third to last" do
45
+ expect(shared).to_not receive(:loadable?)
46
+ ClimateControl.modify(
47
+ "AWS_ACCESS_KEY_ID" => "key1",
48
+ "AWS_SECRET_ACCESS_KEY" => "value1",
49
+ "AWS_SESSION_TOKEN" => "token1"
50
+ ) do
51
+ expect(Kitchen::Driver::Aws::Client.get_credentials("profile", nil, nil, nil)).to \
52
+ be_a(Aws::Credentials).and have_attributes(
53
+ :access_key_id => "key1",
54
+ :secret_access_key => "value1",
55
+ :session_token => "token1"
56
+ )
57
+ end
58
+ end
59
+
60
+ it "loads deprecated credentials fourth to last" do
61
+ expect(shared).to_not receive(:loadable?)
62
+ ClimateControl.modify(
63
+ "AWS_ACCESS_KEY" => "key2",
64
+ "AWS_SECRET_KEY" => "value2",
65
+ "AWS_TOKEN" => "token2"
66
+ ) do
67
+ expect(Kitchen::Driver::Aws::Client.get_credentials("profile", nil, nil, nil)).to \
68
+ be_a(Aws::Credentials).and have_attributes(
69
+ :access_key_id => "key2",
70
+ :secret_access_key => "value2",
71
+ :session_token => "token2"
72
+ )
73
+ end
74
+ end
75
+
76
+ it "loads provided credentials first" do
77
+ expect(shared).to_not receive(:loadable?)
78
+ expect(Kitchen::Driver::Aws::Client.get_credentials("profile", "key3", "value3", nil)).to \
79
+ be_a(Aws::Credentials).and have_attributes(
80
+ :access_key_id => "key3",
81
+ :secret_access_key => "value3",
82
+ :session_token => nil
83
+ )
84
+ end
85
+
86
+ it "uses a session token if provided" do
87
+ expect(shared).to_not receive(:loadable?)
88
+ expect(Kitchen::Driver::Aws::Client.get_credentials("profile", "key3", "value3", "t")).to \
89
+ be_a(Aws::Credentials).and have_attributes(
90
+ :access_key_id => "key3",
91
+ :secret_access_key => "value3",
92
+ :session_token => "t"
93
+ )
94
+ end
95
+ end
96
+
97
+ let(:client) { Kitchen::Driver::Aws::Client.new("us-west-1") }
98
+
99
+ describe "#initialize" do
100
+
101
+ it "successfully creates a client" do
102
+ expect(client).to be_a(Kitchen::Driver::Aws::Client)
103
+ end
104
+
105
+ it "Sets the AWS config" do
106
+ client
107
+ expect(Aws.config[:region]).to eq("us-west-1")
108
+ end
109
+ end
110
+
111
+ it "returns a client" do
112
+ expect(client.client).to be_a(Aws::EC2::Client)
113
+ end
114
+
115
+ it "returns a resource" do
116
+ expect(client.resource).to be_a(Aws::EC2::Resource)
117
+ end
118
+
119
+ end
@@ -0,0 +1,303 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Author:: Tyler Ball (<tball@chef.io>)
4
+ #
5
+ # Copyright (C) 2015, Fletcher Nichol
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ require "kitchen/driver/aws/instance_generator"
20
+ require "kitchen/driver/aws/client"
21
+ require "tempfile"
22
+
23
+ describe Kitchen::Driver::Aws::InstanceGenerator do
24
+
25
+ let(:config) { Hash.new }
26
+
27
+ let(:resource) { instance_double(Aws::EC2::Resource) }
28
+
29
+ let(:ec2) { instance_double(Kitchen::Driver::Aws::Client, :resource => resource) }
30
+
31
+ let(:generator) { Kitchen::Driver::Aws::InstanceGenerator.new(config, ec2) }
32
+
33
+ describe "#debug_if_root_device" do
34
+ let(:image_id) { "ami-123456" }
35
+ let(:config) { { :image_id => image_id } }
36
+ let(:image) { double("image") }
37
+
38
+ before do
39
+ expect(resource).to receive(:image).with(image_id).and_return(image)
40
+ end
41
+
42
+ it "returns nil when the image cannot be found" do
43
+ expect(image).to receive(:root_device_name).and_raise(
44
+ ::Aws::EC2::Errors::InvalidAMIIDNotFound.new({}, "")
45
+ )
46
+ expect(generator).to_not receive(:info)
47
+ expect(generator.debug_if_root_device({})).to eq(nil)
48
+ end
49
+
50
+ it "logs an info message when the device mappings are overriding the root device" do
51
+ expect(image).to receive(:root_device_name).and_return("name")
52
+ expect(generator).to receive(:info)
53
+ expect(generator.debug_if_root_device([{ :device_name => "name" }])).to eq(nil)
54
+ end
55
+ end
56
+
57
+ describe "#prepared_user_data" do
58
+ context "when config[:user_data] is a file" do
59
+ let(:tmp_file) { Tempfile.new("prepared_user_data_test") }
60
+ let(:config) { { :user_data => tmp_file.path } }
61
+
62
+ before do
63
+ tmp_file.write("foo\nbar")
64
+ tmp_file.rewind
65
+ end
66
+
67
+ after do
68
+ tmp_file.close
69
+ tmp_file.unlink
70
+ end
71
+
72
+ it "reads the file contents" do
73
+ expect(generator.prepared_user_data).to eq("foo\nbar")
74
+ end
75
+
76
+ it "memoizes the file contents" do
77
+ expect(generator.prepared_user_data).to eq("foo\nbar")
78
+ tmp_file.write("other\nvalue")
79
+ tmp_file.rewind
80
+ expect(generator.prepared_user_data).to eq("foo\nbar")
81
+ end
82
+ end
83
+ end
84
+
85
+ describe "block_device_mappings" do
86
+ before do
87
+ expect(generator).to receive(:debug_if_root_device)
88
+ end
89
+
90
+ it "returns empty if nothing is provided" do
91
+ expect(generator.block_device_mappings).to eq([])
92
+ end
93
+
94
+ context "when populated with multiple mappings" do
95
+ let(:config) do
96
+ { :block_device_mappings => [
97
+ {
98
+ :ebs_volume_size => 13,
99
+ :ebs_delete_on_termination => true,
100
+ :ebs_device_name => "/dev/sda1"
101
+ },
102
+ {
103
+ :ebs_volume_size => 15,
104
+ :ebs_delete_on_termination => false,
105
+ :ebs_device_name => "/dev/sda2",
106
+ :ebs_volume_type => "gp2",
107
+ :ebs_snapshot_id => "id",
108
+ :ebs_virtual_name => "test"
109
+ }
110
+ ] }
111
+ end
112
+
113
+ it "returns the transformed mappings" do
114
+ expect(generator.block_device_mappings).to match(
115
+ [
116
+ {
117
+ :ebs => {
118
+ :volume_size => 13,
119
+ :delete_on_termination => true
120
+ },
121
+ :device_name => "/dev/sda1"
122
+ },
123
+ {
124
+ :ebs => {
125
+ :volume_size => 15,
126
+ :volume_type => "gp2",
127
+ :snapshot_id => "id",
128
+ :delete_on_termination => false
129
+ },
130
+ :device_name => "/dev/sda2",
131
+ :virtual_name => "test"
132
+ }
133
+ ]
134
+ )
135
+ end
136
+
137
+ end
138
+
139
+ context "when populatd with deprecated configs" do
140
+ let(:config) do
141
+ {
142
+ :ebs_volume_size => 13,
143
+ :ebs_delete_on_termination => true,
144
+ :ebs_device_name => "/dev/sda1",
145
+ :ebs_volume_type => "gp2"
146
+ }
147
+ end
148
+
149
+ it "returns the transformed mappings" do
150
+ expect(generator.block_device_mappings).to match(
151
+ [
152
+ {
153
+ :ebs => {
154
+ :volume_size => 13,
155
+ :delete_on_termination => true,
156
+ :volume_type => "gp2"
157
+ },
158
+ :device_name => "/dev/sda1"
159
+ }
160
+ ]
161
+ )
162
+ end
163
+ end
164
+
165
+ context "when populated with deprecated configs and new configs" do
166
+ let(:config) do
167
+ {
168
+ :ebs_volume_size => 13,
169
+ :ebs_delete_on_termination => true,
170
+ :ebs_device_name => "/dev/sda1",
171
+ :ebs_volume_type => "gp2",
172
+ :block_device_mappings => [
173
+ {
174
+ :ebs_volume_size => 15,
175
+ :ebs_delete_on_termination => false,
176
+ :ebs_device_name => "/dev/sda2",
177
+ :ebs_volume_type => "gp2",
178
+ :ebs_snapshot_id => "id",
179
+ :ebs_virtual_name => "test"
180
+ }
181
+ ]
182
+ }
183
+ end
184
+
185
+ it "ignores the old configs" do
186
+ expect(generator.block_device_mappings).to match(
187
+ [
188
+ {
189
+ :ebs => {
190
+ :volume_size => 15,
191
+ :volume_type => "gp2",
192
+ :snapshot_id => "id",
193
+ :delete_on_termination => false
194
+ },
195
+ :device_name => "/dev/sda2",
196
+ :virtual_name => "test"
197
+ }
198
+ ]
199
+ )
200
+ end
201
+ end
202
+ end
203
+
204
+ describe "#ec2_instance_data" do
205
+ before do
206
+ expect(generator).to receive(:debug_if_root_device)
207
+ end
208
+
209
+ it "returns empty on nil" do
210
+ expect(generator.ec2_instance_data).to eq(
211
+ :placement => { :availability_zone => nil },
212
+ :instance_type => nil,
213
+ :ebs_optimized => nil,
214
+ :image_id => nil,
215
+ :key_name => nil,
216
+ :subnet_id => nil,
217
+ :private_ip_address => nil
218
+ )
219
+ end
220
+
221
+ context "when populated with minimum requirements" do
222
+ let(:config) do
223
+ {
224
+ :availability_zone => "eu-west-1a",
225
+ :instance_type => "micro",
226
+ :ebs_optimized => true,
227
+ :image_id => "ami-123",
228
+ :aws_ssh_key_id => "key",
229
+ :subnet_id => "s-456",
230
+ :private_ip_address => "0.0.0.0"
231
+ }
232
+ end
233
+
234
+ it "returns the minimum data" do
235
+ expect(generator.ec2_instance_data).to eq(
236
+ :placement => { :availability_zone => "eu-west-1a" },
237
+ :instance_type => "micro",
238
+ :ebs_optimized => true,
239
+ :image_id => "ami-123",
240
+ :key_name => "key",
241
+ :subnet_id => "s-456",
242
+ :private_ip_address => "0.0.0.0"
243
+ )
244
+ end
245
+ end
246
+
247
+ context "when provided the maximum config" do
248
+ let(:config) do
249
+ {
250
+ :availability_zone => "eu-west-1a",
251
+ :instance_type => "micro",
252
+ :ebs_optimized => true,
253
+ :image_id => "ami-123",
254
+ :aws_ssh_key_id => "key",
255
+ :subnet_id => "s-456",
256
+ :private_ip_address => "0.0.0.0",
257
+ :block_device_mappings => [
258
+ {
259
+ :ebs_volume_size => 15,
260
+ :ebs_delete_on_termination => false,
261
+ :ebs_device_name => "/dev/sda2",
262
+ :ebs_volume_type => "gp2",
263
+ :ebs_snapshot_id => "id",
264
+ :ebs_virtual_name => "test"
265
+ }
266
+ ],
267
+ :security_group_ids => ["sg-789"],
268
+ :user_data => "foo",
269
+ :iam_instance_profile => "iam-123",
270
+ :associate_public_ip_address => true
271
+ }
272
+ end
273
+
274
+ it "returns the maximum data" do
275
+ expect(generator.ec2_instance_data).to eq(
276
+ :placement => { :availability_zone => "eu-west-1a" },
277
+ :instance_type => "micro",
278
+ :ebs_optimized => true,
279
+ :image_id => "ami-123",
280
+ :key_name => "key",
281
+ :subnet_id => "s-456",
282
+ :private_ip_address => "0.0.0.0",
283
+ :block_device_mappings => [
284
+ {
285
+ :ebs => {
286
+ :volume_size => 15,
287
+ :delete_on_termination => false,
288
+ :volume_type => "gp2",
289
+ :snapshot_id => "id"
290
+ },
291
+ :device_name => "/dev/sda2",
292
+ :virtual_name => "test"
293
+ }
294
+ ],
295
+ :iam_instance_profile => { :name => nil },
296
+ :network_interfaces => [{ :device_index => 0, :associate_public_ip_address => true }],
297
+ :security_group_ids => ["sg-789"],
298
+ :user_data => "foo"
299
+ )
300
+ end
301
+ end
302
+ end
303
+ end
@@ -0,0 +1,82 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Author:: Tyler Ball (<tball@chef.io>)
4
+ #
5
+ # Copyright (C) 2015, Fletcher Nichol
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ require "kitchen/driver/ec2"
20
+ require "kitchen/provisioner/dummy"
21
+ require "kitchen/transport/dummy"
22
+ require "kitchen/verifier/dummy"
23
+
24
+ describe Kitchen::Driver::Ec2 do
25
+
26
+ let(:logged_output) { StringIO.new }
27
+ let(:logger) { Logger.new(logged_output) }
28
+ let(:config) { { :aws_ssh_key_id => "key", :image_id => "ami-1234567" } }
29
+ let(:platform) { Kitchen::Platform.new(:name => "fooos-99") }
30
+ let(:suite) { Kitchen::Suite.new(:name => "suitey") }
31
+ let(:verifier) { Kitchen::Verifier::Dummy.new }
32
+ let(:provisioner) { Kitchen::Provisioner::Dummy.new }
33
+ let(:transport) { Kitchen::Transport::Dummy.new }
34
+ let(:state_file) { double("state_file") }
35
+
36
+ let(:driver) { Kitchen::Driver::Ec2.new(config) }
37
+
38
+ let(:instance) do
39
+ Kitchen::Instance.new(
40
+ :verifier => verifier,
41
+ :driver => driver,
42
+ :logger => logger,
43
+ :suite => suite,
44
+ :platform => platform,
45
+ :provisioner => provisioner,
46
+ :transport => transport,
47
+ :state_file => state_file
48
+ )
49
+ end
50
+
51
+ it "driver api_version is 2" do
52
+ expect(driver.diagnose_plugin[:api_version]).to eq(2)
53
+ end
54
+
55
+ it "plugin_version is set to Kitchen::Vagrant::VERSION" do
56
+ expect(driver.diagnose_plugin[:version]).to eq(
57
+ Kitchen::Driver::EC2_VERSION)
58
+ end
59
+
60
+ describe "configuration" do
61
+ let(:config) { {} }
62
+ it "requires :aws_ssh_key_id to be provided" do
63
+ expect { driver.finalize_config!(instance) }.to \
64
+ raise_error(Kitchen::UserError, /:aws_ssh_key_id/)
65
+ end
66
+
67
+ it "requires :image_id to be provided" do
68
+ config[:aws_ssh_key_id] = "key"
69
+ expect { driver.finalize_config!(instance) }.to \
70
+ raise_error(Kitchen::UserError, /:image_id/)
71
+ end
72
+ end
73
+
74
+ describe "finalize_config!" do
75
+ it "defaults the availability zone if not provided" do
76
+ expect(config[:availability_zone]).to eq(nil)
77
+ driver.finalize_config!(instance)
78
+ expect(config[:availability_zone]).to eq("us-east-1b")
79
+ end
80
+ end
81
+
82
+ end