kitchen-ec2 3.16.0 → 3.17.1

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 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