kitchen-ec2 3.20.0 → 3.22.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
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3baf7353ac946ddb0ad70a816dc36b0017afaddddb8392bd8a26e0e2f2afbbe4
|
|
4
|
+
data.tar.gz: 53f26dec487092acfe0e9b8bf495e7727e161b43b0b45085e92391a85fc7efae
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a28a41f6067d48c4f233963a40e0b96756f06c30096f0dc0cded285f11b2c6f08c31f0a52762c89e26f223512304655ec807bfc0f20700150dfe01c0e2c489c4
|
|
7
|
+
data.tar.gz: d17ba3bb0ed6bba7744e4f0b8ebca626bb26fbfd11e37a00ce243a64e91a51d7df69af6a2f238bf13fe5a88de699b2248cb3fa2c3d014468d4ce9e2ac13f6bb5
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Author:: Alex Kokkinos
|
|
3
|
+
#
|
|
4
|
+
# Copyright:: 2025, Alex Kokkinos
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
require "aws-sdk-ec2instanceconnect"
|
|
18
|
+
|
|
19
|
+
module Kitchen
|
|
20
|
+
module Driver
|
|
21
|
+
class Aws
|
|
22
|
+
class InstanceConnect
|
|
23
|
+
def initialize(config, logger)
|
|
24
|
+
@config = config
|
|
25
|
+
@logger = logger
|
|
26
|
+
@client = ::Aws::EC2InstanceConnect::Client.new(region: config[:region])
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def send_ssh_public_key(instance_id, username, public_key)
|
|
30
|
+
@logger.info("Sending SSH public key to instance #{instance_id} for user #{username}")
|
|
31
|
+
|
|
32
|
+
@client.send_ssh_public_key({
|
|
33
|
+
instance_id: instance_id,
|
|
34
|
+
instance_os_user: username,
|
|
35
|
+
ssh_public_key: public_key,
|
|
36
|
+
availability_zone: @config[:availability_zone],
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
@logger.debug("SSH public key successfully sent to instance")
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Author:: GitHub Copilot
|
|
3
|
+
#
|
|
4
|
+
# Copyright:: 2025, GitHub
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
|
|
18
|
+
require "aws-sdk-ssm"
|
|
19
|
+
require "open3"
|
|
20
|
+
|
|
21
|
+
module Kitchen
|
|
22
|
+
module Driver
|
|
23
|
+
class Aws
|
|
24
|
+
# Manages AWS Systems Manager Session Manager connections for Test Kitchen
|
|
25
|
+
class SsmSessionManager
|
|
26
|
+
def initialize(config, logger)
|
|
27
|
+
@config = config
|
|
28
|
+
@logger = logger
|
|
29
|
+
@ssm_client = ::Aws::SSM::Client.new(
|
|
30
|
+
region: config[:region],
|
|
31
|
+
profile: config[:shared_credentials_profile]
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Check if SSM agent is running on the instance
|
|
36
|
+
def ssm_agent_available?(instance_id)
|
|
37
|
+
@logger.debug("Checking if SSM agent is available on instance #{instance_id}")
|
|
38
|
+
|
|
39
|
+
begin
|
|
40
|
+
resp = @ssm_client.describe_instance_information(
|
|
41
|
+
filters: [
|
|
42
|
+
{
|
|
43
|
+
key: "InstanceIds",
|
|
44
|
+
values: [instance_id],
|
|
45
|
+
},
|
|
46
|
+
]
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
available = !resp.instance_information_list.empty? &&
|
|
50
|
+
resp.instance_information_list.first.ping_status == "Online"
|
|
51
|
+
|
|
52
|
+
if available
|
|
53
|
+
@logger.info("SSM agent is available on instance #{instance_id}")
|
|
54
|
+
else
|
|
55
|
+
@logger.warn("SSM agent is not available on instance #{instance_id}")
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
available
|
|
59
|
+
rescue ::Aws::SSM::Errors::ServiceError => e
|
|
60
|
+
@logger.warn("Error checking SSM agent status: #{e.message}")
|
|
61
|
+
false
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Verify that the AWS CLI session manager plugin is installed
|
|
66
|
+
def session_manager_plugin_installed?
|
|
67
|
+
_output, status = Open3.capture2e("session-manager-plugin", "--version")
|
|
68
|
+
installed = status.success?
|
|
69
|
+
|
|
70
|
+
if installed
|
|
71
|
+
@logger.debug("Session Manager plugin is installed")
|
|
72
|
+
else
|
|
73
|
+
@logger.warn("Session Manager plugin is not installed. Install it from: " \
|
|
74
|
+
"https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html")
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
installed
|
|
78
|
+
rescue StandardError => e
|
|
79
|
+
@logger.warn("Error checking for session-manager-plugin: #{e.message}")
|
|
80
|
+
false
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -23,17 +23,15 @@ module Kitchen
|
|
|
23
23
|
class Debian < StandardPlatform
|
|
24
24
|
StandardPlatform.platforms["debian"] = self
|
|
25
25
|
|
|
26
|
-
# 12/13 are listed last since we default to the first item in the hash
|
|
27
|
-
# and 12/13 are not released yet. When they're released move them up
|
|
28
26
|
DEBIAN_CODENAMES = {
|
|
27
|
+
13 => "trixie",
|
|
28
|
+
12 => "bookworm",
|
|
29
29
|
11 => "bullseye",
|
|
30
30
|
10 => "buster",
|
|
31
31
|
9 => "stretch",
|
|
32
32
|
8 => "jessie",
|
|
33
33
|
7 => "wheezy",
|
|
34
34
|
6 => "squeeze",
|
|
35
|
-
12 => "bookworm",
|
|
36
|
-
13 => "trixie",
|
|
37
35
|
}.freeze
|
|
38
36
|
|
|
39
37
|
# default username for this platform's ami
|
data/lib/kitchen/driver/ec2.rb
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
# See the License for the specific language governing permissions and
|
|
17
17
|
# limitations under the License.
|
|
18
18
|
|
|
19
|
+
require "sshkey" unless defined?(SSHKey)
|
|
19
20
|
require "benchmark" unless defined?(Benchmark)
|
|
20
21
|
require "json" unless defined?(JSON)
|
|
21
22
|
require "kitchen"
|
|
@@ -37,12 +38,15 @@ require_relative "aws/standard_platform/freebsd"
|
|
|
37
38
|
require_relative "aws/standard_platform/macos"
|
|
38
39
|
require_relative "aws/standard_platform/ubuntu"
|
|
39
40
|
require_relative "aws/standard_platform/windows"
|
|
41
|
+
require_relative "aws/instance_connect"
|
|
42
|
+
require_relative "aws/ssm_session_manager"
|
|
40
43
|
require "aws-sdk-ec2"
|
|
41
44
|
require "aws-sdk-core/waiters/errors"
|
|
42
45
|
require "retryable" unless defined?(Retryable)
|
|
43
46
|
require "time" unless defined?(Time)
|
|
44
47
|
require "etc" unless defined?(Etc)
|
|
45
48
|
require "socket" unless defined?(Socket)
|
|
49
|
+
require "shellwords" unless defined?(Shellwords)
|
|
46
50
|
|
|
47
51
|
module Kitchen
|
|
48
52
|
module Driver
|
|
@@ -95,6 +99,11 @@ module Kitchen
|
|
|
95
99
|
default_config :skip_cost_warning, false
|
|
96
100
|
default_config :allocate_dedicated_host, false
|
|
97
101
|
default_config :deallocate_dedicated_host, false
|
|
102
|
+
default_config :use_instance_connect, false
|
|
103
|
+
default_config :instance_connect_endpoint_id, nil
|
|
104
|
+
default_config :instance_connect_max_tunnel_duration, 3600
|
|
105
|
+
default_config :use_ssm_session_manager, false
|
|
106
|
+
default_config :ssm_session_manager_document_name, nil
|
|
98
107
|
|
|
99
108
|
include Kitchen::Driver::Mixins::DedicatedHosts
|
|
100
109
|
|
|
@@ -191,6 +200,15 @@ module Kitchen
|
|
|
191
200
|
end
|
|
192
201
|
end
|
|
193
202
|
|
|
203
|
+
# Ensure use_instance_connect and use_ssm_session_manager are not both enabled
|
|
204
|
+
validations[:use_ssm_session_manager] = lambda do |_attr, val, driver|
|
|
205
|
+
if val && driver[:use_instance_connect]
|
|
206
|
+
warn "Cannot use both 'use_instance_connect' and 'use_ssm_session_manager' at the same time. " \
|
|
207
|
+
"Please enable only one transport method."
|
|
208
|
+
exit!
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
194
212
|
# empty keys cause failures when tagging and they make no sense
|
|
195
213
|
validations[:tags] = lambda do |_attr, val, _driver|
|
|
196
214
|
# if someone puts the tags each on their own line it's an array not a hash
|
|
@@ -226,7 +244,8 @@ module Kitchen
|
|
|
226
244
|
case config[:aws_ssh_key_id]
|
|
227
245
|
when nil
|
|
228
246
|
create_key(state)
|
|
229
|
-
|
|
247
|
+
# Don't set aws_ssh_key_id if using Instance Connect
|
|
248
|
+
config[:aws_ssh_key_id] = state[:auto_key_id] unless config[:use_instance_connect]
|
|
230
249
|
when "_disable"
|
|
231
250
|
info("Disabling AWS-managed SSH key pairs for this EC2 instance.")
|
|
232
251
|
info("The key pairs for the kitchen transport config and the AMI must match.")
|
|
@@ -265,13 +284,20 @@ module Kitchen
|
|
|
265
284
|
# Waiting can fail, so we have to retry on that.
|
|
266
285
|
Retryable.retryable(
|
|
267
286
|
tries: 10,
|
|
268
|
-
sleep:
|
|
287
|
+
sleep: ->(n) { [2**n, 30].min },
|
|
269
288
|
on: ::Aws::EC2::Errors::InvalidInstanceIDNotFound
|
|
270
289
|
) do |_r, _|
|
|
271
290
|
wait_until_ready(server, state)
|
|
272
291
|
end
|
|
273
292
|
|
|
274
293
|
info("EC2 instance <#{state[:server_id]}> ready (hostname: #{state[:hostname]}).")
|
|
294
|
+
|
|
295
|
+
if config[:use_instance_connect]
|
|
296
|
+
instance_connect_setup_ready(state)
|
|
297
|
+
elsif config[:use_ssm_session_manager]
|
|
298
|
+
ssm_session_manager_setup_ready(state)
|
|
299
|
+
end
|
|
300
|
+
|
|
275
301
|
instance.transport.connection(state).wait_until_ready
|
|
276
302
|
attach_network_interface(state) unless config[:elastic_network_interface_id].nil?
|
|
277
303
|
create_ec2_json(state) if /chef/i.match?(instance.provisioner.name)
|
|
@@ -444,10 +470,7 @@ module Kitchen
|
|
|
444
470
|
expanded = []
|
|
445
471
|
keys = %i{instance_type}
|
|
446
472
|
|
|
447
|
-
|
|
448
|
-
# => Use explicitly specified subnets
|
|
449
|
-
keys << :subnet_id
|
|
450
|
-
else
|
|
473
|
+
if config[:subnet_filter]
|
|
451
474
|
# => Enable cascading through matching subnets
|
|
452
475
|
client = ::Aws::EC2::Client.new(region: config[:region])
|
|
453
476
|
|
|
@@ -472,6 +495,9 @@ module Kitchen
|
|
|
472
495
|
new_config[:subnet_filter] = nil
|
|
473
496
|
new_config
|
|
474
497
|
end
|
|
498
|
+
else
|
|
499
|
+
# => Use explicitly specified subnets
|
|
500
|
+
keys << :subnet_id
|
|
475
501
|
end
|
|
476
502
|
|
|
477
503
|
keys.each do |key|
|
|
@@ -526,7 +552,7 @@ module Kitchen
|
|
|
526
552
|
# not retry if the price could not be satisfied immediately.
|
|
527
553
|
Retryable.retryable(
|
|
528
554
|
tries: config[:spot_wait] / config[:retryable_sleep],
|
|
529
|
-
sleep:
|
|
555
|
+
sleep: ->(_n) { config[:retryable_sleep] },
|
|
530
556
|
on: ::Aws::EC2::Errors::SpotMaxPriceTooLow
|
|
531
557
|
) do |retries|
|
|
532
558
|
c = retries * config[:retryable_sleep]
|
|
@@ -560,12 +586,12 @@ module Kitchen
|
|
|
560
586
|
# supplied, try to fetch it from the AWS instance
|
|
561
587
|
fetch_windows_admin_password(server, state)
|
|
562
588
|
else
|
|
563
|
-
output = server.console_output.output
|
|
589
|
+
output = server.console_output.output || ""
|
|
564
590
|
unless output.nil?
|
|
565
591
|
output = Base64.decode64(output)
|
|
566
592
|
debug "Console output: --- \n#{output}"
|
|
567
593
|
end
|
|
568
|
-
ready = !!
|
|
594
|
+
ready = !!output.include?("Windows is Ready to use")
|
|
569
595
|
end
|
|
570
596
|
end
|
|
571
597
|
ready
|
|
@@ -671,7 +697,7 @@ module Kitchen
|
|
|
671
697
|
|
|
672
698
|
def create_ec2_json(state)
|
|
673
699
|
if windows_os?
|
|
674
|
-
cmd =
|
|
700
|
+
cmd = 'New-Item -Force C:\\chef\\ohai\\hints\\ec2.json -ItemType File'
|
|
675
701
|
else
|
|
676
702
|
debug "Using sudo_command='#{sudo_command}' for ohai hints"
|
|
677
703
|
cmd = "#{sudo_command} mkdir -p /etc/chef/ohai/hints; #{sudo_command} touch /etc/chef/ohai/hints/ec2.json"
|
|
@@ -899,16 +925,16 @@ module Kitchen
|
|
|
899
925
|
client = ::Aws::EC2::Client.new(region: config[:region])
|
|
900
926
|
begin
|
|
901
927
|
check_eni = client.describe_network_interface_attribute({
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
928
|
+
attribute: "attachment",
|
|
929
|
+
network_interface_id: config[:elastic_network_interface_id],
|
|
930
|
+
})
|
|
905
931
|
if check_eni.attachment.nil?
|
|
906
932
|
unless state[:server_id].nil?
|
|
907
933
|
client.attach_network_interface({
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
934
|
+
device_index: 1,
|
|
935
|
+
instance_id: state[:server_id],
|
|
936
|
+
network_interface_id: config[:elastic_network_interface_id],
|
|
937
|
+
})
|
|
912
938
|
info("Attached Network interface <#{config[:elastic_network_interface_id]}> with the instance <#{state[:server_id]}> .")
|
|
913
939
|
end
|
|
914
940
|
else
|
|
@@ -945,6 +971,456 @@ module Kitchen
|
|
|
945
971
|
state.delete(:auto_key_id)
|
|
946
972
|
File.unlink("#{config[:kitchen_root]}/.kitchen/#{instance.name}.pem")
|
|
947
973
|
end
|
|
974
|
+
|
|
975
|
+
def finalize_config!(instance)
|
|
976
|
+
super
|
|
977
|
+
|
|
978
|
+
# Set up Instance Connect transport override if configured
|
|
979
|
+
if config[:use_instance_connect]
|
|
980
|
+
debug("[AWS EC2 Instance Connect] Setting up Instance Connect overrides")
|
|
981
|
+
instance_connect_setup_override(instance)
|
|
982
|
+
instance_connect_setup_inspec_override(instance)
|
|
983
|
+
elsif config[:use_ssm_session_manager]
|
|
984
|
+
debug("[AWS SSM Session Manager] Setting up SSM Session Manager overrides")
|
|
985
|
+
ssm_session_manager_setup_override(instance)
|
|
986
|
+
ssm_session_manager_setup_inspec_override(instance)
|
|
987
|
+
end
|
|
988
|
+
|
|
989
|
+
self
|
|
990
|
+
end
|
|
991
|
+
|
|
992
|
+
private
|
|
993
|
+
|
|
994
|
+
def instance_connect_setup_override(instance)
|
|
995
|
+
# Prevent double pushing of the SSH public keys
|
|
996
|
+
return if instance.transport.respond_to?(:instance_connect_override_applied)
|
|
997
|
+
|
|
998
|
+
# Store reference to driver for use in override
|
|
999
|
+
driver_instance = self
|
|
1000
|
+
use_instance_connect = config[:use_instance_connect]
|
|
1001
|
+
|
|
1002
|
+
# Override the transport's connection method to inject Instance Connect setup
|
|
1003
|
+
original_connection = instance.transport.method(:connection)
|
|
1004
|
+
|
|
1005
|
+
instance.transport.define_singleton_method(:connection) do |state, &block|
|
|
1006
|
+
# Set up Instance Connect configuration before every connection
|
|
1007
|
+
if use_instance_connect
|
|
1008
|
+
# Refresh Instance Connect SSH key
|
|
1009
|
+
driver_instance.send(:instance_connect_refresh_key, state)
|
|
1010
|
+
|
|
1011
|
+
# Configure connection mode based on endpoint availability
|
|
1012
|
+
if driver_instance.send(:instance_connect_endpoint_available?, state)
|
|
1013
|
+
# Proxy command mode - ensure ssh_proxy_command is set
|
|
1014
|
+
unless state[:ssh_proxy_command]
|
|
1015
|
+
driver_instance.send(:instance_connect_configure_ssh_proxy_command, state)
|
|
1016
|
+
end
|
|
1017
|
+
driver_instance.debug("[AWS EC2 Instance Connect] Transport using proxy command mode")
|
|
1018
|
+
else
|
|
1019
|
+
# Direct SSH mode - ensure hostname is set to public DNS
|
|
1020
|
+
driver_instance.send(:instance_connect_configure_direct_ssh, state)
|
|
1021
|
+
driver_instance.debug("[AWS EC2 Instance Connect] Transport using direct SSH mode")
|
|
1022
|
+
end
|
|
1023
|
+
end
|
|
1024
|
+
# Call original connection method
|
|
1025
|
+
original_connection.call(state, &block)
|
|
1026
|
+
end
|
|
1027
|
+
|
|
1028
|
+
# Mark as applied to prevent double pushing of the SSH public keys
|
|
1029
|
+
instance.transport.define_singleton_method(:instance_connect_override_applied) { true }
|
|
1030
|
+
end
|
|
1031
|
+
|
|
1032
|
+
def instance_connect_setup_inspec_override(instance)
|
|
1033
|
+
# Only apply to InSpec verifier
|
|
1034
|
+
return unless instance.verifier.name.downcase == "inspec"
|
|
1035
|
+
return if instance.verifier.respond_to?(:instance_connect_inspec_override_applied)
|
|
1036
|
+
|
|
1037
|
+
# Store reference to driver for use in override
|
|
1038
|
+
driver_instance = self
|
|
1039
|
+
use_instance_connect = config[:use_instance_connect]
|
|
1040
|
+
|
|
1041
|
+
# Override the verifier's call method to inject proxy command setup
|
|
1042
|
+
original_call = instance.verifier.method(:call)
|
|
1043
|
+
|
|
1044
|
+
instance.verifier.define_singleton_method(:call) do |state|
|
|
1045
|
+
driver_instance.debug("[AWS EC2 Instance Connect] InSpec call method intercepted, connecting using kitchen-ec2 driver AWS EC2 Instance Connect")
|
|
1046
|
+
driver_instance.debug("[AWS EC2 Instance Connect] Instance ID: #{state[:server_id]}")
|
|
1047
|
+
|
|
1048
|
+
# If using Instance Connect, set up the override just before the call
|
|
1049
|
+
if use_instance_connect && state[:server_id]
|
|
1050
|
+
|
|
1051
|
+
# Check if we already have the override method defined
|
|
1052
|
+
unless respond_to?(:instance_connect_original_runner_options_for_ssh)
|
|
1053
|
+
# Store the original method
|
|
1054
|
+
define_singleton_method(:instance_connect_original_runner_options_for_ssh, method(:runner_options_for_ssh))
|
|
1055
|
+
|
|
1056
|
+
# Override runner_options_for_ssh
|
|
1057
|
+
define_singleton_method(:runner_options_for_ssh) do |config_data|
|
|
1058
|
+
# Get the original options
|
|
1059
|
+
opts = instance_connect_original_runner_options_for_ssh(config_data)
|
|
1060
|
+
|
|
1061
|
+
# Inject Instance Connect configuration if enabled
|
|
1062
|
+
if use_instance_connect && config_data[:server_id]
|
|
1063
|
+
# Refresh Instance Connect SSH key
|
|
1064
|
+
driver_instance.send(:instance_connect_refresh_key, config_data)
|
|
1065
|
+
|
|
1066
|
+
# Check if we should use proxy command or direct SSH
|
|
1067
|
+
if driver_instance.send(:instance_connect_endpoint_available?, config_data)
|
|
1068
|
+
# Build proxy command with the instance ID from state
|
|
1069
|
+
proxy_command = [
|
|
1070
|
+
"aws", "ec2-instance-connect", "open-tunnel",
|
|
1071
|
+
"--instance-id", config_data[:server_id]
|
|
1072
|
+
]
|
|
1073
|
+
|
|
1074
|
+
# Add optional parameters
|
|
1075
|
+
if driver_instance.config[:instance_connect_endpoint_id]
|
|
1076
|
+
proxy_command += ["--instance-connect-endpoint-id", driver_instance.config[:instance_connect_endpoint_id]]
|
|
1077
|
+
end
|
|
1078
|
+
if driver_instance.config[:instance_connect_max_tunnel_duration]
|
|
1079
|
+
proxy_command += ["--max-tunnel-duration", driver_instance.config[:instance_connect_max_tunnel_duration].to_s]
|
|
1080
|
+
end
|
|
1081
|
+
if driver_instance.config[:shared_credentials_profile]
|
|
1082
|
+
proxy_command += ["--profile", driver_instance.config[:shared_credentials_profile]]
|
|
1083
|
+
end
|
|
1084
|
+
proxy_command += ["--region", driver_instance.config[:region]]
|
|
1085
|
+
|
|
1086
|
+
opts["proxy_command"] = proxy_command.join(" ")
|
|
1087
|
+
driver_instance.info("[AWS EC2 Instance Connect] InSpec using proxy command: #{opts["proxy_command"]}")
|
|
1088
|
+
else
|
|
1089
|
+
# Direct SSH mode - ensure we're using the public DNS and proper SSH options
|
|
1090
|
+
server = driver_instance.ec2.get_instance(config_data[:server_id])
|
|
1091
|
+
public_dns = server&.public_dns_name
|
|
1092
|
+
|
|
1093
|
+
if public_dns && !public_dns.empty?
|
|
1094
|
+
opts["host"] = public_dns
|
|
1095
|
+
opts["ssh_options"] = (opts["ssh_options"] || {}).merge({
|
|
1096
|
+
"IdentitiesOnly" => "yes",
|
|
1097
|
+
})
|
|
1098
|
+
driver_instance.info("[AWS EC2 Instance Connect] InSpec using direct SSH to #{public_dns} with IdentitiesOnly=yes")
|
|
1099
|
+
else
|
|
1100
|
+
driver_instance.warn("[AWS EC2 Instance Connect] No public DNS available for direct SSH mode")
|
|
1101
|
+
end
|
|
1102
|
+
end
|
|
1103
|
+
else
|
|
1104
|
+
driver_instance.info("[AWS EC2 Instance Connect] Not configuring Instance Connect - use_instance_connect: #{use_instance_connect}, server_id present: #{!!config_data[:server_id]}")
|
|
1105
|
+
end
|
|
1106
|
+
|
|
1107
|
+
opts
|
|
1108
|
+
end
|
|
1109
|
+
end
|
|
1110
|
+
end
|
|
1111
|
+
|
|
1112
|
+
# Call the original method
|
|
1113
|
+
original_call.call(state)
|
|
1114
|
+
end
|
|
1115
|
+
|
|
1116
|
+
# Mark as applied to prevent double setup
|
|
1117
|
+
instance.verifier.define_singleton_method(:instance_connect_inspec_override_applied) { true }
|
|
1118
|
+
end
|
|
1119
|
+
|
|
1120
|
+
def instance_connect_setup_ready(state)
|
|
1121
|
+
# Determine whether to use proxy command or direct SSH based on endpoint availability
|
|
1122
|
+
if instance_connect_endpoint_available?(state)
|
|
1123
|
+
# Configure SSH proxy command if not already done
|
|
1124
|
+
instance_connect_configure_ssh_proxy_command(state) unless state[:ssh_proxy_command]
|
|
1125
|
+
info("[AWS EC2 Instance Connect] Using tunnel mode - Instance Connect endpoint available")
|
|
1126
|
+
else
|
|
1127
|
+
# Configure direct SSH with public DNS
|
|
1128
|
+
instance_connect_configure_direct_ssh(state)
|
|
1129
|
+
info("[AWS EC2 Instance Connect] Using direct SSH mode - no Instance Connect endpoint")
|
|
1130
|
+
end
|
|
1131
|
+
|
|
1132
|
+
# Refresh Instance Connect SSH key before connection
|
|
1133
|
+
instance_connect_refresh_key(state)
|
|
1134
|
+
end
|
|
1135
|
+
|
|
1136
|
+
def instance_connect_refresh_key(state)
|
|
1137
|
+
# Extract public key from the key that was already set up
|
|
1138
|
+
key_path = state[:ssh_key] || instance.transport[:ssh_key]
|
|
1139
|
+
return unless key_path
|
|
1140
|
+
|
|
1141
|
+
public_key = instance_connect_extract_public_key(key_path)
|
|
1142
|
+
return unless public_key
|
|
1143
|
+
|
|
1144
|
+
username = state[:username] || actual_platform&.username
|
|
1145
|
+
|
|
1146
|
+
# Build AWS CLI command to send public key
|
|
1147
|
+
cmd = [
|
|
1148
|
+
"aws", "ec2-instance-connect", "send-ssh-public-key",
|
|
1149
|
+
"--instance-id", state[:server_id],
|
|
1150
|
+
"--instance-os-user", username,
|
|
1151
|
+
"--ssh-public-key", public_key,
|
|
1152
|
+
"--region", config[:region]
|
|
1153
|
+
]
|
|
1154
|
+
|
|
1155
|
+
cmd += ["--profile", config[:shared_credentials_profile]] if config[:shared_credentials_profile]
|
|
1156
|
+
|
|
1157
|
+
# Execute the command with proper shell escaping
|
|
1158
|
+
debug("[AWS EC2 Instance Connect] Refreshing SSH public key for #{state[:server_id]}")
|
|
1159
|
+
escaped_cmd = cmd.map { |arg| Shellwords.escape(arg) }.join(" ")
|
|
1160
|
+
result = `#{escaped_cmd} 2>&1`
|
|
1161
|
+
unless $?.success?
|
|
1162
|
+
warn("[AWS EC2 Instance Connect] Failed to refresh SSH key: #{result}")
|
|
1163
|
+
end
|
|
1164
|
+
end
|
|
1165
|
+
|
|
1166
|
+
def instance_connect_configure_ssh_proxy_command(state)
|
|
1167
|
+
info("[AWS EC2 Instance Connect] Configuring proxy command mode (tunnel)")
|
|
1168
|
+
|
|
1169
|
+
# Build the AWS CLI command for the tunnel
|
|
1170
|
+
proxy_command = [
|
|
1171
|
+
"aws", "ec2-instance-connect", "open-tunnel",
|
|
1172
|
+
"--instance-id", state[:server_id]
|
|
1173
|
+
]
|
|
1174
|
+
|
|
1175
|
+
# Add optional parameters
|
|
1176
|
+
if config[:instance_connect_endpoint_id]
|
|
1177
|
+
proxy_command += ["--instance-connect-endpoint-id", config[:instance_connect_endpoint_id]]
|
|
1178
|
+
end
|
|
1179
|
+
if config[:instance_connect_max_tunnel_duration]
|
|
1180
|
+
proxy_command += ["--max-tunnel-duration", config[:instance_connect_max_tunnel_duration].to_s]
|
|
1181
|
+
end
|
|
1182
|
+
if config[:shared_credentials_profile]
|
|
1183
|
+
proxy_command += ["--profile", config[:shared_credentials_profile]]
|
|
1184
|
+
end
|
|
1185
|
+
proxy_command += ["--region", config[:region]]
|
|
1186
|
+
proxy_command_str = proxy_command.join(" ")
|
|
1187
|
+
|
|
1188
|
+
info("Configuring SSH to use Instance Connect tunnel: #{proxy_command_str}")
|
|
1189
|
+
state[:ssh_proxy_command] = proxy_command_str
|
|
1190
|
+
|
|
1191
|
+
# Store Instance Connect details for the transport to use
|
|
1192
|
+
state[:instance_connect_config] = {
|
|
1193
|
+
server_id: state[:server_id],
|
|
1194
|
+
username: state[:username] || actual_platform&.username,
|
|
1195
|
+
region: config[:region],
|
|
1196
|
+
profile: config[:shared_credentials_profile],
|
|
1197
|
+
tunnel_mode: true,
|
|
1198
|
+
}
|
|
1199
|
+
end
|
|
1200
|
+
|
|
1201
|
+
def instance_connect_endpoint_available?(state)
|
|
1202
|
+
# If explicitly configured, respect that configuration
|
|
1203
|
+
return true if config[:instance_connect_endpoint_id]
|
|
1204
|
+
|
|
1205
|
+
# Check if there are any instance connect endpoints in the VPC
|
|
1206
|
+
vpc_id = get_vpc_id_for_instance(state)
|
|
1207
|
+
return false unless vpc_id
|
|
1208
|
+
|
|
1209
|
+
begin
|
|
1210
|
+
endpoints = ec2.client.describe_instance_connect_endpoints(
|
|
1211
|
+
filters: [
|
|
1212
|
+
{ name: "vpc-id", values: [vpc_id] },
|
|
1213
|
+
{ name: "state", values: ["create-complete"] },
|
|
1214
|
+
]
|
|
1215
|
+
).instance_connect_endpoints
|
|
1216
|
+
|
|
1217
|
+
!endpoints.empty?
|
|
1218
|
+
rescue ::Aws::EC2::Errors::InvalidAction, ::Aws::EC2::Errors::UnauthorizedOperation => e
|
|
1219
|
+
# Instance Connect endpoints may not be available in this region or account
|
|
1220
|
+
debug("[AWS EC2 Instance Connect] Cannot check for endpoints: #{e.message}")
|
|
1221
|
+
false
|
|
1222
|
+
end
|
|
1223
|
+
end
|
|
1224
|
+
|
|
1225
|
+
def get_vpc_id_for_instance(state)
|
|
1226
|
+
# Get the instance details to find its VPC
|
|
1227
|
+
return unless state[:server_id]
|
|
1228
|
+
|
|
1229
|
+
begin
|
|
1230
|
+
instance_info = ec2.client.describe_instances(instance_ids: [state[:server_id]]).reservations.first&.instances&.first
|
|
1231
|
+
return unless instance_info
|
|
1232
|
+
|
|
1233
|
+
instance_info.vpc_id
|
|
1234
|
+
rescue => e
|
|
1235
|
+
debug("[AWS EC2 Instance Connect] Error getting VPC ID for instance: #{e.message}")
|
|
1236
|
+
nil
|
|
1237
|
+
end
|
|
1238
|
+
end
|
|
1239
|
+
|
|
1240
|
+
def instance_connect_configure_direct_ssh(state)
|
|
1241
|
+
# For direct SSH, we need to ensure the hostname is the public DNS name
|
|
1242
|
+
# and configure SSH options appropriately
|
|
1243
|
+
server = ec2.get_instance(state[:server_id])
|
|
1244
|
+
public_dns = server.public_dns_name
|
|
1245
|
+
|
|
1246
|
+
if public_dns && !public_dns.empty?
|
|
1247
|
+
info("[AWS EC2 Instance Connect] Configuring direct SSH to #{public_dns}")
|
|
1248
|
+
state[:hostname] = public_dns
|
|
1249
|
+
|
|
1250
|
+
# Store Instance Connect details for direct SSH mode
|
|
1251
|
+
state[:instance_connect_config] = {
|
|
1252
|
+
server_id: state[:server_id],
|
|
1253
|
+
username: state[:username] || actual_platform&.username,
|
|
1254
|
+
region: config[:region],
|
|
1255
|
+
profile: config[:shared_credentials_profile],
|
|
1256
|
+
direct_ssh: true,
|
|
1257
|
+
hostname: public_dns,
|
|
1258
|
+
}
|
|
1259
|
+
else
|
|
1260
|
+
warn("[AWS EC2 Instance Connect] No public DNS available for direct SSH, falling back to existing hostname")
|
|
1261
|
+
end
|
|
1262
|
+
end
|
|
1263
|
+
|
|
1264
|
+
def instance_connect_extract_public_key(private_key_path)
|
|
1265
|
+
public_key_path = "#{private_key_path}.pub"
|
|
1266
|
+
|
|
1267
|
+
if File.exist?(public_key_path)
|
|
1268
|
+
return File.read(public_key_path).strip
|
|
1269
|
+
end
|
|
1270
|
+
|
|
1271
|
+
begin
|
|
1272
|
+
key = SSHKey.new(File.read(private_key_path))
|
|
1273
|
+
key.ssh_public_key
|
|
1274
|
+
rescue => e
|
|
1275
|
+
raise "Unable to extract public key from #{private_key_path}: #{e.message}"
|
|
1276
|
+
end
|
|
1277
|
+
end
|
|
1278
|
+
|
|
1279
|
+
# SSM Session Manager Support Methods
|
|
1280
|
+
|
|
1281
|
+
def ssm_session_manager
|
|
1282
|
+
@ssm_session_manager ||= Aws::SsmSessionManager.new(config, instance.logger)
|
|
1283
|
+
end
|
|
1284
|
+
|
|
1285
|
+
def ssm_session_manager_setup_ready(state)
|
|
1286
|
+
info("[AWS SSM Session Manager] Setting up SSM Session Manager connection")
|
|
1287
|
+
|
|
1288
|
+
# Verify session manager plugin is installed
|
|
1289
|
+
unless ssm_session_manager.session_manager_plugin_installed?
|
|
1290
|
+
warn("[AWS SSM Session Manager] Session Manager plugin not found. Please install it from: " \
|
|
1291
|
+
"https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html")
|
|
1292
|
+
end
|
|
1293
|
+
|
|
1294
|
+
# Wait for SSM agent to be available
|
|
1295
|
+
max_retries = 12
|
|
1296
|
+
retry_delay = 10
|
|
1297
|
+
retries = 0
|
|
1298
|
+
|
|
1299
|
+
loop do
|
|
1300
|
+
if ssm_session_manager.ssm_agent_available?(state[:server_id])
|
|
1301
|
+
info("[AWS SSM Session Manager] SSM agent is available on instance #{state[:server_id]}")
|
|
1302
|
+
break
|
|
1303
|
+
end
|
|
1304
|
+
|
|
1305
|
+
retries += 1
|
|
1306
|
+
if retries >= max_retries
|
|
1307
|
+
warn("[AWS SSM Session Manager] SSM agent did not become available after #{max_retries * retry_delay} seconds. " \
|
|
1308
|
+
"Ensure the instance has an IAM instance profile with SSM permissions and the SSM agent is running.")
|
|
1309
|
+
break
|
|
1310
|
+
end
|
|
1311
|
+
|
|
1312
|
+
info("[AWS SSM Session Manager] Waiting for SSM agent to be available (attempt #{retries}/#{max_retries})...")
|
|
1313
|
+
sleep retry_delay
|
|
1314
|
+
end
|
|
1315
|
+
end
|
|
1316
|
+
|
|
1317
|
+
def ssm_session_manager_setup_override(instance)
|
|
1318
|
+
# Prevent double setup
|
|
1319
|
+
return if instance.transport.respond_to?(:ssm_session_manager_override_applied)
|
|
1320
|
+
|
|
1321
|
+
# Store reference to driver for use in override
|
|
1322
|
+
driver_instance = self
|
|
1323
|
+
use_ssm = config[:use_ssm_session_manager]
|
|
1324
|
+
|
|
1325
|
+
# Override the transport's connection method to inject SSM setup
|
|
1326
|
+
original_connection = instance.transport.method(:connection)
|
|
1327
|
+
|
|
1328
|
+
instance.transport.define_singleton_method(:connection) do |state, &block|
|
|
1329
|
+
if use_ssm && state[:server_id]
|
|
1330
|
+
# Build SSM start-session command
|
|
1331
|
+
cmd = [
|
|
1332
|
+
"aws", "ssm", "start-session",
|
|
1333
|
+
"--target", state[:server_id],
|
|
1334
|
+
"--region", driver_instance.config[:region]
|
|
1335
|
+
]
|
|
1336
|
+
|
|
1337
|
+
# Add document name if specified
|
|
1338
|
+
if driver_instance.config[:ssm_session_manager_document_name]
|
|
1339
|
+
cmd += ["--document-name", driver_instance.config[:ssm_session_manager_document_name]]
|
|
1340
|
+
end
|
|
1341
|
+
|
|
1342
|
+
# Add AWS profile if specified
|
|
1343
|
+
if driver_instance.config[:shared_credentials_profile]
|
|
1344
|
+
cmd += ["--profile", driver_instance.config[:shared_credentials_profile]]
|
|
1345
|
+
end
|
|
1346
|
+
|
|
1347
|
+
proxy_command = cmd.join(" ")
|
|
1348
|
+
driver_instance.info("[AWS SSM Session Manager] Using proxy command: #{proxy_command}")
|
|
1349
|
+
|
|
1350
|
+
# Set proxy command for SSH transport
|
|
1351
|
+
state[:ssh_proxy_command] = proxy_command
|
|
1352
|
+
end
|
|
1353
|
+
|
|
1354
|
+
# Call original connection method
|
|
1355
|
+
original_connection.call(state, &block)
|
|
1356
|
+
end
|
|
1357
|
+
|
|
1358
|
+
# Mark as applied
|
|
1359
|
+
instance.transport.define_singleton_method(:ssm_session_manager_override_applied) { true }
|
|
1360
|
+
end
|
|
1361
|
+
|
|
1362
|
+
def ssm_session_manager_setup_inspec_override(instance)
|
|
1363
|
+
# Only apply to InSpec verifier
|
|
1364
|
+
return unless instance.verifier.name.downcase == "inspec"
|
|
1365
|
+
return if instance.verifier.respond_to?(:ssm_session_manager_inspec_override_applied)
|
|
1366
|
+
|
|
1367
|
+
# Store reference to driver for use in override
|
|
1368
|
+
driver_instance = self
|
|
1369
|
+
use_ssm = config[:use_ssm_session_manager]
|
|
1370
|
+
|
|
1371
|
+
# Override the verifier's call method to inject SSM setup
|
|
1372
|
+
original_call = instance.verifier.method(:call)
|
|
1373
|
+
|
|
1374
|
+
instance.verifier.define_singleton_method(:call) do |state|
|
|
1375
|
+
driver_instance.debug("[AWS SSM Session Manager] InSpec call method intercepted")
|
|
1376
|
+
|
|
1377
|
+
# Set up SSM for InSpec if enabled
|
|
1378
|
+
if use_ssm && state[:server_id]
|
|
1379
|
+
# Check if we already have the override method defined
|
|
1380
|
+
unless respond_to?(:ssm_original_runner_options_for_ssh)
|
|
1381
|
+
# Store the original method
|
|
1382
|
+
define_singleton_method(:ssm_original_runner_options_for_ssh, method(:runner_options_for_ssh))
|
|
1383
|
+
|
|
1384
|
+
# Override runner_options_for_ssh
|
|
1385
|
+
define_singleton_method(:runner_options_for_ssh) do |config_data|
|
|
1386
|
+
# Get the original options
|
|
1387
|
+
opts = ssm_original_runner_options_for_ssh(config_data)
|
|
1388
|
+
|
|
1389
|
+
# Inject SSM Session Manager configuration if enabled
|
|
1390
|
+
if use_ssm && config_data[:server_id]
|
|
1391
|
+
# Build SSM start-session command
|
|
1392
|
+
cmd = [
|
|
1393
|
+
"aws", "ssm", "start-session",
|
|
1394
|
+
"--target", config_data[:server_id],
|
|
1395
|
+
"--region", driver_instance.config[:region]
|
|
1396
|
+
]
|
|
1397
|
+
|
|
1398
|
+
# Add document name if specified
|
|
1399
|
+
if driver_instance.config[:ssm_session_manager_document_name]
|
|
1400
|
+
cmd += ["--document-name", driver_instance.config[:ssm_session_manager_document_name]]
|
|
1401
|
+
end
|
|
1402
|
+
|
|
1403
|
+
# Add AWS profile if specified
|
|
1404
|
+
if driver_instance.config[:shared_credentials_profile]
|
|
1405
|
+
cmd += ["--profile", driver_instance.config[:shared_credentials_profile]]
|
|
1406
|
+
end
|
|
1407
|
+
|
|
1408
|
+
opts["proxy_command"] = cmd.join(" ")
|
|
1409
|
+
driver_instance.info("[AWS SSM Session Manager] InSpec using proxy command: #{opts["proxy_command"]}")
|
|
1410
|
+
end
|
|
1411
|
+
|
|
1412
|
+
opts
|
|
1413
|
+
end
|
|
1414
|
+
end
|
|
1415
|
+
end
|
|
1416
|
+
|
|
1417
|
+
# Call the original method
|
|
1418
|
+
original_call.call(state)
|
|
1419
|
+
end
|
|
1420
|
+
|
|
1421
|
+
# Mark as applied
|
|
1422
|
+
instance.verifier.define_singleton_method(:ssm_session_manager_inspec_override_applied) { true }
|
|
1423
|
+
end
|
|
948
1424
|
end
|
|
949
1425
|
end
|
|
950
1426
|
end
|
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: 3.
|
|
4
|
+
version: 3.22.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Test Kitchen Team
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-01-22 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: aws-sdk-ec2
|
|
@@ -24,6 +24,34 @@ dependencies:
|
|
|
24
24
|
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
26
|
version: '1.0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: aws-sdk-ec2instanceconnect
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '1.0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '1.0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: aws-sdk-ssm
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '1.0'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '1.0'
|
|
27
55
|
- !ruby/object:Gem::Dependency
|
|
28
56
|
name: retryable
|
|
29
57
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -44,13 +72,27 @@ dependencies:
|
|
|
44
72
|
- - "<"
|
|
45
73
|
- !ruby/object:Gem::Version
|
|
46
74
|
version: '4.0'
|
|
75
|
+
- !ruby/object:Gem::Dependency
|
|
76
|
+
name: sshkey
|
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - "~>"
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '2.0'
|
|
82
|
+
type: :runtime
|
|
83
|
+
prerelease: false
|
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - "~>"
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '2.0'
|
|
47
89
|
- !ruby/object:Gem::Dependency
|
|
48
90
|
name: test-kitchen
|
|
49
91
|
requirement: !ruby/object:Gem::Requirement
|
|
50
92
|
requirements:
|
|
51
93
|
- - ">="
|
|
52
94
|
- !ruby/object:Gem::Version
|
|
53
|
-
version:
|
|
95
|
+
version: 3.9.0
|
|
54
96
|
- - "<"
|
|
55
97
|
- !ruby/object:Gem::Version
|
|
56
98
|
version: '4'
|
|
@@ -60,7 +102,7 @@ dependencies:
|
|
|
60
102
|
requirements:
|
|
61
103
|
- - ">="
|
|
62
104
|
- !ruby/object:Gem::Version
|
|
63
|
-
version:
|
|
105
|
+
version: 3.9.0
|
|
64
106
|
- - "<"
|
|
65
107
|
- !ruby/object:Gem::Version
|
|
66
108
|
version: '4'
|
|
@@ -74,7 +116,9 @@ files:
|
|
|
74
116
|
- LICENSE
|
|
75
117
|
- lib/kitchen/driver/aws/client.rb
|
|
76
118
|
- lib/kitchen/driver/aws/dedicated_hosts.rb
|
|
119
|
+
- lib/kitchen/driver/aws/instance_connect.rb
|
|
77
120
|
- lib/kitchen/driver/aws/instance_generator.rb
|
|
121
|
+
- lib/kitchen/driver/aws/ssm_session_manager.rb
|
|
78
122
|
- lib/kitchen/driver/aws/standard_platform.rb
|
|
79
123
|
- lib/kitchen/driver/aws/standard_platform/alma.rb
|
|
80
124
|
- lib/kitchen/driver/aws/standard_platform/amazon.rb
|