kitchen-ec2 1.2.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +3 -3
- data/CHANGELOG.md +17 -2
- data/Gemfile +2 -1
- data/README.md +45 -3
- data/lib/kitchen/driver/aws/client.rb +31 -7
- data/lib/kitchen/driver/aws/instance_generator.rb +60 -0
- data/lib/kitchen/driver/aws/standard_platform/windows.rb +29 -19
- data/lib/kitchen/driver/ec2.rb +53 -6
- data/lib/kitchen/driver/ec2_version.rb +1 -1
- data/spec/kitchen/driver/ec2/client_spec.rb +97 -30
- data/spec/kitchen/driver/ec2/image_selection_spec.rb +11 -3
- data/spec/kitchen/driver/ec2/instance_generator_spec.rb +208 -0
- data/spec/kitchen/driver/ec2_spec.rb +102 -1
- metadata +3 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b0d06c5717fea6db5e691129f2e7b2c29126ad83
|
4
|
+
data.tar.gz: 9fe52faf85c23d960c618c26fdd28145a2a1f761
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: acdc60d833d8c93b17eb73914e207101c8d5cd22945857155e70ad91f99586418f434929039c61fdba6252a73f60f179733b2a67d66f31eff934a9904b143e4f
|
7
|
+
data.tar.gz: be0377db0b71c4e76ebecde7310c572d4081530eee5256d4d6c0c17a8eab2e2d76413797a3f293b5580230c4779928f19f2c808bd15c3062390e7fbc53d56657
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,7 +1,22 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
-
## [
|
4
|
-
[Full Changelog](https://github.com/test-kitchen/kitchen-ec2/compare/v1.
|
3
|
+
## [v1.3.0](https://github.com/test-kitchen/kitchen-ec2/tree/v1.3.0) (2017-02-10)
|
4
|
+
[Full Changelog](https://github.com/test-kitchen/kitchen-ec2/compare/v1.2.0...v1.3.0)
|
5
|
+
|
6
|
+
**Implemented Enhancements:**
|
7
|
+
|
8
|
+
- Support Windows 2016 [\#291](https://github.com/test-kitchen/kitchen-ec2/pull/291) ([gdavison](https://github.com/gdavison))
|
9
|
+
- Add expiration to spot requests [\#285](https://github.com/test-kitchen/kitchen-ec2/pull/285) ([alanbrent](https://github.com/alanbrent))
|
10
|
+
- Don't break if we're using a custom "platform" AMI [\#273](https://github.com/test-kitchen/kitchen-ec2/pull/273) ([hynd](https://github.com/hynd))
|
11
|
+
- Propagate tags to volumes [\#260](https://github.com/test-kitchen/kitchen-ec2/pull/260) ([mrbobbytables](https://github.com/mrbobbytables))
|
12
|
+
- In the client, only source creds from the shared file when necessary [\#259](https://github.com/test-kitchen/kitchen-ec2/pull/259) ([davidcpell](https://github.com/davidcpell))
|
13
|
+
- Add notes for AMI image name requirements [\#252](https://github.com/test-kitchen/kitchen-ec2/pull/252) ([freimer](https://github.com/freimer))
|
14
|
+
- Provide the option to set ssl\_peer\_verify to false [\#251](https://github.com/test-kitchen/kitchen-ec2/pull/251) ([mwrock](https://github.com/mwrock))
|
15
|
+
- Adding support for tenancy parameter in placement config. [\#235](https://github.com/test-kitchen/kitchen-ec2/pull/235) ([jcastillocano](https://github.com/jcastillocano))
|
16
|
+
- Lookup ID from tag [\#232](https://github.com/test-kitchen/kitchen-ec2/pull/232) ([dlukman](https://github.com/dlukman))
|
17
|
+
|
18
|
+
## [v1.2.0](https://github.com/test-kitchen/kitchen-ec2/tree/v1.2.0) (2016-09-12)
|
19
|
+
[Full Changelog](https://github.com/test-kitchen/kitchen-ec2/compare/v1.1.0...v1.2.0)
|
5
20
|
|
6
21
|
**Fixed bugs:**
|
7
22
|
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -79,9 +79,10 @@ You can learn more about the available filters in the AWS CLI doc under `--filte
|
|
79
79
|
```yaml
|
80
80
|
platforms:
|
81
81
|
- name: ubuntu-14.04
|
82
|
-
|
83
|
-
|
84
|
-
|
82
|
+
driver:
|
83
|
+
image_search:
|
84
|
+
owner-id: "099720109477"
|
85
|
+
name: ubuntu/images/*/ubuntu-*-14.04*
|
85
86
|
```
|
86
87
|
|
87
88
|
In the event that there are multiple matches (as sometimes happens), we sort to
|
@@ -92,6 +93,16 @@ get the best results. In order of priority from greatest to least, we prefer:
|
|
92
93
|
- 64-bit over 32-bit
|
93
94
|
- The most recently created image (to pick up patch releases)
|
94
95
|
|
96
|
+
Note that the image_search method *requires* that the AMI image names be in a specific format.
|
97
|
+
Some examples are:
|
98
|
+
|
99
|
+
- Windows-2012
|
100
|
+
- Windows-2012r2
|
101
|
+
- Windows-2012r2sp1
|
102
|
+
- RHEL-7.2
|
103
|
+
|
104
|
+
It is safest to use the same naming convention as used by the public images published by the OS vendors on the AWS marketplace.
|
105
|
+
|
95
106
|
#### `platform.name`
|
96
107
|
|
97
108
|
The third way to specify the image is by leaving `image_id` and `image_search`
|
@@ -247,6 +258,20 @@ instance.
|
|
247
258
|
|
248
259
|
The default is `["default"]`.
|
249
260
|
|
261
|
+
### `security_group_filter`
|
262
|
+
|
263
|
+
The EC2 [security group][group_docs] which will be applied to the instance,
|
264
|
+
specified by tag. Only one group can be specified this way.
|
265
|
+
|
266
|
+
The default is unset, or `nil`.
|
267
|
+
|
268
|
+
An example of usage:
|
269
|
+
```yaml
|
270
|
+
security_group_filter:
|
271
|
+
tag: 'Name'
|
272
|
+
value: 'example-group-name'
|
273
|
+
```
|
274
|
+
|
250
275
|
### `region`
|
251
276
|
|
252
277
|
**Required** The AWS [region][region_docs] to use.
|
@@ -260,6 +285,19 @@ The EC2 [subnet][subnet_docs] to use.
|
|
260
285
|
|
261
286
|
The default is unset, or `nil`.
|
262
287
|
|
288
|
+
### `subnet_filter`
|
289
|
+
|
290
|
+
The EC2 [subnet][subnet_docs] to use, specified by tag.
|
291
|
+
|
292
|
+
The default is unset, or `nil`.
|
293
|
+
|
294
|
+
An example of usage:
|
295
|
+
```yaml
|
296
|
+
subnet_filter:
|
297
|
+
tag: 'Name'
|
298
|
+
value: 'example-subnet-name'
|
299
|
+
```
|
300
|
+
|
263
301
|
### `tags`
|
264
302
|
|
265
303
|
The Hash of EC tag name/value pairs which will be applied to the instance.
|
@@ -312,6 +350,10 @@ The default is `ENV["HTTPS_PROXY"] || ENV["HTTP_PROXY"]`. If you have these env
|
|
312
350
|
|
313
351
|
**Note** - The AWS command line utility allow you to specify [two proxies](http://docs.aws.amazon.com/cli/latest/userguide/cli-http-proxy.html), one for HTTP and one for HTTPS. The AWS Ruby SDK only allows you to specify 1 proxy and because all requests are `https://` this proxy needs to support HTTPS.
|
314
352
|
|
353
|
+
### `ssl_verify_peer`
|
354
|
+
|
355
|
+
If you need to turn off ssl certificate verification for HTTP calls made to AWS, set `ssl_verify_peer: false`.
|
356
|
+
|
315
357
|
### Disk Configuration
|
316
358
|
|
317
359
|
#### <a name="config-block_device_mappings"></a> `block_device_mappings`
|
@@ -39,15 +39,17 @@ module Kitchen
|
|
39
39
|
secret_access_key = nil,
|
40
40
|
session_token = nil,
|
41
41
|
http_proxy = nil,
|
42
|
-
retry_limit = nil
|
42
|
+
retry_limit = nil,
|
43
|
+
ssl_verify_peer = true
|
43
44
|
)
|
44
45
|
creds = self.class.get_credentials(
|
45
|
-
profile_name, access_key_id, secret_access_key, session_token
|
46
|
+
profile_name, access_key_id, secret_access_key, session_token, region
|
46
47
|
)
|
47
48
|
::Aws.config.update(
|
48
49
|
:region => region,
|
49
50
|
:credentials => creds,
|
50
|
-
:http_proxy => http_proxy
|
51
|
+
:http_proxy => http_proxy,
|
52
|
+
:ssl_verify_peer => ssl_verify_peer
|
51
53
|
)
|
52
54
|
::Aws.config.update(:retry_limit => retry_limit) unless retry_limit.nil?
|
53
55
|
end
|
@@ -55,8 +57,10 @@ module Kitchen
|
|
55
57
|
# Try and get the credentials from an ordered list of locations
|
56
58
|
# http://docs.aws.amazon.com/sdkforruby/api/index.html#Configuration
|
57
59
|
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
58
|
-
|
59
|
-
|
60
|
+
# rubocop:disable Metrics/ParameterLists, Metrics/MethodLength
|
61
|
+
def self.get_credentials(profile_name, access_key_id, secret_access_key, session_token,
|
62
|
+
region, options = {})
|
63
|
+
source_creds =
|
60
64
|
if access_key_id && secret_access_key
|
61
65
|
::Aws::Credentials.new(access_key_id, secret_access_key, session_token)
|
62
66
|
elsif ENV["AWS_ACCESS_KEY_ID"] && ENV["AWS_SECRET_ACCESS_KEY"]
|
@@ -65,14 +69,34 @@ module Kitchen
|
|
65
69
|
ENV["AWS_SECRET_ACCESS_KEY"],
|
66
70
|
ENV["AWS_SESSION_TOKEN"]
|
67
71
|
)
|
68
|
-
elsif
|
69
|
-
|
72
|
+
elsif profile_name
|
73
|
+
::Aws::SharedCredentials.new(:profile_name => profile_name)
|
70
74
|
else
|
71
75
|
::Aws::InstanceProfileCredentials.new(:retries => 1)
|
72
76
|
end
|
77
|
+
|
78
|
+
if options[:assume_role_arn] && options[:assume_role_session_name]
|
79
|
+
sts = ::Aws::STS::Client.new(:credentials => source_creds, :region => region)
|
80
|
+
|
81
|
+
assume_role_options = (options[:assume_role_options] || {}).merge(
|
82
|
+
:client => sts,
|
83
|
+
:role_arn => options[:assume_role_arn],
|
84
|
+
:role_session_name => options[:assume_role_session_name]
|
85
|
+
)
|
86
|
+
|
87
|
+
::Aws::AssumeRoleCredentials.new(assume_role_options)
|
88
|
+
else
|
89
|
+
source_creds
|
90
|
+
end
|
73
91
|
end
|
74
92
|
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
75
93
|
|
94
|
+
def self.get_shared_creds(profile_name)
|
95
|
+
::Aws::SharedCredentials.new(:profile_name => profile_name)
|
96
|
+
rescue ::Aws::Errors::NoSuchProfileError
|
97
|
+
false
|
98
|
+
end
|
99
|
+
|
76
100
|
def create_instance(options)
|
77
101
|
resource.create_instances(options)[0]
|
78
102
|
end
|
@@ -17,6 +17,7 @@
|
|
17
17
|
# limitations under the License.
|
18
18
|
|
19
19
|
require "base64"
|
20
|
+
require "aws-sdk"
|
20
21
|
|
21
22
|
module Kitchen
|
22
23
|
|
@@ -40,6 +41,42 @@ module Kitchen
|
|
40
41
|
# Transform the provided config into the hash to send to AWS. Some fields
|
41
42
|
# can be passed in null, others need to be ommitted if they are null
|
42
43
|
def ec2_instance_data # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
44
|
+
# Support for looking up security group id and subnet id using tags.
|
45
|
+
|
46
|
+
if config[:subnet_id].nil? && config[:subnet_filter]
|
47
|
+
config[:subnet_id] = ::Aws::EC2::Client.
|
48
|
+
new(:region => config[:region]).describe_subnets(
|
49
|
+
:filters => [
|
50
|
+
{
|
51
|
+
:name => "tag:#{config[:subnet_filter][:tag]}",
|
52
|
+
:values => [config[:subnet_filter][:value]]
|
53
|
+
}
|
54
|
+
]
|
55
|
+
)[0][0].subnet_id
|
56
|
+
|
57
|
+
if config[:subnet_id].nil?
|
58
|
+
fail "The subnet tagged '#{config[:subnet_filter][:tag]}\
|
59
|
+
#{config[:subnet_filter][:value]}' does not exist!"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
if config[:security_group_ids].nil? && config[:security_group_filter]
|
64
|
+
config[:security_group_ids] = [::Aws::EC2::Client.
|
65
|
+
new(:region => config[:region]).describe_security_groups(
|
66
|
+
:filters => [
|
67
|
+
{
|
68
|
+
:name => "tag:#{config[:security_group_filter][:tag]}",
|
69
|
+
:values => [config[:security_group_filter][:value]]
|
70
|
+
}
|
71
|
+
]
|
72
|
+
)[0][0].group_id]
|
73
|
+
|
74
|
+
if config[:security_group_ids].nil?
|
75
|
+
fail "The group tagged '#{config[:security_group_filter][:tag]}\
|
76
|
+
#{config[:security_group_filter][:value]}' does not exist!"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
43
80
|
i = {
|
44
81
|
:instance_type => config[:instance_type],
|
45
82
|
:ebs_optimized => config[:ebs_optimized],
|
@@ -56,6 +93,14 @@ module Kitchen
|
|
56
93
|
end
|
57
94
|
i[:placement] = { :availability_zone => availability_zone.downcase }
|
58
95
|
end
|
96
|
+
tenancy = config[:tenancy]
|
97
|
+
if tenancy && %w[default dedicated].include?(tenancy)
|
98
|
+
if i.key?(:placement)
|
99
|
+
i[:placement][:tenancy] = tenancy
|
100
|
+
else
|
101
|
+
i[:placement] = { :tenancy => tenancy }
|
102
|
+
end
|
103
|
+
end
|
59
104
|
unless config[:block_device_mappings].nil? || config[:block_device_mappings].empty?
|
60
105
|
i[:block_device_mappings] = config[:block_device_mappings]
|
61
106
|
end
|
@@ -84,6 +129,21 @@ module Kitchen
|
|
84
129
|
i[:network_interfaces][0][:groups] = i.delete(:security_group_ids)
|
85
130
|
end
|
86
131
|
end
|
132
|
+
availability_zone = config[:availability_zone]
|
133
|
+
if availability_zone
|
134
|
+
if availability_zone =~ /^[a-z]$/i
|
135
|
+
availability_zone = "#{config[:region]}#{availability_zone}"
|
136
|
+
end
|
137
|
+
i[:placement] = { :availability_zone => availability_zone.downcase }
|
138
|
+
end
|
139
|
+
tenancy = config[:tenancy]
|
140
|
+
if tenancy && %w[default dedicated].include?(tenancy)
|
141
|
+
if i.key?(:placement)
|
142
|
+
i[:placement][:tenancy] = tenancy
|
143
|
+
else
|
144
|
+
i[:placement] = { :tenancy => tenancy }
|
145
|
+
end
|
146
|
+
end
|
87
147
|
unless config[:instance_initiated_shutdown_behavior].nil? ||
|
88
148
|
config[:instance_initiated_shutdown_behavior].empty?
|
89
149
|
i[:instance_initiated_shutdown_behavior] = config[:instance_initiated_shutdown_behavior]
|
@@ -16,7 +16,8 @@ module Kitchen
|
|
16
16
|
#
|
17
17
|
# "windows" -> [nil, nil, nil]
|
18
18
|
# Windows_Server-*-R*_RTM-, Windows_Server-*-R*_SP*-,
|
19
|
-
# Windows_Server-*-RTM-, Windows_Server-*-SP
|
19
|
+
# Windows_Server-*-RTM-, Windows_Server-*-SP*-,
|
20
|
+
# Windows_Server-*-
|
20
21
|
# "windows-2012" -> [2012, 0, nil]
|
21
22
|
# Windows_Server-2012-RTM-, Windows_Server-2012-SP*-
|
22
23
|
# "windows-2012r2" -> [2012, 2, nil]
|
@@ -29,6 +30,8 @@ module Kitchen
|
|
29
30
|
# Windows_Server-2012-R2_SP1-
|
30
31
|
# "windows-2012r2rtm" -> [2012, 2, 0]
|
31
32
|
# Windows_Server-2012-R2_RTM-
|
33
|
+
# "windows-2016" -> [2016, 0, nil]
|
34
|
+
# Windows_Server-2016-
|
32
35
|
def image_search
|
33
36
|
search = {
|
34
37
|
"owner-alias" => "amazon",
|
@@ -75,6 +78,7 @@ module Kitchen
|
|
75
78
|
# 2012r2sp4 -> [ 2012, 2, 4 ]
|
76
79
|
# 2012sp4 -> [ 2012, 0, 4 ]
|
77
80
|
# 2012rtm -> [ 2012, 0, 0 ]
|
81
|
+
# 2016 -> [ 2016, 0, nil ]
|
78
82
|
def windows_version_parts
|
79
83
|
version = self.version
|
80
84
|
if version
|
@@ -106,29 +110,35 @@ module Kitchen
|
|
106
110
|
|
107
111
|
private
|
108
112
|
|
109
|
-
def windows_name_filter
|
113
|
+
def windows_name_filter # rubocop:disable Metrics/MethodLength
|
110
114
|
major, revision, service_pack = windows_version_parts
|
111
115
|
|
112
|
-
|
113
|
-
|
114
|
-
revision_strings = ["", "R*_"]
|
115
|
-
when 0
|
116
|
-
revision_strings = [""]
|
116
|
+
if major == 2016
|
117
|
+
"Windows_Server-2016-English-Full-Base-*"
|
117
118
|
else
|
118
|
-
|
119
|
-
|
119
|
+
case revision
|
120
|
+
when nil
|
121
|
+
revision_strings = ["", "R*_"]
|
122
|
+
when 0
|
123
|
+
revision_strings = [""]
|
124
|
+
else
|
125
|
+
revision_strings = ["R#{revision}_"]
|
126
|
+
end
|
120
127
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
128
|
+
case service_pack
|
129
|
+
when nil
|
130
|
+
revision_strings = revision_strings.flat_map { |r| ["#{r}RTM", "#{r}SP*"] }
|
131
|
+
when 0
|
132
|
+
revision_strings = revision_strings.map { |r| "#{r}RTM" }
|
133
|
+
else
|
134
|
+
revision_strings = revision_strings.map { |r| "#{r}SP#{service_pack}" }
|
135
|
+
end
|
129
136
|
|
130
|
-
|
131
|
-
|
137
|
+
name_filter = revision_strings.map do |r|
|
138
|
+
"Windows_Server-#{major || "*"}-#{r}-English-*-Base-*"
|
139
|
+
end
|
140
|
+
name_filter << "Windows_Server-*-English-Full-Base-*" if major.nil?
|
141
|
+
name_filter
|
132
142
|
end
|
133
143
|
end
|
134
144
|
end
|
data/lib/kitchen/driver/ec2.rb
CHANGED
@@ -81,7 +81,9 @@ module Kitchen
|
|
81
81
|
default_config :interface, nil
|
82
82
|
default_config :http_proxy, ENV["HTTPS_PROXY"] || ENV["HTTP_PROXY"]
|
83
83
|
default_config :retry_limit, 3
|
84
|
+
default_config :tenancy, "default"
|
84
85
|
default_config :instance_initiated_shutdown_behavior, nil
|
86
|
+
default_config :ssl_verify_peer, true
|
85
87
|
|
86
88
|
def initialize(*args, &block)
|
87
89
|
super
|
@@ -194,6 +196,8 @@ module Kitchen
|
|
194
196
|
# Tagging can fail with a NotFound error even though we waited until the server exists
|
195
197
|
# Waiting can also fail, so we have to also retry on that. If it means we re-tag the
|
196
198
|
# instance, so be it.
|
199
|
+
# Tagging an instance is possible before volumes are attached. Tagging the volumes after
|
200
|
+
# instance creation is consistent.
|
197
201
|
Retryable.retryable(
|
198
202
|
:tries => 10,
|
199
203
|
:sleep => lambda { |n| [2**n, 30].min },
|
@@ -204,6 +208,8 @@ module Kitchen
|
|
204
208
|
|
205
209
|
state[:server_id] = server.id
|
206
210
|
info("EC2 instance <#{state[:server_id]}> created.")
|
211
|
+
wait_until_volumes_ready(server, state)
|
212
|
+
tag_volumes(server)
|
207
213
|
wait_until_ready(server, state)
|
208
214
|
end
|
209
215
|
|
@@ -296,9 +302,14 @@ module Kitchen
|
|
296
302
|
end
|
297
303
|
|
298
304
|
def update_username(state)
|
299
|
-
#
|
300
|
-
#
|
301
|
-
|
305
|
+
# BUG: With the following equality condition on username, if the user specifies 'root'
|
306
|
+
# as the transport's username then we will overwrite that value with one from the standard
|
307
|
+
# platform definitions. This seems difficult to handle here as the default username is
|
308
|
+
# provided by the underlying transport classes, and is often non-nil (eg; 'root'), leaving
|
309
|
+
# us no way to distinguish a user-set value from the transport's default.
|
310
|
+
# See https://github.com/test-kitchen/kitchen-ec2/pull/273
|
311
|
+
if actual_platform &&
|
312
|
+
instance.transport[:username] == instance.transport.class.defaults[:username]
|
302
313
|
debug("No SSH username specified: using default username #{actual_platform.username} " \
|
303
314
|
" for image #{config[:image_id]}, which we detected as #{actual_platform}.")
|
304
315
|
state[:username] = actual_platform.username
|
@@ -313,7 +324,8 @@ module Kitchen
|
|
313
324
|
config[:aws_secret_access_key],
|
314
325
|
config[:aws_session_token],
|
315
326
|
config[:http_proxy],
|
316
|
-
config[:retry_limit]
|
327
|
+
config[:retry_limit],
|
328
|
+
config[:ssl_verify_peer]
|
317
329
|
)
|
318
330
|
end
|
319
331
|
|
@@ -356,9 +368,11 @@ module Kitchen
|
|
356
368
|
end
|
357
369
|
|
358
370
|
def create_spot_request
|
371
|
+
request_duration = config[:retryable_tries] * config[:retryable_sleep]
|
359
372
|
request_data = {
|
360
373
|
:spot_price => config[:spot_price].to_s,
|
361
|
-
:launch_specification => instance_generator.ec2_instance_data
|
374
|
+
:launch_specification => instance_generator.ec2_instance_data,
|
375
|
+
:valid_until => Time.now + request_duration
|
362
376
|
}
|
363
377
|
if config[:block_duration_minutes]
|
364
378
|
request_data[:block_duration_minutes] = config[:block_duration_minutes]
|
@@ -378,6 +392,34 @@ module Kitchen
|
|
378
392
|
end
|
379
393
|
end
|
380
394
|
|
395
|
+
def tag_volumes(server)
|
396
|
+
tags = []
|
397
|
+
config[:tags].each do |k, v|
|
398
|
+
tags << { :key => k, :value => v }
|
399
|
+
end
|
400
|
+
server.volumes.each do |volume|
|
401
|
+
volume.create_tags(:tags => tags)
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
405
|
+
# Compares the requested volume count vs what has actually been set to be
|
406
|
+
# attached to the instance. The information requested through
|
407
|
+
# ec2.client.described_volumes is updated before the instance volume
|
408
|
+
# information.
|
409
|
+
def wait_until_volumes_ready(server, state)
|
410
|
+
wait_with_destroy(server, state, "volumes to be ready") do |aws_instance|
|
411
|
+
described_volume_count = 0
|
412
|
+
ready_volume_count = 0
|
413
|
+
if aws_instance.exists?
|
414
|
+
described_volume_count = ec2.client.describe_volumes(:filters => [
|
415
|
+
{ :name => "attachment.instance-id", :values => ["#{state[:server_id]}"] }]
|
416
|
+
).volumes.length
|
417
|
+
aws_instance.volumes.each { ready_volume_count += 1 }
|
418
|
+
end
|
419
|
+
(described_volume_count > 0) && (described_volume_count == ready_volume_count)
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
381
423
|
# Normally we could use `server.wait_until_running` but we actually need
|
382
424
|
# to check more than just the instance state
|
383
425
|
def wait_until_ready(server, state)
|
@@ -513,10 +555,15 @@ module Kitchen
|
|
513
555
|
EOH
|
514
556
|
end
|
515
557
|
|
558
|
+
if actual_platform.version =~ /2016/
|
559
|
+
logfile_name = 'C:\\ProgramData\\Amazon\\EC2-Windows\\Launch\\Log\\kitchen-ec2.log'
|
560
|
+
else
|
561
|
+
logfile_name = 'C:\\Program Files\\Amazon\\Ec2ConfigService\\Logs\\kitchen-ec2.log'
|
562
|
+
end
|
516
563
|
# Returning the fully constructed PowerShell script to user_data
|
517
564
|
Kitchen::Util.outdent!(<<-EOH)
|
518
565
|
<powershell>
|
519
|
-
$logfile
|
566
|
+
$logfile=#{logfile_name}
|
520
567
|
# Allow script execution
|
521
568
|
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Force
|
522
569
|
#PS Remoting and & winrm.cmd basic config
|
@@ -21,45 +21,48 @@ require "climate_control"
|
|
21
21
|
|
22
22
|
describe Kitchen::Driver::Aws::Client do
|
23
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
24
|
# nothing else is set, so we default to this
|
33
25
|
it "loads IAM credentials last" do
|
34
|
-
|
35
|
-
|
36
|
-
|
26
|
+
iam = instance_double(Aws::InstanceProfileCredentials)
|
27
|
+
|
28
|
+
allow(Kitchen::Driver::Aws::Client).to receive(:get_shared_creds).and_return(false)
|
29
|
+
allow(Aws::InstanceProfileCredentials).to receive(:new).and_return(iam)
|
30
|
+
|
31
|
+
env_creds(nil, nil) do
|
32
|
+
expect(Kitchen::Driver::Aws::Client.get_credentials(nil, nil, nil, nil, nil)).to eq(iam)
|
33
|
+
end
|
37
34
|
end
|
38
35
|
|
39
|
-
it "loads shared credentials second to last" do
|
40
|
-
|
41
|
-
|
36
|
+
it "loads the shared credentials file second to last" do
|
37
|
+
shared = instance_double(Aws::SharedCredentials)
|
38
|
+
|
39
|
+
allow(Aws::SharedCredentials).to \
|
40
|
+
receive(:new).with(:profile_name => "profile").and_return(shared)
|
41
|
+
|
42
|
+
env_creds(nil, nil) do
|
43
|
+
expect(Kitchen::Driver::Aws::Client.get_credentials("profile", nil, nil, nil, nil)).to \
|
44
|
+
eq(shared)
|
45
|
+
end
|
42
46
|
end
|
43
47
|
|
44
|
-
it "loads
|
45
|
-
|
46
|
-
|
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 \
|
48
|
+
it "loads credentials from the environment third to last" do
|
49
|
+
env_creds("key_id", "secret") do
|
50
|
+
expect(Kitchen::Driver::Aws::Client.get_credentials(nil, nil, nil, nil, nil)).to \
|
52
51
|
be_a(Aws::Credentials).and have_attributes(
|
53
|
-
:access_key_id => "
|
54
|
-
:secret_access_key => "
|
55
|
-
:session_token => "token1"
|
52
|
+
:access_key_id => "key_id",
|
53
|
+
:secret_access_key => "secret"
|
56
54
|
)
|
57
55
|
end
|
58
56
|
end
|
59
57
|
|
60
58
|
it "loads provided credentials first" do
|
61
|
-
expect(
|
62
|
-
|
59
|
+
expect(Kitchen::Driver::Aws::Client.get_credentials(
|
60
|
+
"profile",
|
61
|
+
"key3",
|
62
|
+
"value3",
|
63
|
+
nil,
|
64
|
+
"us-west-1"
|
65
|
+
)).to \
|
63
66
|
be_a(Aws::Credentials).and have_attributes(
|
64
67
|
:access_key_id => "key3",
|
65
68
|
:secret_access_key => "value3",
|
@@ -68,8 +71,13 @@ describe Kitchen::Driver::Aws::Client do
|
|
68
71
|
end
|
69
72
|
|
70
73
|
it "uses a session token if provided" do
|
71
|
-
expect(
|
72
|
-
|
74
|
+
expect(Kitchen::Driver::Aws::Client.get_credentials(
|
75
|
+
"profile",
|
76
|
+
"key3",
|
77
|
+
"value3",
|
78
|
+
"t",
|
79
|
+
"us-west-1"
|
80
|
+
)).to \
|
73
81
|
be_a(Aws::Credentials).and have_attributes(
|
74
82
|
:access_key_id => "key3",
|
75
83
|
:secret_access_key => "value3",
|
@@ -78,6 +86,55 @@ describe Kitchen::Driver::Aws::Client do
|
|
78
86
|
end
|
79
87
|
end
|
80
88
|
|
89
|
+
describe "::get_credentials + STS AssumeRole" do
|
90
|
+
let(:shared) { instance_double(Aws::SharedCredentials) }
|
91
|
+
let(:iam) { instance_double(Aws::InstanceProfileCredentials) }
|
92
|
+
let(:assume_role) { instance_double(Aws::AssumeRoleCredentials) }
|
93
|
+
let(:sts_client) { instance_double(Aws::STS::Client) }
|
94
|
+
|
95
|
+
before do
|
96
|
+
expect(Aws::AssumeRoleCredentials).to \
|
97
|
+
receive(:new).with(
|
98
|
+
:client => sts_client,
|
99
|
+
:role_arn => "role_arn",
|
100
|
+
:role_session_name => "role_session_name"
|
101
|
+
).and_return(assume_role)
|
102
|
+
end
|
103
|
+
|
104
|
+
# nothing else is set, so we default to this
|
105
|
+
it "loads an Instance Profile last" do
|
106
|
+
expect(Aws::InstanceProfileCredentials).to \
|
107
|
+
receive(:new).and_return(iam)
|
108
|
+
expect(Aws::STS::Client).to \
|
109
|
+
receive(:new).with(:credentials => iam, :region => "us-west-1").and_return(sts_client)
|
110
|
+
|
111
|
+
expect(Kitchen::Driver::Aws::Client.get_credentials(
|
112
|
+
nil,
|
113
|
+
nil,
|
114
|
+
nil,
|
115
|
+
nil,
|
116
|
+
"us-west-1",
|
117
|
+
:assume_role_arn => "role_arn", :assume_role_session_name => "role_session_name"
|
118
|
+
)).to eq(assume_role)
|
119
|
+
end
|
120
|
+
|
121
|
+
it "loads shared credentials second to last" do
|
122
|
+
expect(::Aws::SharedCredentials).to \
|
123
|
+
receive(:new).with(:profile_name => "profile").and_return(shared)
|
124
|
+
expect(Aws::STS::Client).to \
|
125
|
+
receive(:new).with(:credentials => shared, :region => "us-west-1").and_return(sts_client)
|
126
|
+
|
127
|
+
expect(Kitchen::Driver::Aws::Client.get_credentials(
|
128
|
+
"profile",
|
129
|
+
nil,
|
130
|
+
nil,
|
131
|
+
nil,
|
132
|
+
"us-west-1",
|
133
|
+
:assume_role_arn => "role_arn", :assume_role_session_name => "role_session_name"
|
134
|
+
)).to eq(assume_role)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
81
138
|
let(:client) { Kitchen::Driver::Aws::Client.new("us-west-1") }
|
82
139
|
|
83
140
|
describe "#initialize" do
|
@@ -100,7 +157,8 @@ describe Kitchen::Driver::Aws::Client do
|
|
100
157
|
"secret_access_key",
|
101
158
|
"session_token",
|
102
159
|
"http_proxy",
|
103
|
-
999
|
160
|
+
999,
|
161
|
+
false
|
104
162
|
)
|
105
163
|
}
|
106
164
|
let(:creds) { double("creds") }
|
@@ -111,6 +169,7 @@ describe Kitchen::Driver::Aws::Client do
|
|
111
169
|
expect(Aws.config[:credentials]).to eq(creds)
|
112
170
|
expect(Aws.config[:http_proxy]).to eq("http_proxy")
|
113
171
|
expect(Aws.config[:retry_limit]).to eq(999)
|
172
|
+
expect(Aws.config[:ssl_verify_peer]).to eq(false)
|
114
173
|
end
|
115
174
|
end
|
116
175
|
end
|
@@ -123,4 +182,12 @@ describe Kitchen::Driver::Aws::Client do
|
|
123
182
|
expect(client.resource).to be_a(Aws::EC2::Resource)
|
124
183
|
end
|
125
184
|
|
185
|
+
def env_creds(key_id, secret, &block)
|
186
|
+
ClimateControl.modify(
|
187
|
+
"AWS_ACCESS_KEY_ID" => key_id,
|
188
|
+
"AWS_SECRET_ACCESS_KEY" => secret
|
189
|
+
) do
|
190
|
+
block.call
|
191
|
+
end
|
192
|
+
end
|
126
193
|
end
|
@@ -221,7 +221,8 @@ describe "Default images for various platforms" do
|
|
221
221
|
Windows_Server-*-RTM-English-*-Base-*
|
222
222
|
Windows_Server-*-SP*-English-*-Base-*
|
223
223
|
Windows_Server-*-R*_RTM-English-*-Base-*
|
224
|
-
Windows_Server-*-R*_SP*-English-*-Base-*
|
224
|
+
Windows_Server-*-R*_SP*-English-*-Base-*
|
225
|
+
Windows_Server-*-English-Full-Base-*] }
|
225
226
|
],
|
226
227
|
"windows-2008" => [
|
227
228
|
{ :name => "owner-alias", :values => %w[amazon] },
|
@@ -267,7 +268,8 @@ describe "Default images for various platforms" do
|
|
267
268
|
Windows_Server-*-RTM-English-*-Base-*
|
268
269
|
Windows_Server-*-SP*-English-*-Base-*
|
269
270
|
Windows_Server-*-R*_RTM-English-*-Base-*
|
270
|
-
Windows_Server-*-R*_SP*-English-*-Base-*
|
271
|
+
Windows_Server-*-R*_SP*-English-*-Base-*
|
272
|
+
Windows_Server-*-English-Full-Base-*] },
|
271
273
|
{ :name => "architecture", :values => %w[x86_64] }
|
272
274
|
],
|
273
275
|
"windows-2012r2-x86_64" => [
|
@@ -283,7 +285,8 @@ describe "Default images for various platforms" do
|
|
283
285
|
Windows_Server-*-RTM-English-*-Base-*
|
284
286
|
Windows_Server-*-SP*-English-*-Base-*
|
285
287
|
Windows_Server-*-R*_RTM-English-*-Base-*
|
286
|
-
Windows_Server-*-R*_SP*-English-*-Base-*
|
288
|
+
Windows_Server-*-R*_SP*-English-*-Base-*
|
289
|
+
Windows_Server-*-English-Full-Base-*] }
|
287
290
|
],
|
288
291
|
"windows-server-2012r2-x86_64" => [
|
289
292
|
{ :name => "owner-alias", :values => %w[amazon] },
|
@@ -291,6 +294,11 @@ describe "Default images for various platforms" do
|
|
291
294
|
Windows_Server-2012-R2_RTM-English-*-Base-*
|
292
295
|
Windows_Server-2012-R2_SP*-English-*-Base-*] },
|
293
296
|
{ :name => "architecture", :values => %w[x86_64] }
|
297
|
+
],
|
298
|
+
"windows-2016" => [
|
299
|
+
{ :name => "owner-alias", :values => %w[amazon] },
|
300
|
+
{ :name => "name", :values => %w[
|
301
|
+
Windows_Server-2016-English-Full-Base-*] }
|
294
302
|
]
|
295
303
|
}
|
296
304
|
|
@@ -20,6 +20,7 @@ require "kitchen/driver/aws/instance_generator"
|
|
20
20
|
require "kitchen/driver/aws/client"
|
21
21
|
require "tempfile"
|
22
22
|
require "base64"
|
23
|
+
require "aws-sdk"
|
23
24
|
|
24
25
|
describe Kitchen::Driver::Aws::InstanceGenerator do
|
25
26
|
|
@@ -59,6 +60,28 @@ describe Kitchen::Driver::Aws::InstanceGenerator do
|
|
59
60
|
end
|
60
61
|
|
61
62
|
describe "#ec2_instance_data" do
|
63
|
+
ec2_stub = Aws::EC2::Client.new(:stub_responses => true)
|
64
|
+
|
65
|
+
ec2_stub.stub_responses(
|
66
|
+
:describe_subnets,
|
67
|
+
:subnets => [
|
68
|
+
{
|
69
|
+
:subnet_id => "s-123",
|
70
|
+
:tags => [{ :key => "foo", :value => "bar" }]
|
71
|
+
}
|
72
|
+
]
|
73
|
+
)
|
74
|
+
|
75
|
+
ec2_stub.stub_responses(
|
76
|
+
:describe_security_groups,
|
77
|
+
:security_groups => [
|
78
|
+
{
|
79
|
+
:group_id => "sg-123",
|
80
|
+
:tags => [{ :key => "foo", :value => "bar" }]
|
81
|
+
}
|
82
|
+
]
|
83
|
+
)
|
84
|
+
|
62
85
|
it "returns empty on nil" do
|
63
86
|
expect(generator.ec2_instance_data).to eq(
|
64
87
|
:instance_type => nil,
|
@@ -117,6 +140,69 @@ describe Kitchen::Driver::Aws::InstanceGenerator do
|
|
117
140
|
end
|
118
141
|
end
|
119
142
|
|
143
|
+
context "when provided subnet tag instead of id" do
|
144
|
+
let(:config) do
|
145
|
+
{
|
146
|
+
:instance_type => "micro",
|
147
|
+
:ebs_optimized => true,
|
148
|
+
:image_id => "ami-123",
|
149
|
+
:aws_ssh_key_id => "key",
|
150
|
+
:subnet_id => nil,
|
151
|
+
:region => "us-west-2",
|
152
|
+
:subnet_filter =>
|
153
|
+
{
|
154
|
+
:tag => "foo",
|
155
|
+
:value => "bar"
|
156
|
+
}
|
157
|
+
}
|
158
|
+
end
|
159
|
+
|
160
|
+
it "generates id from the provided tag" do
|
161
|
+
allow(::Aws::EC2::Client).to receive(:new).and_return(ec2_stub)
|
162
|
+
expect(ec2_stub).to receive(:describe_subnets).with(
|
163
|
+
:filters => [
|
164
|
+
{
|
165
|
+
:name => "tag:foo",
|
166
|
+
:values => ["bar"]
|
167
|
+
}
|
168
|
+
]
|
169
|
+
).and_return(ec2_stub.describe_subnets)
|
170
|
+
expect(generator.ec2_instance_data[:subnet_id]).to eq("s-123")
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
context "when provided security_group tag instead of id" do
|
175
|
+
let(:config) do
|
176
|
+
{
|
177
|
+
:instance_type => "micro",
|
178
|
+
:ebs_optimized => true,
|
179
|
+
:image_id => "ami-123",
|
180
|
+
:aws_ssh_key_id => "key",
|
181
|
+
:subnet_id => "s-123",
|
182
|
+
:security_group_ids => nil,
|
183
|
+
:region => "us-west-2",
|
184
|
+
:security_group_filter =>
|
185
|
+
{
|
186
|
+
:tag => "foo",
|
187
|
+
:value => "bar"
|
188
|
+
}
|
189
|
+
}
|
190
|
+
end
|
191
|
+
|
192
|
+
it "generates id from the provided tag" do
|
193
|
+
allow(::Aws::EC2::Client).to receive(:new).and_return(ec2_stub)
|
194
|
+
expect(ec2_stub).to receive(:describe_security_groups).with(
|
195
|
+
:filters => [
|
196
|
+
{
|
197
|
+
:name => "tag:foo",
|
198
|
+
:values => ["bar"]
|
199
|
+
}
|
200
|
+
]
|
201
|
+
).and_return(ec2_stub.describe_security_groups)
|
202
|
+
expect(generator.ec2_instance_data[:security_group_ids]).to eq(["sg-123"])
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
120
206
|
context "when passed an empty block_device_mappings" do
|
121
207
|
let(:config) do
|
122
208
|
{
|
@@ -200,6 +286,128 @@ describe Kitchen::Driver::Aws::InstanceGenerator do
|
|
200
286
|
end
|
201
287
|
end
|
202
288
|
|
289
|
+
context "when availability_zone and tenancy are provided" do
|
290
|
+
let(:config) do
|
291
|
+
{
|
292
|
+
:region => "eu-east-1",
|
293
|
+
:availability_zone => "c",
|
294
|
+
:tenancy => "dedicated"
|
295
|
+
}
|
296
|
+
end
|
297
|
+
it "adds the region to it in the instance data" do
|
298
|
+
expect(generator.ec2_instance_data).to eq(
|
299
|
+
:instance_type => nil,
|
300
|
+
:ebs_optimized => nil,
|
301
|
+
:image_id => nil,
|
302
|
+
:key_name => nil,
|
303
|
+
:subnet_id => nil,
|
304
|
+
:private_ip_address => nil,
|
305
|
+
:placement => { :availability_zone => "eu-east-1c",
|
306
|
+
:tenancy => "dedicated" }
|
307
|
+
)
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
context "when tenancy is provided but availability_zone isn't" do
|
312
|
+
let(:config) do
|
313
|
+
{
|
314
|
+
:region => "eu-east-1",
|
315
|
+
:tenancy => "default"
|
316
|
+
}
|
317
|
+
end
|
318
|
+
it "is not added to the instance data" do
|
319
|
+
expect(generator.ec2_instance_data).to eq(
|
320
|
+
:instance_type => nil,
|
321
|
+
:ebs_optimized => nil,
|
322
|
+
:image_id => nil,
|
323
|
+
:key_name => nil,
|
324
|
+
:subnet_id => nil,
|
325
|
+
:private_ip_address => nil,
|
326
|
+
:placement => { :tenancy => "default" }
|
327
|
+
)
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
context "when tenancy is not supported" do
|
332
|
+
let(:config) do
|
333
|
+
{
|
334
|
+
:region => "eu-east-1",
|
335
|
+
:tenancy => "ephemeral"
|
336
|
+
}
|
337
|
+
end
|
338
|
+
it "is not added to the instance data" do
|
339
|
+
expect(generator.ec2_instance_data).to eq(
|
340
|
+
:instance_type => nil,
|
341
|
+
:ebs_optimized => nil,
|
342
|
+
:image_id => nil,
|
343
|
+
:key_name => nil,
|
344
|
+
:subnet_id => nil,
|
345
|
+
:private_ip_address => nil
|
346
|
+
)
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
context "when availability_zone and tenancy are provided" do
|
351
|
+
let(:config) do
|
352
|
+
{
|
353
|
+
:region => "eu-east-1",
|
354
|
+
:availability_zone => "c",
|
355
|
+
:tenancy => "dedicated"
|
356
|
+
}
|
357
|
+
end
|
358
|
+
it "adds the region to it in the instance data" do
|
359
|
+
expect(generator.ec2_instance_data).to eq(
|
360
|
+
:instance_type => nil,
|
361
|
+
:ebs_optimized => nil,
|
362
|
+
:image_id => nil,
|
363
|
+
:key_name => nil,
|
364
|
+
:subnet_id => nil,
|
365
|
+
:private_ip_address => nil,
|
366
|
+
:placement => { :availability_zone => "eu-east-1c",
|
367
|
+
:tenancy => "dedicated" }
|
368
|
+
)
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
context "when tenancy is provided but availability_zone isn't" do
|
373
|
+
let(:config) do
|
374
|
+
{
|
375
|
+
:region => "eu-east-1",
|
376
|
+
:tenancy => "default"
|
377
|
+
}
|
378
|
+
end
|
379
|
+
it "is not added to the instance data" do
|
380
|
+
expect(generator.ec2_instance_data).to eq(
|
381
|
+
:instance_type => nil,
|
382
|
+
:ebs_optimized => nil,
|
383
|
+
:image_id => nil,
|
384
|
+
:key_name => nil,
|
385
|
+
:subnet_id => nil,
|
386
|
+
:private_ip_address => nil,
|
387
|
+
:placement => { :tenancy => "default" }
|
388
|
+
)
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
context "when tenancy is not supported" do
|
393
|
+
let(:config) do
|
394
|
+
{
|
395
|
+
:region => "eu-east-1",
|
396
|
+
:tenancy => "ephemeral"
|
397
|
+
}
|
398
|
+
end
|
399
|
+
it "is not added to the instance data" do
|
400
|
+
expect(generator.ec2_instance_data).to eq(
|
401
|
+
:instance_type => nil,
|
402
|
+
:ebs_optimized => nil,
|
403
|
+
:image_id => nil,
|
404
|
+
:key_name => nil,
|
405
|
+
:subnet_id => nil,
|
406
|
+
:private_ip_address => nil
|
407
|
+
)
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
203
411
|
context "when subnet_id is provided" do
|
204
412
|
let(:config) do
|
205
413
|
{
|
@@ -70,6 +70,35 @@ describe Kitchen::Driver::Ec2 do
|
|
70
70
|
Kitchen::Driver::EC2_VERSION)
|
71
71
|
end
|
72
72
|
|
73
|
+
describe "default_config" do
|
74
|
+
context "Windows" do
|
75
|
+
let(:resource) { instance_double(::Aws::EC2::Resource, :image => image) }
|
76
|
+
before do
|
77
|
+
allow(driver).to receive(:windows_os?).and_return(true)
|
78
|
+
allow(client).to receive(:resource).and_return(resource)
|
79
|
+
allow(instance).to receive(:name).and_return("instance_name")
|
80
|
+
end
|
81
|
+
context "Windows 2016" do
|
82
|
+
let(:image) {
|
83
|
+
FakeImage.new(:name => "Windows_Server-2016-English-Full-Base-2017.01.11")
|
84
|
+
}
|
85
|
+
it "sets :user_data to something" do
|
86
|
+
expect(driver[:user_data]).to include
|
87
|
+
'$logfile=C:\\ProgramData\\Amazon\\EC2-Windows\\Launch\\Log\\kitchen-ec2.log'
|
88
|
+
end
|
89
|
+
end
|
90
|
+
context "Windows 2012R2" do
|
91
|
+
let(:image) {
|
92
|
+
FakeImage.new(:name => "Windows_Server-2012-R2_RTM-English-64Bit-Base-2017.01.11")
|
93
|
+
}
|
94
|
+
it "sets :user_data to something" do
|
95
|
+
expect(driver[:user_data]).to include
|
96
|
+
'$logfile=C:\\Program Files\\Amazon\\Ec2ConfigService\\Logs\\kitchen-ec2.log'
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
73
102
|
describe "#hostname" do
|
74
103
|
let(:public_dns_name) { nil }
|
75
104
|
let(:private_dns_name) { nil }
|
@@ -212,12 +241,16 @@ describe Kitchen::Driver::Ec2 do
|
|
212
241
|
|
213
242
|
before do
|
214
243
|
expect(driver).to receive(:instance).at_least(:once).and_return(instance)
|
244
|
+
allow(Time).to receive(:now).and_return(Time.now)
|
215
245
|
end
|
216
246
|
|
217
247
|
it "submits the server request" do
|
218
248
|
expect(generator).to receive(:ec2_instance_data).and_return({})
|
219
249
|
expect(actual_client).to receive(:request_spot_instances).with(
|
220
|
-
:spot_price => "",
|
250
|
+
:spot_price => "",
|
251
|
+
:launch_specification => {},
|
252
|
+
:valid_until => Time.now + (config[:retryable_tries] * config[:retryable_sleep]),
|
253
|
+
:block_duration_minutes => 60
|
221
254
|
).and_return(response)
|
222
255
|
expect(actual_client).to receive(:wait_until)
|
223
256
|
expect(client).to receive(:get_instance_from_spot_request).with("id")
|
@@ -243,6 +276,23 @@ describe Kitchen::Driver::Ec2 do
|
|
243
276
|
end
|
244
277
|
end
|
245
278
|
|
279
|
+
describe "#tag_volumes" do
|
280
|
+
let(:volume) { double("aws volume resource") }
|
281
|
+
before do
|
282
|
+
allow(server).to receive(:volumes).and_return([volume])
|
283
|
+
end
|
284
|
+
it "tags the instance volumes" do
|
285
|
+
config[:tags] = { :key1 => :value1, :key2 => :value2 }
|
286
|
+
expect(volume).to receive(:create_tags).with(
|
287
|
+
:tags => [
|
288
|
+
{ :key => :key1, :value => :value1 },
|
289
|
+
{ :key => :key2, :value => :value2 }
|
290
|
+
]
|
291
|
+
)
|
292
|
+
driver.tag_volumes(server)
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
246
296
|
describe "#wait_until_ready" do
|
247
297
|
let(:hostname) { "0.0.0.0" }
|
248
298
|
let(:msg) { "to become ready" }
|
@@ -286,6 +336,43 @@ describe Kitchen::Driver::Ec2 do
|
|
286
336
|
end
|
287
337
|
end
|
288
338
|
|
339
|
+
describe "#wait_until_volumes_ready" do
|
340
|
+
let(:aws_instance) { double("aws instance") }
|
341
|
+
let(:msg) { "volumes to be ready" }
|
342
|
+
let(:volume) { double("aws volume resource") }
|
343
|
+
|
344
|
+
before do
|
345
|
+
expect(driver).to receive(:wait_with_destroy).with(server, state, msg).and_yield(aws_instance)
|
346
|
+
end
|
347
|
+
it "first checks instance existence" do
|
348
|
+
expect(aws_instance).to receive(:exists?).and_return(false)
|
349
|
+
expect(driver.wait_until_volumes_ready(server, state)).to eq(false)
|
350
|
+
end
|
351
|
+
it "second, it checks for prescence of described volumes" do
|
352
|
+
expect(aws_instance).to receive(:exists?).and_return(true)
|
353
|
+
expect(actual_client).to receive_message_chain(:describe_volumes, :volumes, :length
|
354
|
+
).and_return(0)
|
355
|
+
expect(aws_instance).to receive(:volumes).and_return([])
|
356
|
+
expect(driver.wait_until_volumes_ready(server, state)).to eq(false)
|
357
|
+
end
|
358
|
+
it "third, it compares the described volumes and instance volumes" do
|
359
|
+
expect(aws_instance).to receive(:exists?).and_return(true)
|
360
|
+
expect(actual_client).to receive_message_chain(:describe_volumes, :volumes, :length
|
361
|
+
).and_return(2)
|
362
|
+
expect(aws_instance).to receive(:volumes).and_return([volume])
|
363
|
+
expect(driver.wait_until_volumes_ready(server, state)).to eq(false)
|
364
|
+
end
|
365
|
+
context "when it exists, and both client and instance agree on volumes" do
|
366
|
+
it "returns true" do
|
367
|
+
expect(aws_instance).to receive(:exists?).and_return(true)
|
368
|
+
expect(actual_client).to receive_message_chain(:describe_volumes, :volumes, :length
|
369
|
+
).and_return(1)
|
370
|
+
expect(aws_instance).to receive(:volumes).and_return([volume])
|
371
|
+
expect(driver.wait_until_volumes_ready(server, state)).to eq(true)
|
372
|
+
end
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
289
376
|
describe "#fetch_windows_admin_password" do
|
290
377
|
let(:msg) { "to fetch windows admin password" }
|
291
378
|
let(:aws_instance) { double("aws instance") }
|
@@ -362,6 +449,8 @@ describe Kitchen::Driver::Ec2 do
|
|
362
449
|
expect(server).to receive(:wait_until_exists)
|
363
450
|
expect(driver).to receive(:update_username)
|
364
451
|
expect(driver).to receive(:tag_server).with(server)
|
452
|
+
expect(driver).to receive(:tag_volumes).with(server)
|
453
|
+
expect(driver).to receive(:wait_until_volumes_ready).with(server, state)
|
365
454
|
expect(driver).to receive(:wait_until_ready).with(server, state)
|
366
455
|
expect(transport).to receive_message_chain("connection.wait_until_ready")
|
367
456
|
expect(driver).to receive(:create_ec2_json).with(state)
|
@@ -399,6 +488,18 @@ describe Kitchen::Driver::Ec2 do
|
|
399
488
|
include_examples "common create"
|
400
489
|
end
|
401
490
|
|
491
|
+
context "instance is not a standard platform" do
|
492
|
+
let(:state) { {} }
|
493
|
+
before do
|
494
|
+
expect(driver).to receive(:actual_platform).and_return(nil)
|
495
|
+
end
|
496
|
+
|
497
|
+
it "doesn't set the state username" do
|
498
|
+
driver.update_username(state)
|
499
|
+
expect(state).to eq({})
|
500
|
+
end
|
501
|
+
end
|
502
|
+
|
402
503
|
end
|
403
504
|
|
404
505
|
describe "#destroy" do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kitchen-ec2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Fletcher Nichol
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-02-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: test-kitchen
|
@@ -253,7 +253,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
253
253
|
version: '0'
|
254
254
|
requirements: []
|
255
255
|
rubyforge_project:
|
256
|
-
rubygems_version: 2.
|
256
|
+
rubygems_version: 2.6.10
|
257
257
|
signing_key:
|
258
258
|
specification_version: 4
|
259
259
|
summary: A Test Kitchen Driver for Amazon EC2
|
@@ -263,4 +263,3 @@ test_files:
|
|
263
263
|
- spec/kitchen/driver/ec2/instance_generator_spec.rb
|
264
264
|
- spec/kitchen/driver/ec2_spec.rb
|
265
265
|
- spec/spec_helper.rb
|
266
|
-
has_rdoc:
|