kitchen-ec2 3.16.0 → 3.17.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 765177643e317a19c906f4d74bbeab0df60e3b24f40b56cf96cc1784511a2204
4
- data.tar.gz: bdd75366f92fdfda91a3e6e07076cee7fc15eec5a4099bffb1e119d2abbe10c6
3
+ metadata.gz: abdd51753a33ae7cf21aeb7f3073cd949d0c0c998eb065955b2bb8982441760a
4
+ data.tar.gz: f8a5ffd49f6213cc5822857f2af830be06fd42fe1c9a29cb60328b0fd4835e45
5
5
  SHA512:
6
- metadata.gz: ae46afd545a7228e429ccfc23dc8e55ec1e1526853ba2977020934a2d394bf1beffe3006f9205f5a39f35704329a3d965c2fc65a828cc1d49e5e2fd89214eadb
7
- data.tar.gz: 2ba5bbfb8c4bbc9ac2dfd827f427dfb4ecc42d548aa5e7c6926fbb2bdc1dfa52904b06f0a76cac1f03fb174513e2b604970540020ee7c3b1b684dedf329684b7
6
+ metadata.gz: b68043de14aab7e71d0b4c4570492e81b0e5b6af6e04ed0ed55df1db476f2f791de20030e0b630919a811c0605f8388719820ed3b7adf87d982b3a53b2b3d230
7
+ data.tar.gz: 74c9d0cea39fccd36f79ea1490f24946eac7ecd2670c0fe2434e1a43110727015e18472839a56b28f351e342d69eb038166edc45ecc00b4e15bfa9f393696461
@@ -22,16 +22,12 @@ require "aws-sdk-core/shared_credentials"
22
22
  require "aws-sdk-core/instance_profile_credentials"
23
23
 
24
24
  module Kitchen
25
-
26
25
  module Driver
27
-
28
26
  class Aws
29
-
30
27
  # A class for creating and managing the EC2 client connection
31
28
  #
32
29
  # @author Tyler Ball <tball@chef.io>
33
30
  class Client
34
-
35
31
  def initialize(
36
32
  region,
37
33
  profile_name = "default",
@@ -40,12 +36,12 @@ module Kitchen
40
36
  ssl_verify_peer = true
41
37
  )
42
38
  ::Aws.config.update(
43
- region: region,
39
+ region:,
44
40
  profile: profile_name,
45
- http_proxy: http_proxy,
46
- ssl_verify_peer: ssl_verify_peer
41
+ http_proxy:,
42
+ ssl_verify_peer:
47
43
  )
48
- ::Aws.config.update(retry_limit: retry_limit) unless retry_limit.nil?
44
+ ::Aws.config.update(retry_limit:) unless retry_limit.nil?
49
45
  end
50
46
 
51
47
  # create a new AWS EC2 instance
@@ -89,11 +85,7 @@ module Kitchen
89
85
  def resource
90
86
  @resource ||= ::Aws::EC2::Resource.new
91
87
  end
92
-
93
88
  end
94
-
95
89
  end
96
-
97
90
  end
98
-
99
91
  end
@@ -98,10 +98,10 @@ module Kitchen
98
98
  info("Deallocating dedicated host #{host_id}")
99
99
 
100
100
  response = ec2.client.release_hosts({ host_ids: [host_id] })
101
- unless response.unsuccessful.empty?
102
- warn "ERROR: Could not release dedicated host #{host_id}. Host may remain allocated and incur cost"
103
- exit!
104
- end
101
+ return if response.unsuccessful.empty?
102
+
103
+ warn "ERROR: Could not release dedicated host #{host_id}. Host may remain allocated and incur cost"
104
+ exit!
105
105
  end
106
106
 
107
107
  # return instance family from type
@@ -20,16 +20,12 @@ require "base64" unless defined?(Base64)
20
20
  require "aws-sdk-ec2"
21
21
 
22
22
  module Kitchen
23
-
24
23
  module Driver
25
-
26
24
  class Aws
27
-
28
25
  # A class for encapsulating the instance payload logic
29
26
  #
30
27
  # @author Tyler Ball <tball@chef.io>
31
28
  class InstanceGenerator
32
-
33
29
  attr_reader :config, :ec2, :logger
34
30
 
35
31
  def initialize(config, ec2, logger)
@@ -42,7 +38,7 @@ module Kitchen
42
38
  # can be passed in null, others need to be ommitted if they are null
43
39
  # Some fields can be passed in null, others need to be ommitted if they are null
44
40
  # @return [Hash]
45
- def ec2_instance_data # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
41
+ def ec2_instance_data
46
42
  # Support for looking up security group id and subnet id using tags.
47
43
  vpc_id = nil
48
44
  client = ::Aws::EC2::Client.new(region: config[:region])
@@ -129,8 +125,8 @@ module Kitchen
129
125
  # and Integers need to be represented as Strings
130
126
  { key: k, value: v.to_s }
131
127
  end
132
- instance_tag_spec = { resource_type: "instance", tags: tags }
133
- volume_tag_spec = { resource_type: "volume", tags: tags }
128
+ instance_tag_spec = { resource_type: "instance", tags: }
129
+ volume_tag_spec = { resource_type: "volume", tags: }
134
130
  i[:tag_specifications] = [instance_tag_spec, volume_tag_spec]
135
131
  end
136
132
 
@@ -146,7 +142,7 @@ module Kitchen
146
142
  if i.key?(:placement)
147
143
  i[:placement][:tenancy] = tenancy
148
144
  else
149
- i[:placement] = { tenancy: tenancy }
145
+ i[:placement] = { tenancy: }
150
146
  end
151
147
  end
152
148
  unless config[:block_device_mappings].nil? || config[:block_device_mappings].empty?
@@ -190,7 +186,7 @@ module Kitchen
190
186
  if i.key?(:placement)
191
187
  i[:placement][:tenancy] = tenancy
192
188
  else
193
- i[:placement] = { tenancy: tenancy }
189
+ i[:placement] = { tenancy: }
194
190
  end
195
191
  end
196
192
  unless config[:instance_initiated_shutdown_behavior].nil? ||
@@ -212,11 +208,7 @@ module Kitchen
212
208
 
213
209
  @user_data = Base64.encode64(raw_user_data)
214
210
  end
215
-
216
211
  end
217
-
218
212
  end
219
-
220
213
  end
221
-
222
214
  end
@@ -0,0 +1,52 @@
1
+ #
2
+ # Copyright:: 2023, Jared Kauppila
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ require_relative "../standard_platform"
17
+
18
+ module Kitchen
19
+ module Driver
20
+ class Aws
21
+ class StandardPlatform
22
+ # https://wiki.almalinux.org/cloud/AWS.html
23
+ class Alma < StandardPlatform
24
+ StandardPlatform.platforms["alma"] = self
25
+ StandardPlatform.platforms["almalinux"] = self
26
+
27
+ # default username for this platform's ami
28
+ # @return [String]
29
+ def username
30
+ "ec2-user"
31
+ end
32
+
33
+ def image_search
34
+ search = {
35
+ "owner-id" => "764336703387",
36
+ "name" => version ? "AlmaLinux OS #{version}*" : "AlmaLinux OS *",
37
+ }
38
+ search["architecture"] = architecture if architecture
39
+ search
40
+ end
41
+
42
+ def self.from_image(driver, image)
43
+ return unless /AlmaLinux OS/i.match?(image.name)
44
+
45
+ image.name =~ /\b(\d+(\.\d+)?)\b/i
46
+ new(driver, "alma", (Regexp.last_match || [])[1], image.architecture)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -39,10 +39,10 @@ module Kitchen
39
39
  end
40
40
 
41
41
  def self.from_image(driver, image)
42
- if /amzn-ami/i.match?(image.name)
43
- image.name =~ /\b(\d+(\.\d+[\.\d])?)/i
44
- new(driver, "amazon", (Regexp.last_match || [])[1], image.architecture)
45
- end
42
+ return unless /amzn-ami/i.match?(image.name)
43
+
44
+ image.name =~ /\b(\d+(\.\d+[\.\d])?)/i
45
+ new(driver, "amazon", (Regexp.last_match || [])[1], image.architecture)
46
46
  end
47
47
  end
48
48
  end
@@ -39,10 +39,10 @@ module Kitchen
39
39
  end
40
40
 
41
41
  def self.from_image(driver, image)
42
- if /amzn2-ami/i.match?(image.name)
43
- image.name =~ /\b(\d+(\.\d+[\.\d])?)/i
44
- new(driver, "amazon2", (Regexp.last_match || [])[1], image.architecture)
45
- end
42
+ return unless /amzn2-ami/i.match?(image.name)
43
+
44
+ image.name =~ /\b(\d+(\.\d+[\.\d])?)/i
45
+ new(driver, "amazon2", (Regexp.last_match || [])[1], image.architecture)
46
46
  end
47
47
  end
48
48
  end
@@ -39,10 +39,10 @@ module Kitchen
39
39
  end
40
40
 
41
41
  def self.from_image(driver, image)
42
- if /al2023-ami/i.match?(image.name)
43
- image.name =~ /\b(\d+(\.\d+[\.\d])?)/i
44
- new(driver, "amazon2023", (Regexp.last_match || [])[1], image.architecture)
45
- end
42
+ return unless /al2023-ami/i.match?(image.name)
43
+
44
+ image.name =~ /\b(\d+(\.\d+[\.\d])?)/i
45
+ new(driver, "amazon2023", (Regexp.last_match || [])[1], image.architecture)
46
46
  end
47
47
  end
48
48
  end
@@ -52,10 +52,10 @@ module Kitchen
52
52
  end
53
53
 
54
54
  def self.from_image(driver, image)
55
- if /centos/i.match?(image.name)
56
- image.name =~ /\b(\d+(\.\d+)?)\b/i
57
- new(driver, "centos", (Regexp.last_match || [])[1], image.architecture)
58
- end
55
+ return unless /centos/i.match?(image.name)
56
+
57
+ image.name =~ /\b(\d+(\.\d+)?)\b/i
58
+ new(driver, "centos", (Regexp.last_match || [])[1], image.architecture)
59
59
  end
60
60
  end
61
61
  end
@@ -73,16 +73,16 @@ module Kitchen
73
73
  end
74
74
 
75
75
  def self.from_image(driver, image)
76
- if /debian/i.match?(image.name)
77
- image.name =~ /\b(\d+|#{DEBIAN_CODENAMES.values.join("|")})\b/i
78
- version = (Regexp.last_match || [])[1]
79
- if version && version.to_i == 0
80
- version = DEBIAN_CODENAMES.find do |_v, codename|
81
- codename == version.downcase
82
- end.first
83
- end
84
- new(driver, "debian", version, image.architecture)
76
+ return unless /debian/i.match?(image.name)
77
+
78
+ image.name =~ /\b(\d+|#{DEBIAN_CODENAMES.values.join("|")})\b/i
79
+ version = (Regexp.last_match || [])[1]
80
+ if version&.to_i&.zero?
81
+ version = DEBIAN_CODENAMES.find do |_v, codename|
82
+ codename == version.downcase
83
+ end.first
85
84
  end
85
+ new(driver, "debian", version, image.architecture)
86
86
  end
87
87
  end
88
88
  end
@@ -39,10 +39,10 @@ module Kitchen
39
39
  end
40
40
 
41
41
  def self.from_image(driver, image)
42
- if /fedora/i.match?(image.name)
43
- image.name =~ /\b(\d+(\.\d+)?)\b/i
44
- new(driver, "fedora", (Regexp.last_match || [])[1], image.architecture)
45
- end
42
+ return unless /fedora/i.match?(image.name)
43
+
44
+ image.name =~ /\b(\d+(\.\d+)?)\b/i
45
+ new(driver, "fedora", (Regexp.last_match || [])[1], image.architecture)
46
46
  end
47
47
  end
48
48
  end
@@ -41,10 +41,10 @@ module Kitchen
41
41
  end
42
42
 
43
43
  def self.from_image(driver, image)
44
- if /freebsd/i.match?(image.name)
45
- image.name =~ /\b(\d+(\.\d+)?)\b/i
46
- new(driver, "freebsd", (Regexp.last_match || [])[1], image.architecture)
47
- end
44
+ return unless /freebsd/i.match?(image.name)
45
+
46
+ image.name =~ /\b(\d+(\.\d+)?)\b/i
47
+ new(driver, "freebsd", (Regexp.last_match || [])[1], image.architecture)
48
48
  end
49
49
  end
50
50
  end
@@ -39,10 +39,10 @@ module Kitchen
39
39
  end
40
40
 
41
41
  def self.from_image(driver, image)
42
- if /amzn-ec2-macos/i.match?(image.name)
43
- image.name =~ /\b(\d+(\.\d+[\.\d])?)/i
44
- new(driver, "macos", (Regexp.last_match || [])[1], image.architecture)
45
- end
42
+ return unless /amzn-ec2-macos/i.match?(image.name)
43
+
44
+ image.name =~ /\b(\d+(\.\d+[\.\d])?)/i
45
+ new(driver, "macos", (Regexp.last_match || [])[1], image.architecture)
46
46
  end
47
47
  end
48
48
  end
@@ -32,7 +32,7 @@ module Kitchen
32
32
  # default username for this platform's ami
33
33
  # @return [String]
34
34
  def username
35
- (version && version.to_f < 6.4) ? "root" : "ec2-user"
35
+ version && version.to_f < 6.4 ? "root" : "ec2-user"
36
36
  end
37
37
 
38
38
  def image_search
@@ -45,10 +45,10 @@ module Kitchen
45
45
  end
46
46
 
47
47
  def self.from_image(driver, image)
48
- if /rhel/i.match?(image.name)
49
- image.name =~ /\b(\d+(\.\d+)?)/i
50
- new(driver, "rhel", (Regexp.last_match || [])[1], image.architecture)
51
- end
48
+ return unless /rhel/i.match?(image.name)
49
+
50
+ image.name =~ /\b(\d+(\.\d+)?)/i
51
+ new(driver, "rhel", (Regexp.last_match || [])[1], image.architecture)
52
52
  end
53
53
 
54
54
  def sort_by_version(images)
@@ -0,0 +1,51 @@
1
+ #
2
+ # Copyright:: 2023, Jared Kauppila
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ require_relative "../standard_platform"
17
+
18
+ module Kitchen
19
+ module Driver
20
+ class Aws
21
+ class StandardPlatform
22
+ class Rocky < StandardPlatform
23
+ StandardPlatform.platforms["rocky"] = self
24
+ StandardPlatform.platforms["rockylinux"] = self
25
+
26
+ # default username for this platform's ami
27
+ # @return [String]
28
+ def username
29
+ "rocky"
30
+ end
31
+
32
+ def image_search
33
+ search = {
34
+ "owner-id" => "792107900819",
35
+ "name" => version ? "Rocky-#{version}-*" : "Rocky-*",
36
+ }
37
+ search["architecture"] = architecture if architecture
38
+ search
39
+ end
40
+
41
+ def self.from_image(driver, image)
42
+ return unless /Rocky-/i.match?(image.name)
43
+
44
+ image.name =~ /\b(\d+(\.\d+[\.\d])?)/i
45
+ new(driver, "rocky", (Regexp.last_match || [])[1], image.architecture)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -39,10 +39,10 @@ module Kitchen
39
39
  end
40
40
 
41
41
  def self.from_image(driver, image)
42
- if /ubuntu/i.match?(image.name)
43
- image.name =~ /\b(\d+(\.\d+)?)\b/i
44
- new(driver, "ubuntu", (Regexp.last_match || [])[1], image.architecture)
45
- end
42
+ return unless /ubuntu/i.match?(image.name)
43
+
44
+ image.name =~ /\b(\d+(\.\d+)?)\b/i
45
+ new(driver, "ubuntu", (Regexp.last_match || [])[1], image.architecture)
46
46
  end
47
47
  end
48
48
  end
@@ -70,21 +70,20 @@ module Kitchen
70
70
  end
71
71
 
72
72
  def self.from_image(driver, image)
73
- if /Windows/i.match?(image.name)
74
- # 2008 R2 SP2
75
- if image.name =~ /(\b\d+)\W*(r\d+)?/i
76
- major, revision = (Regexp.last_match || [])[1], (Regexp.last_match || [])[2]
77
- if image.name =~ /(sp\d+|rtm)/i
78
- service_pack = (Regexp.last_match || [])[1]
79
- end
80
- revision = revision.downcase if revision
81
- service_pack ||= "rtm"
82
- service_pack = service_pack.downcase
83
- version = "#{major}#{revision}#{service_pack}"
84
- end
85
-
86
- new(driver, "windows", version, image.architecture)
73
+ return unless /Windows/i.match?(image.name)
74
+
75
+ # 2008 R2 SP2
76
+ if image.name =~ /(\b\d+)\W*(r\d+)?/i
77
+ major = (Regexp.last_match || [])[1]
78
+ revision = (Regexp.last_match || [])[2]
79
+ service_pack = (Regexp.last_match || [])[1] if image.name =~ /(sp\d+|rtm)/i
80
+ revision = revision.downcase if revision
81
+ service_pack ||= "rtm"
82
+ service_pack = service_pack.downcase
83
+ version = "#{major}#{revision}#{service_pack}"
87
84
  end
85
+
86
+ new(driver, "windows", version, image.architecture)
88
87
  end
89
88
 
90
89
  protected
@@ -132,30 +131,30 @@ module Kitchen
132
131
 
133
132
  private
134
133
 
135
- def windows_name_filter # rubocop:disable Metrics/MethodLength
134
+ def windows_name_filter
136
135
  major, revision, service_pack = windows_version_parts
137
- if major == 2022 || major == 2019 || major == 2016
136
+ if [2022, 2019, 2016].include?(major)
138
137
  "Windows_Server-#{major}-English-Full-Base-*"
139
- elsif major == 1709 || major == 1803
138
+ elsif [1709, 1803].include?(major)
140
139
  "Windows_Server-#{major}-English-Core-ContainersLatest-*"
141
140
  else
142
- case revision
143
- when nil
144
- revision_strings = ["", "R*_"]
145
- when 0
146
- revision_strings = [""]
147
- else
148
- revision_strings = ["R#{revision}_"]
149
- end
150
-
151
- case service_pack
152
- when nil
153
- revision_strings = revision_strings.flat_map { |r| ["#{r}RTM", "#{r}SP*"] }
154
- when 0
155
- revision_strings = revision_strings.map { |r| "#{r}RTM" }
156
- else
157
- revision_strings = revision_strings.map { |r| "#{r}SP#{service_pack}" }
158
- end
141
+ revision_strings = case revision
142
+ when nil
143
+ ["", "R*_"]
144
+ when 0
145
+ [""]
146
+ else
147
+ ["R#{revision}_"]
148
+ end
149
+
150
+ revision_strings = case service_pack
151
+ when nil
152
+ revision_strings.flat_map { |r| ["#{r}RTM", "#{r}SP*"] }
153
+ when 0
154
+ revision_strings.map { |r| "#{r}RTM" }
155
+ else
156
+ revision_strings.map { |r| "#{r}SP#{service_pack}" }
157
+ end
159
158
 
160
159
  name_filter = revision_strings.map do |r|
161
160
  "Windows_Server-#{major || "*"}-#{r}-English-*-Base-*"
@@ -91,6 +91,11 @@ module Kitchen
91
91
  #
92
92
  SUPPORTED_ARCHITECTURES = %w{x86_64 i386 arm64}.freeze
93
93
 
94
+ #
95
+ # The list of supported ebs volume types
96
+ #
97
+ EBS_VOLUME_TYPES = %w{gp3 gp2}.freeze
98
+
94
99
  #
95
100
  # Find the best matching image for the given image search.
96
101
  #
@@ -103,12 +108,12 @@ module Kitchen
103
108
  end
104
109
 
105
110
  # We prefer most recent first
106
- images = driver.ec2.resource.images(filters: filters)
111
+ images = driver.ec2.resource.images(filters:)
107
112
  images = sort_images(images)
108
113
  show_returned_images(images)
109
114
 
110
115
  # Grab the best match
111
- images.first && images.first.id
116
+ images.first&.id
112
117
  end
113
118
 
114
119
  #
@@ -136,9 +141,9 @@ module Kitchen
136
141
  #
137
142
  def self.from_platform_string(driver, platform_string)
138
143
  platform, version, architecture = parse_platform_string(platform_string)
139
- if platform && platforms[platform]
140
- platforms[platform].new(driver, platform, version, architecture)
141
- end
144
+ return unless platform && platforms[platform]
145
+
146
+ platforms[platform].new(driver, platform, version, architecture)
142
147
  end
143
148
 
144
149
  #
@@ -157,6 +162,20 @@ module Kitchen
157
162
  nil
158
163
  end
159
164
 
165
+ def self.parse_platform_string(platform_string)
166
+ platform, version = platform_string.split("-", 2)
167
+
168
+ # If the right side is a valid architecture, use it as such
169
+ # i.e. debian-i386 or windows-server-2012r2-i386
170
+ if version && SUPPORTED_ARCHITECTURES.include?(version.split("-")[-1])
171
+ # server-2012r2-i386 -> server-2012r2, -, i386
172
+ version, _dash, architecture = version.rpartition("-")
173
+ version = nil if version == ""
174
+ end
175
+
176
+ [platform, version, architecture]
177
+ end
178
+
160
179
  protected
161
180
 
162
181
  #
@@ -192,20 +211,6 @@ module Kitchen
192
211
 
193
212
  private
194
213
 
195
- def self.parse_platform_string(platform_string)
196
- platform, version = platform_string.split("-", 2)
197
-
198
- # If the right side is a valid architecture, use it as such
199
- # i.e. debian-i386 or windows-server-2012r2-i386
200
- if version && SUPPORTED_ARCHITECTURES.include?(version.split("-")[-1])
201
- # server-2012r2-i386 -> server-2012r2, -, i386
202
- version, _dash, architecture = version.rpartition("-")
203
- version = nil if version == ""
204
- end
205
-
206
- [platform, version, architecture]
207
- end
208
-
209
214
  def sort_images(images)
210
215
  # P6: We prefer more recent images over older ones
211
216
  images = images.sort_by(&:creation_date).reverse
@@ -214,7 +219,7 @@ module Kitchen
214
219
  # P4: We prefer (SSD) (if available)
215
220
  images = prefer(images) do |image|
216
221
  image.block_device_mappings.any? do |b|
217
- b.device_name == image.root_device_name && b.ebs && %w{gp3 gp2}.any? { |t| b.ebs.volume_type == t }
222
+ b.device_name == image.root_device_name && b.ebs && EBS_VOLUME_TYPES.any?(b.ebs.volume_type)
218
223
  end
219
224
  end
220
225
  # P3: We prefer ebs over instance_store (if available)
@@ -24,12 +24,14 @@ require_relative "aws/client"
24
24
  require_relative "aws/dedicated_hosts"
25
25
  require_relative "aws/instance_generator"
26
26
  require_relative "aws/standard_platform"
27
+ require_relative "aws/standard_platform/alma"
27
28
  require_relative "aws/standard_platform/amazon"
28
29
  require_relative "aws/standard_platform/amazon2"
29
30
  require_relative "aws/standard_platform/amazon2023"
30
31
  require_relative "aws/standard_platform/centos"
31
32
  require_relative "aws/standard_platform/debian"
32
33
  require_relative "aws/standard_platform/rhel"
34
+ require_relative "aws/standard_platform/rocky"
33
35
  require_relative "aws/standard_platform/fedora"
34
36
  require_relative "aws/standard_platform/freebsd"
35
37
  require_relative "aws/standard_platform/macos"
@@ -43,20 +45,17 @@ require "etc" unless defined?(Etc)
43
45
  require "socket" unless defined?(Socket)
44
46
 
45
47
  module Kitchen
46
-
47
48
  module Driver
48
-
49
49
  # Amazon EC2 driver for Test Kitchen.
50
50
  #
51
51
  # @author Fletcher Nichol <fnichol@nichol.ca>
52
52
  class Ec2 < Kitchen::Driver::Base
53
-
54
53
  kitchen_driver_api_version 2
55
54
 
56
55
  plugin_version Kitchen::Driver::EC2_VERSION
57
56
 
58
57
  default_config :region, ENV["AWS_REGION"] || "us-east-1"
59
- default_config :shared_credentials_profile, ENV["AWS_PROFILE"]
58
+ default_config :shared_credentials_profile, ENV.fetch("AWS_PROFILE", nil)
60
59
  default_config :availability_zone, nil
61
60
  default_config :instance_type, &:default_instance_type
62
61
  default_config :ebs_optimized, false
@@ -80,14 +79,14 @@ module Kitchen
80
79
  default_config :aws_access_key_id, nil
81
80
  default_config :aws_secret_access_key, nil
82
81
  default_config :aws_session_token, nil
83
- default_config :aws_ssh_key_id, ENV["AWS_SSH_KEY_ID"]
82
+ default_config :aws_ssh_key_id, ENV.fetch("AWS_SSH_KEY_ID", nil)
84
83
  default_config :aws_ssh_key_type, "rsa"
85
84
  default_config :image_id, &:default_ami
86
85
  default_config :image_search, nil
87
86
  default_config :username, nil
88
87
  default_config :associate_public_ip, nil
89
88
  default_config :interface, nil
90
- default_config :http_proxy, ENV["HTTPS_PROXY"] || ENV["HTTP_PROXY"]
89
+ default_config :http_proxy, ENV["HTTPS_PROXY"] || ENV.fetch("HTTP_PROXY", nil)
91
90
  default_config :retry_limit, 3
92
91
  default_config :tenancy, "default"
93
92
  default_config :instance_initiated_shutdown_behavior, nil
@@ -192,10 +191,10 @@ module Kitchen
192
191
  end
193
192
 
194
193
  # empty keys cause failures when tagging and they make no sense
195
- validations[:tags] = lambda do |attr, val, _driver|
194
+ validations[:tags] = lambda do |_attr, val, _driver|
196
195
  # if someone puts the tags each on their own line it's an array not a hash
197
196
  # @todo we should probably just do the right thing and support this format
198
- if val.class == Array
197
+ if val.instance_of?(Array)
199
198
  warn "AWS instance tags must be specified as a single hash, not a tag " \
200
199
  "on each line. Example: {:foo => 'bar', :bar => 'foo'}"
201
200
  exit!
@@ -245,13 +244,13 @@ module Kitchen
245
244
  info("Auto placement on one dedicated host out of: #{hosts_with_capacity.map(&:host_id).join(", ")}")
246
245
  end
247
246
 
248
- if config[:spot_price]
249
- # Spot instance when a price is set
250
- server = with_request_limit_backoff(state) { submit_spots }
251
- else
252
- # On-demand instance
253
- server = with_request_limit_backoff(state) { submit_server }
254
- end
247
+ server = if config[:spot_price]
248
+ # Spot instance when a price is set
249
+ with_request_limit_backoff(state) { submit_spots }
250
+ else
251
+ # On-demand instance
252
+ with_request_limit_backoff(state) { submit_server }
253
+ end
255
254
  info("Instance <#{server.id}> requested.")
256
255
  with_request_limit_backoff(state) do
257
256
  logging_proc = ->(attempts) { info("Polling AWS for existence, attempt #{attempts}...") }
@@ -267,7 +266,7 @@ module Kitchen
267
266
  tries: 10,
268
267
  sleep: lambda { |n| [2**n, 30].min },
269
268
  on: ::Aws::EC2::Errors::InvalidInstanceIDNotFound
270
- ) do |r, _|
269
+ ) do |_r, _|
271
270
  wait_until_ready(server, state)
272
271
  end
273
272
 
@@ -318,10 +317,10 @@ module Kitchen
318
317
  delete_key(state)
319
318
 
320
319
  # Clean up dedicated hosts matching instance_type and unused (if allowed)
321
- if config[:tenancy] == "host" && allow_deallocate_host?
322
- empty_hosts = hosts_with_capacity.select { |host| host_unused?(host) }
323
- empty_hosts.each { |host| deallocate_host(host.host_id) }
324
- end
320
+ return unless config[:tenancy] == "host" && allow_deallocate_host?
321
+
322
+ empty_hosts = hosts_with_capacity.select { |host| host_unused?(host) }
323
+ empty_hosts.each { |host| deallocate_host(host.host_id) }
325
324
  end
326
325
 
327
326
  def image
@@ -425,7 +424,7 @@ module Kitchen
425
424
  def expand_config(conf, key)
426
425
  configs = []
427
426
 
428
- if conf[key] && conf[key].is_a?(Array)
427
+ if conf[key].is_a?(Array)
429
428
  values = conf[key]
430
429
  values.each do |value|
431
430
  new_config = conf.clone
@@ -497,11 +496,11 @@ module Kitchen
497
496
  instance_data = instance_generator.ec2_instance_data
498
497
 
499
498
  config_spot_price = config[:spot_price].to_s
500
- if %w{ondemand on-demand}.include?(config_spot_price)
501
- spot_price = ""
502
- else
503
- spot_price = config_spot_price
504
- end
499
+ spot_price = if %w{ondemand on-demand}.include?(config_spot_price)
500
+ ""
501
+ else
502
+ config_spot_price
503
+ end
505
504
  spot_options = {
506
505
  # Must use one-time in order to use instance_interruption_behavior=terminate
507
506
  # spot_instance_type: "one-time", # default
@@ -517,7 +516,7 @@ module Kitchen
517
516
 
518
517
  instance_data[:instance_market_options] = {
519
518
  market_type: "spot",
520
- spot_options: spot_options,
519
+ spot_options:,
521
520
  }
522
521
 
523
522
  # The preferred way to create a spot instance is via request_spot_instances()
@@ -549,7 +548,7 @@ module Kitchen
549
548
  aws_instance.state.name == "running" &&
550
549
  hostname != "0.0.0.0"
551
550
 
552
- if ready && ( hostname.nil? || hostname == "" )
551
+ if ready && (hostname.nil? || hostname == "")
553
552
  debug("Unable to detect hostname using interface_type #{config[:interface]}. Fallback to ordered mapping")
554
553
  state[:hostname] = hostname(aws_instance, nil)
555
554
  end
@@ -565,7 +564,7 @@ module Kitchen
565
564
  output = Base64.decode64(output)
566
565
  debug "Console output: --- \n#{output}"
567
566
  end
568
- ready = !!(output =~ /Windows is Ready to use/)
567
+ ready = !!(output.include?("Windows is Ready to use"))
569
568
  end
570
569
  end
571
570
  ready
@@ -598,7 +597,7 @@ module Kitchen
598
597
  end
599
598
 
600
599
  def fetch_windows_admin_password(server, state)
601
- wait_with_destroy(server, state, "to fetch windows admin password") do |aws_instance|
600
+ wait_with_destroy(server, state, "to fetch windows admin password") do |_aws_instance|
602
601
  enc = server.client.get_password_data(
603
602
  instance_id: state[:server_id]
604
603
  ).password_data
@@ -653,7 +652,7 @@ module Kitchen
653
652
  server.send(interface_type)
654
653
  else
655
654
  potential_hostname = nil
656
- INTERFACE_TYPES.values.each do |type|
655
+ INTERFACE_TYPES.each_value do |type|
657
656
  potential_hostname ||= server.send(type)
658
657
  # AWS returns an empty string if the dns name isn't populated yet
659
658
  potential_hostname = nil if potential_hostname == ""
@@ -715,7 +714,7 @@ module Kitchen
715
714
 
716
715
  # Preparing custom static admin user if we defined something other than Administrator
717
716
  custom_admin_script = ""
718
- if !(instance.transport[:username] =~ /administrator/i) && instance.transport[:password]
717
+ if instance.transport[:username] !~ /administrator/i && instance.transport[:password]
719
718
  custom_admin_script = Kitchen::Util.outdent!(<<-EOH)
720
719
  "Disabling Complex Passwords" >> $logfile
721
720
  $seccfg = [IO.Path]::GetTempFileName()
@@ -757,7 +756,7 @@ module Kitchen
757
756
  def image_info(image)
758
757
  root_device = image.block_device_mappings
759
758
  .find { |b| b.device_name == image.root_device_name }
760
- volume_type = " #{root_device.ebs.volume_type}" if root_device && root_device.ebs
759
+ volume_type = " #{root_device.ebs.volume_type}" if root_device&.ebs
761
760
 
762
761
  " Architecture: #{image.architecture}," \
763
762
  " Virtualization: #{image.virtualization_type}," \
@@ -811,6 +810,17 @@ module Kitchen
811
810
  params = {
812
811
  group_name: "kitchen-#{Array.new(8) { rand(36).to_s(36) }.join}",
813
812
  description: "Test Kitchen for #{instance.name} by #{Etc.getlogin || "nologin"} on #{Socket.gethostname}",
813
+ tag_specifications: [
814
+ {
815
+ resource_type: "security-group",
816
+ tags: [
817
+ {
818
+ key: "created-by",
819
+ value: "test-kitchen",
820
+ },
821
+ ],
822
+ },
823
+ ],
814
824
  }
815
825
  params[:vpc_id] = vpc_id if vpc_id
816
826
  resp = ec2.client.create_security_group(params)
@@ -827,7 +837,7 @@ module Kitchen
827
837
  from_port: port,
828
838
  to_port: port,
829
839
  ip_ranges: Array(config[:security_group_cidr_ip]).map do |cidr_ip|
830
- { cidr_ip: cidr_ip }
840
+ { cidr_ip: }
831
841
  end,
832
842
  }
833
843
  end
@@ -849,14 +859,28 @@ module Kitchen
849
859
  (Etc.getlogin || "nologin").gsub(/\W/, ""),
850
860
  Socket.gethostname.gsub(/\W/, "")[0..20],
851
861
  Time.now.utc.iso8601,
852
- Array.new(8) { rand(36).to_s(36) }.join(""),
862
+ Array.new(8) { rand(36).to_s(36) }.join,
853
863
  ]
854
864
  # In a perfect world this would generate the key locally and use ImportKey
855
865
  # instead for better security, but given the use case that is very likely
856
866
  # to rapidly exhaust local entropy by creating a lot of keys. So this is
857
867
  # probably fine. If you want very high security, probably don't use this
858
868
  # feature anyway.
859
- resp = ec2.client.create_key_pair(key_name: "kitchen-#{name_parts.join("-")}", key_type: config[:aws_ssh_key_type])
869
+ resp = ec2.client.create_key_pair(
870
+ key_name: "kitchen-#{name_parts.join("-")}",
871
+ key_type: config[:aws_ssh_key_type],
872
+ tag_specifications: [
873
+ {
874
+ resource_type: "key-pair",
875
+ tags: [
876
+ {
877
+ key: "created-by",
878
+ value: "test-kitchen",
879
+ },
880
+ ],
881
+ },
882
+ ]
883
+ )
860
884
  state[:auto_key_id] = resp.key_name
861
885
  info("Created automatic key pair #{state[:auto_key_id]}")
862
886
  # Write the key out with safe permissions
@@ -890,7 +914,7 @@ module Kitchen
890
914
  puts "ENI #{config[:elastic_network_interface_id]} already attached."
891
915
  end
892
916
  rescue ::Aws::EC2::Errors::InvalidNetworkInterfaceIDNotFound => e
893
- warn("#{e}")
917
+ warn(e.to_s)
894
918
  end
895
919
  end
896
920
 
@@ -920,7 +944,6 @@ module Kitchen
920
944
  state.delete(:auto_key_id)
921
945
  File.unlink("#{config[:kitchen_root]}/.kitchen/#{instance.name}.pem")
922
946
  end
923
-
924
947
  end
925
948
  end
926
949
  end
@@ -17,10 +17,8 @@
17
17
  # limitations under the License.
18
18
 
19
19
  module Kitchen
20
-
21
20
  module Driver
22
-
23
21
  # Version string for EC2 Test Kitchen driver
24
- EC2_VERSION = "3.16.0".freeze
22
+ EC2_VERSION = "3.17.1".freeze
25
23
  end
26
24
  end
metadata CHANGED
@@ -1,35 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kitchen-ec2
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.16.0
4
+ version: 3.17.1
5
5
  platform: ruby
6
6
  authors:
7
- - Fletcher Nichol
7
+ - Test Kitchen Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-03-16 00:00:00.000000000 Z
11
+ date: 2023-11-27 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: test-kitchen
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: 1.4.1
20
- - - "<"
21
- - !ruby/object:Gem::Version
22
- version: '4'
23
- type: :runtime
24
- prerelease: false
25
- version_requirements: !ruby/object:Gem::Requirement
26
- requirements:
27
- - - ">="
28
- - !ruby/object:Gem::Version
29
- version: 1.4.1
30
- - - "<"
31
- - !ruby/object:Gem::Version
32
- version: '4'
33
13
  - !ruby/object:Gem::Dependency
34
14
  name: aws-sdk-ec2
35
15
  requirement: !ruby/object:Gem::Requirement
@@ -64,9 +44,29 @@ dependencies:
64
44
  - - "<"
65
45
  - !ruby/object:Gem::Version
66
46
  version: '4.0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: test-kitchen
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 1.4.1
54
+ - - "<"
55
+ - !ruby/object:Gem::Version
56
+ version: '4'
57
+ type: :runtime
58
+ prerelease: false
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: 1.4.1
64
+ - - "<"
65
+ - !ruby/object:Gem::Version
66
+ version: '4'
67
67
  description: A Test Kitchen Driver for Amazon EC2
68
68
  email:
69
- - fnichol@nichol.ca
69
+ - help@sous-chefs.org
70
70
  executables: []
71
71
  extensions: []
72
72
  extra_rdoc_files: []
@@ -76,6 +76,7 @@ files:
76
76
  - lib/kitchen/driver/aws/dedicated_hosts.rb
77
77
  - lib/kitchen/driver/aws/instance_generator.rb
78
78
  - lib/kitchen/driver/aws/standard_platform.rb
79
+ - lib/kitchen/driver/aws/standard_platform/alma.rb
79
80
  - lib/kitchen/driver/aws/standard_platform/amazon.rb
80
81
  - lib/kitchen/driver/aws/standard_platform/amazon2.rb
81
82
  - lib/kitchen/driver/aws/standard_platform/amazon2023.rb
@@ -85,6 +86,7 @@ files:
85
86
  - lib/kitchen/driver/aws/standard_platform/freebsd.rb
86
87
  - lib/kitchen/driver/aws/standard_platform/macos.rb
87
88
  - lib/kitchen/driver/aws/standard_platform/rhel.rb
89
+ - lib/kitchen/driver/aws/standard_platform/rocky.rb
88
90
  - lib/kitchen/driver/aws/standard_platform/ubuntu.rb
89
91
  - lib/kitchen/driver/aws/standard_platform/windows.rb
90
92
  - lib/kitchen/driver/ec2.rb
@@ -108,7 +110,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
108
110
  - !ruby/object:Gem::Version
109
111
  version: '0'
110
112
  requirements: []
111
- rubygems_version: 3.4.3
113
+ rubygems_version: 3.4.10
112
114
  signing_key:
113
115
  specification_version: 4
114
116
  summary: A Test Kitchen Driver for Amazon EC2