kitchen-ec2 1.2.0 → 1.3.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.
- 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:
|