test-kitchen 2.7.0 → 2.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/kitchen.rb +1 -3
- data/lib/kitchen/cli.rb +2 -2
- data/lib/kitchen/command.rb +0 -2
- data/lib/kitchen/command/diagnose.rb +3 -3
- data/lib/kitchen/config.rb +8 -5
- data/lib/kitchen/lifecycle_hook/base.rb +78 -0
- data/lib/kitchen/lifecycle_hook/local.rb +53 -0
- data/lib/kitchen/lifecycle_hook/remote.rb +33 -0
- data/lib/kitchen/lifecycle_hooks.rb +26 -78
- data/lib/kitchen/loader/yaml.rb +1 -0
- data/lib/kitchen/provisioner/base.rb +5 -0
- data/lib/kitchen/provisioner/chef/policyfile.rb +4 -4
- data/lib/kitchen/provisioner/chef_base.rb +6 -4
- data/lib/kitchen/provisioner/chef_solo.rb +4 -0
- data/lib/kitchen/ssh.rb +5 -2
- data/lib/kitchen/state_file.rb +1 -1
- data/lib/kitchen/transport/exec.rb +90 -4
- data/lib/kitchen/transport/winrm.rb +10 -6
- data/lib/kitchen/util.rb +11 -1
- data/lib/kitchen/verifier/busser.rb +1 -1
- data/lib/kitchen/version.rb +1 -1
- data/test-kitchen.gemspec +1 -1
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 51ed51940914e0c4ffb30d1e11be2e3554289833261e0b418eaa2ebdb674cab9
|
4
|
+
data.tar.gz: c3c9be7593e1c0c333102168228c84e217244c5071da0bfb2ba543edca7581ba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 59633a6df2c274542ee3d4c649c55f8632077a98476ee655a1f59c0b39d42fad9077294b0e615d8e7f697feef4cbb027a9df7be81708832760d45eee72fbf159
|
7
|
+
data.tar.gz: 34164cc7046bb2aa507fa33373c928d94be84284a99ba629562c585614c1818d032a42a1b2b83ab0b95382e984841be63a2dc32a05412dbf8d60a15839325146
|
data/lib/kitchen.rb
CHANGED
@@ -16,8 +16,6 @@
|
|
16
16
|
# limitations under the License.
|
17
17
|
|
18
18
|
require "pathname" unless defined?(Pathname)
|
19
|
-
require "thread"
|
20
|
-
|
21
19
|
require_relative "kitchen/errors"
|
22
20
|
require_relative "kitchen/logger"
|
23
21
|
require_relative "kitchen/logging"
|
@@ -67,7 +65,7 @@ module Kitchen
|
|
67
65
|
#
|
68
66
|
# @return [Pathname] root path of gem
|
69
67
|
def source_root
|
70
|
-
@source_root ||= Pathname.new(File.expand_path("
|
68
|
+
@source_root ||= Pathname.new(File.expand_path("..", __dir__))
|
71
69
|
end
|
72
70
|
|
73
71
|
# Returns a default logger which emits on standard output.
|
data/lib/kitchen/cli.rb
CHANGED
@@ -16,7 +16,7 @@
|
|
16
16
|
# limitations under the License.
|
17
17
|
|
18
18
|
# CI tests fail without an explicit unconditional require of Thor
|
19
|
-
require "thor" # rubocop:disable
|
19
|
+
require "thor" # rubocop:disable Chef/Ruby/UnlessDefinedRequire
|
20
20
|
|
21
21
|
require_relative "../kitchen"
|
22
22
|
require_relative "generator/init"
|
@@ -162,7 +162,7 @@ module Kitchen
|
|
162
162
|
long_desc <<-DESC
|
163
163
|
The instance states are in order: destroy, create, converge, setup, verify, destroy.
|
164
164
|
Change one or more instances from the current state to the #{action} state. Actions for all
|
165
|
-
intermediate states will be executed. See
|
165
|
+
intermediate states will be executed. See https://kitchen.ci/ for further explanation.
|
166
166
|
DESC
|
167
167
|
method_option :concurrency,
|
168
168
|
aliases: "-c",
|
data/lib/kitchen/command.rb
CHANGED
@@ -18,7 +18,7 @@
|
|
18
18
|
require_relative "../command"
|
19
19
|
require_relative "../diagnostic"
|
20
20
|
|
21
|
-
|
21
|
+
autoload :YAML, "yaml"
|
22
22
|
|
23
23
|
module Kitchen
|
24
24
|
module Command
|
@@ -32,9 +32,9 @@ module Kitchen
|
|
32
32
|
|
33
33
|
loader = record_failure { load_loader }
|
34
34
|
|
35
|
-
puts Kitchen::Diagnostic.new(
|
35
|
+
puts YAML.dump(Kitchen::Diagnostic.new(
|
36
36
|
loader: loader, instances: instances, plugins: plugins?
|
37
|
-
).read
|
37
|
+
).read)
|
38
38
|
end
|
39
39
|
|
40
40
|
private
|
data/lib/kitchen/config.rb
CHANGED
@@ -213,7 +213,7 @@ module Kitchen
|
|
213
213
|
provisioner: Provisioner::DEFAULT_PLUGIN,
|
214
214
|
verifier: Verifier::DEFAULT_PLUGIN,
|
215
215
|
transport: lambda do |_suite, platform|
|
216
|
-
|
216
|
+
/^win/i.match?(platform) ? "winrm" : Transport::DEFAULT_PLUGIN
|
217
217
|
end,
|
218
218
|
},
|
219
219
|
kitchen_root: kitchen_root,
|
@@ -244,16 +244,18 @@ module Kitchen
|
|
244
244
|
# @return [Instance] a new Instance object
|
245
245
|
# @api private
|
246
246
|
def new_instance(suite, platform, index)
|
247
|
+
sf = new_state_file(suite, platform)
|
248
|
+
|
247
249
|
Instance.new(
|
248
250
|
driver: new_driver(suite, platform),
|
249
|
-
lifecycle_hooks: new_lifecycle_hooks(suite, platform),
|
251
|
+
lifecycle_hooks: new_lifecycle_hooks(suite, platform, sf),
|
250
252
|
logger: new_instance_logger(suite, platform, index),
|
251
253
|
suite: suite,
|
252
254
|
platform: platform,
|
253
255
|
provisioner: new_provisioner(suite, platform),
|
254
256
|
transport: new_transport(suite, platform),
|
255
257
|
verifier: new_verifier(suite, platform),
|
256
|
-
state_file:
|
258
|
+
state_file: sf
|
257
259
|
)
|
258
260
|
end
|
259
261
|
|
@@ -284,11 +286,12 @@ module Kitchen
|
|
284
286
|
#
|
285
287
|
# @param suite [Suite,#name] a Suite
|
286
288
|
# @param platform [Platform,#name] a Platform
|
289
|
+
# @param state_file [Kitchen::StateFile] a SateFile
|
287
290
|
# @return [LifecycleHooks] a new LifecycleHooks object
|
288
291
|
# @api private
|
289
|
-
def new_lifecycle_hooks(suite, platform)
|
292
|
+
def new_lifecycle_hooks(suite, platform, state_file)
|
290
293
|
lhdata = data.lifecycle_hooks_data_for(suite.name, platform.name)
|
291
|
-
LifecycleHooks.new(lhdata)
|
294
|
+
LifecycleHooks.new(lhdata, state_file)
|
292
295
|
end
|
293
296
|
|
294
297
|
# Builds a newly configured Provisioner object, for a given Suite and
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Kitchen
|
2
|
+
class LifecycleHook
|
3
|
+
class Base
|
4
|
+
# @return [Kitchen::LifecycleHooks]
|
5
|
+
attr_reader :lifecycle_hooks
|
6
|
+
|
7
|
+
# return [String]
|
8
|
+
attr_reader :phase
|
9
|
+
|
10
|
+
# return [Hash]
|
11
|
+
attr_reader :hook
|
12
|
+
|
13
|
+
# @param lifecycle_hooks [Kitchen::LifecycleHooks]
|
14
|
+
# @param phase [String]
|
15
|
+
# @param hook [Hash]
|
16
|
+
def initialize(lifecycle_hooks, phase, hook)
|
17
|
+
@lifecycle_hooks = lifecycle_hooks
|
18
|
+
@phase = phase
|
19
|
+
@hook = hook
|
20
|
+
end
|
21
|
+
|
22
|
+
# return [void]
|
23
|
+
def run
|
24
|
+
raise NotImplementedError
|
25
|
+
end
|
26
|
+
|
27
|
+
# @return [TrueClass, FalseClass]
|
28
|
+
def should_run?
|
29
|
+
if !includes.empty?
|
30
|
+
includes.include?(platform_name)
|
31
|
+
elsif !excludes.empty?
|
32
|
+
!excludes.include?(platform_name)
|
33
|
+
else
|
34
|
+
true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# @return [Logger] the lifecycle hooks's logger
|
39
|
+
# otherwise
|
40
|
+
# @api private
|
41
|
+
def logger
|
42
|
+
lifecycle_hooks.send(:logger)
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
# @return [Kitchen::Instance]
|
48
|
+
def instance
|
49
|
+
lifecycle_hooks.instance
|
50
|
+
end
|
51
|
+
|
52
|
+
# @return [Hash]
|
53
|
+
def config
|
54
|
+
lifecycle_hooks.send(:config)
|
55
|
+
end
|
56
|
+
|
57
|
+
# @return [Kitchen::StateFile]
|
58
|
+
def state_file
|
59
|
+
lifecycle_hooks.state_file
|
60
|
+
end
|
61
|
+
|
62
|
+
# @return [Array<String>] names of excluded platforms
|
63
|
+
def excludes
|
64
|
+
@excludes ||= hook.fetch(:excludes, [])
|
65
|
+
end
|
66
|
+
|
67
|
+
# @return [Array<String>] names of only included platforms
|
68
|
+
def includes
|
69
|
+
@includes ||= hook.fetch(:includes, [])
|
70
|
+
end
|
71
|
+
|
72
|
+
# @return [String]
|
73
|
+
def platform_name
|
74
|
+
instance.platform.name
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require_relative "base"
|
2
|
+
require_relative "../shell_out"
|
3
|
+
require_relative "../logging"
|
4
|
+
|
5
|
+
module Kitchen
|
6
|
+
class LifecycleHook
|
7
|
+
class Local < Base
|
8
|
+
include ShellOut
|
9
|
+
include Logging
|
10
|
+
|
11
|
+
# Execute a specific local command hook.
|
12
|
+
#
|
13
|
+
# @return [void]
|
14
|
+
def run
|
15
|
+
state = state_file.read
|
16
|
+
# set up empty user variable
|
17
|
+
user = {}
|
18
|
+
# Set up some environment variables with instance info.
|
19
|
+
environment = {
|
20
|
+
"KITCHEN_INSTANCE_NAME" => instance.name,
|
21
|
+
"KITCHEN_SUITE_NAME" => instance.suite.name,
|
22
|
+
"KITCHEN_PLATFORM_NAME" => instance.platform.name,
|
23
|
+
"KITCHEN_INSTANCE_HOSTNAME" => state[:hostname].to_s,
|
24
|
+
}
|
25
|
+
# If the user specified env vars too, fix them up because symbol keys
|
26
|
+
# make mixlib-shellout sad.
|
27
|
+
hook[:environment]&.each do |k, v|
|
28
|
+
environment[k.to_s] = v.to_s
|
29
|
+
end
|
30
|
+
|
31
|
+
# add user to user hash for later merging
|
32
|
+
user[:user] = hook[:user] if hook[:user]
|
33
|
+
|
34
|
+
# Default the cwd to the kitchen root and resolve a relative input cwd against that.
|
35
|
+
cwd = if hook[:cwd]
|
36
|
+
File.expand_path(hook[:cwd], config[:kitchen_root])
|
37
|
+
else
|
38
|
+
config[:kitchen_root]
|
39
|
+
end
|
40
|
+
# Build the options for mixlib-shellout.
|
41
|
+
opts = {}.merge(user).merge(cwd: cwd, environment: environment)
|
42
|
+
run_command(command, opts)
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
# @return [String]
|
48
|
+
def command
|
49
|
+
hook.fetch(:local)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require_relative "base"
|
2
|
+
require_relative "../errors"
|
3
|
+
|
4
|
+
module Kitchen
|
5
|
+
class LifecycleHook
|
6
|
+
class Remote < Base
|
7
|
+
# Execute a specific remote command hook.
|
8
|
+
#
|
9
|
+
# @return [void]
|
10
|
+
def run
|
11
|
+
# Check if we're in a state that makes sense to even try.
|
12
|
+
unless instance.last_action
|
13
|
+
if hook[:skippable]
|
14
|
+
# Just not even trying.
|
15
|
+
return
|
16
|
+
else
|
17
|
+
raise UserError, "Cannot use remote lifecycle hooks during phases when the instance is not available"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
conn = instance.transport.connection(state_file.read)
|
22
|
+
conn.execute(command)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
# return [String]
|
28
|
+
def command
|
29
|
+
hook.fetch(:remote)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -15,8 +15,11 @@
|
|
15
15
|
# See the License for the specific language governing permissions and
|
16
16
|
# limitations under the License.
|
17
17
|
|
18
|
+
require_relative "configurable"
|
18
19
|
require_relative "errors"
|
19
|
-
require_relative "
|
20
|
+
require_relative "lifecycle_hook/local"
|
21
|
+
require_relative "lifecycle_hook/remote"
|
22
|
+
require_relative "logging"
|
20
23
|
|
21
24
|
module Kitchen
|
22
25
|
# A helper object used by {Instance} to coordinate lifecycle hook calls from
|
@@ -27,10 +30,10 @@ module Kitchen
|
|
27
30
|
class LifecycleHooks
|
28
31
|
include Configurable
|
29
32
|
include Logging
|
30
|
-
include ShellOut
|
31
33
|
|
32
|
-
def initialize(config)
|
34
|
+
def initialize(config, state_file)
|
33
35
|
init_config(config)
|
36
|
+
@state_file = state_file
|
34
37
|
end
|
35
38
|
|
36
39
|
# Run a lifecycle phase with the pre and post hooks.
|
@@ -40,21 +43,22 @@ module Kitchen
|
|
40
43
|
# @param block [Proc] Block of code implementing the lifecycle phase.
|
41
44
|
# @return [void]
|
42
45
|
def run_with_hooks(phase, state_file, &block)
|
43
|
-
run(
|
46
|
+
run(phase, :pre)
|
44
47
|
yield
|
45
|
-
run(
|
48
|
+
run(phase, :post)
|
46
49
|
end
|
47
50
|
|
51
|
+
# @return [Kitchen::StateFile]
|
52
|
+
attr_reader :state_file
|
53
|
+
|
48
54
|
private
|
49
55
|
|
50
56
|
# Execute a specific lifecycle hook.
|
51
57
|
#
|
52
|
-
# @param instance [Instance] The instance object to run against.
|
53
58
|
# @param phase [String] Lifecycle phase which is being executed.
|
54
|
-
# @param state_file [StateFile] Instance state file object.
|
55
59
|
# @param hook_timing [Symbol] `:pre` or `:post` to indicate which hook to run.
|
56
60
|
# @return [void]
|
57
|
-
def run(
|
61
|
+
def run(phase, hook_timing)
|
58
62
|
# Yes this has to be a symbol because of how data munger works.
|
59
63
|
hook_key = :"#{hook_timing}_#{phase}"
|
60
64
|
# No hooks? We're outta here.
|
@@ -65,80 +69,24 @@ module Kitchen
|
|
65
69
|
# Coerce the common case of a bare string to be a local command. This
|
66
70
|
# is to match the behavior of the old `pre_create_command` semi-hook.
|
67
71
|
hook = { local: hook } if hook.is_a?(String)
|
68
|
-
|
69
|
-
|
70
|
-
run_local_hook(instance, state_file, hook)
|
71
|
-
elsif hook.include?(:remote)
|
72
|
-
# Remote command execution on the test instance.
|
73
|
-
run_remote_hook(instance, state_file, hook)
|
74
|
-
else
|
75
|
-
raise UserError, "Unknown lifecycle hook target #{hook.inspect}"
|
76
|
-
end
|
72
|
+
hook = generate_hook(phase, hook)
|
73
|
+
hook.run if hook.should_run?
|
77
74
|
end
|
78
75
|
end
|
79
76
|
|
80
|
-
#
|
81
|
-
#
|
82
|
-
# @
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
environment = {
|
93
|
-
"KITCHEN_INSTANCE_NAME" => instance.name,
|
94
|
-
"KITCHEN_SUITE_NAME" => instance.suite.name,
|
95
|
-
"KITCHEN_PLATFORM_NAME" => instance.platform.name,
|
96
|
-
"KITCHEN_INSTANCE_HOSTNAME" => state[:hostname].to_s,
|
97
|
-
}
|
98
|
-
# If the user specified env vars too, fix them up because symbol keys
|
99
|
-
# make mixlib-shellout sad.
|
100
|
-
if hook[:environment]
|
101
|
-
hook[:environment].each do |k, v|
|
102
|
-
environment[k.to_s] = v.to_s
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
# add user to user hash for later merging
|
107
|
-
if hook[:user]
|
108
|
-
user[:user] = hook[:user]
|
77
|
+
# @param phase [String]
|
78
|
+
# @param hook [Hash]
|
79
|
+
# @return [Kitchen::LifecycleHook::Local, Kitchen::LifecycleHook::Remote]
|
80
|
+
def generate_hook(phase, hook)
|
81
|
+
if hook.include?(:local)
|
82
|
+
# Local command execution on the workstation.
|
83
|
+
Kitchen::LifecycleHook::Local.new(self, phase, hook)
|
84
|
+
elsif hook.include?(:remote)
|
85
|
+
# Remote command execution on the test instance.
|
86
|
+
Kitchen::LifecycleHook::Remote.new(self, phase, hook)
|
87
|
+
else
|
88
|
+
raise UserError, "Unknown lifecycle hook target #{hook.inspect}"
|
109
89
|
end
|
110
|
-
|
111
|
-
# Default the cwd to the kitchen root and resolve a relative input cwd against that.
|
112
|
-
cwd = if hook[:cwd]
|
113
|
-
File.expand_path(hook[:cwd], config[:kitchen_root])
|
114
|
-
else
|
115
|
-
config[:kitchen_root]
|
116
|
-
end
|
117
|
-
# Build the options for mixlib-shellout.
|
118
|
-
opts = {}.merge(user).merge(cwd: cwd, environment: environment)
|
119
|
-
run_command(cmd, opts)
|
120
|
-
end
|
121
|
-
|
122
|
-
# Execute a specific remote command hook.
|
123
|
-
#
|
124
|
-
# @param instance [Instance] The instance object to run against.
|
125
|
-
# @param state_file [StateFile] Instance state file object.
|
126
|
-
# @param hook [Hash] Hook configration to use.
|
127
|
-
# @return [void]
|
128
|
-
def run_remote_hook(instance, state_file, hook)
|
129
|
-
# Check if we're in a state that makes sense to even try.
|
130
|
-
unless instance.last_action
|
131
|
-
if hook[:skippable]
|
132
|
-
# Just not even trying.
|
133
|
-
return
|
134
|
-
else
|
135
|
-
raise UserError, "Cannot use remote lifecycle hooks during phases when the instance is not available"
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
cmd = hook.fetch(:remote)
|
140
|
-
conn = instance.transport.connection(state_file.read)
|
141
|
-
conn.execute(cmd)
|
142
90
|
end
|
143
91
|
end
|
144
92
|
end
|
data/lib/kitchen/loader/yaml.rb
CHANGED
@@ -51,6 +51,7 @@ module Kitchen
|
|
51
51
|
|
52
52
|
default_config :command_prefix, nil
|
53
53
|
|
54
|
+
default_config :uploads, {}
|
54
55
|
default_config :downloads, {}
|
55
56
|
|
56
57
|
expand_path_for :test_base_path
|
@@ -72,6 +73,10 @@ module Kitchen
|
|
72
73
|
sandbox_dirs = Util.list_directory(sandbox_path)
|
73
74
|
|
74
75
|
instance.transport.connection(state) do |conn|
|
76
|
+
config[:uploads].to_h.each do |locals, remote|
|
77
|
+
debug("Uploading #{Array(locals).join(', ')} to #{remote}")
|
78
|
+
conn.upload(locals.to_s, remote)
|
79
|
+
end
|
75
80
|
conn.execute(install_command)
|
76
81
|
conn.execute(init_command)
|
77
82
|
info("Transferring files to #{instance.to_str}")
|
@@ -110,13 +110,13 @@ module Kitchen
|
|
110
110
|
# @return [String]
|
111
111
|
# @api private
|
112
112
|
def escape_path(path)
|
113
|
-
if RbConfig::CONFIG["host_os"]
|
113
|
+
if /mswin|mingw/.match?(RbConfig::CONFIG["host_os"])
|
114
114
|
# I know what you're thinking: "just use Shellwords.escape". That
|
115
115
|
# method produces incorrect results on Windows with certain input
|
116
116
|
# which would be a metacharacter in Sh but is not for one or more of
|
117
117
|
# Windows command line parsing libraries. This covers the 99% case of
|
118
118
|
# spaces in the path without breaking other stuff.
|
119
|
-
if
|
119
|
+
if /[ \t\n\v"]/.match?(path)
|
120
120
|
"\"#{path.gsub(/[ \t\n\v\"\\]/) { |m| '\\' + m[0] }}\""
|
121
121
|
else
|
122
122
|
path
|
@@ -136,7 +136,7 @@ module Kitchen
|
|
136
136
|
# @api private
|
137
137
|
def detect_chef_command!(logger)
|
138
138
|
unless ENV["PATH"].split(File::PATH_SEPARATOR).any? do |path|
|
139
|
-
if RbConfig::CONFIG["host_os"]
|
139
|
+
if /mswin|mingw/.match?(RbConfig::CONFIG["host_os"])
|
140
140
|
# Windows could have different extentions: BAT, EXE or NONE
|
141
141
|
%w{chef chef.exe chef.bat}.each do |bin|
|
142
142
|
File.exist?(File.join(path, bin))
|
@@ -146,7 +146,7 @@ module Kitchen
|
|
146
146
|
end
|
147
147
|
end
|
148
148
|
logger.fatal("The `chef` executable cannot be found in your " \
|
149
|
-
"PATH. Ensure you have installed
|
149
|
+
"PATH. Ensure you have installed Chef Workstation " \
|
150
150
|
"from https://downloads.chef.io and that your PATH " \
|
151
151
|
"setting includes the path to the `chef` command.")
|
152
152
|
raise UserError,
|
@@ -24,9 +24,9 @@ require_relative "chef/policyfile"
|
|
24
24
|
require_relative "chef/berkshelf"
|
25
25
|
require_relative "chef/common_sandbox"
|
26
26
|
require_relative "../util"
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
module LicenseAcceptance
|
28
|
+
autoload :Acceptor, "license_acceptance/acceptor"
|
29
|
+
end
|
30
30
|
|
31
31
|
begin
|
32
32
|
require "chef-config/config"
|
@@ -156,7 +156,7 @@ module Kitchen
|
|
156
156
|
product_name: <chef or chef-workstation>
|
157
157
|
install_strategy: skip
|
158
158
|
MSG
|
159
|
-
when provisioner[:require_chef_omnibus].to_s.match(/\d/)
|
159
|
+
when provisioner[:require_chef_omnibus].to_s.match?(/\d/)
|
160
160
|
Util.outdent!(<<-MSG)
|
161
161
|
The 'require_chef_omnibus' attribute with version values will change
|
162
162
|
to use the new 'product_version' attribute.
|
@@ -503,6 +503,7 @@ module Kitchen
|
|
503
503
|
# @return [String] contents of product based install script
|
504
504
|
# @api private
|
505
505
|
def script_for_product
|
506
|
+
require "mixlib/install"
|
506
507
|
installer = Mixlib::Install.new({
|
507
508
|
product_name: config[:product_name],
|
508
509
|
product_version: config[:product_version],
|
@@ -576,6 +577,7 @@ module Kitchen
|
|
576
577
|
# @return [String] contents of version based install script
|
577
578
|
# @api private
|
578
579
|
def script_for_omnibus_version
|
580
|
+
require "mixlib/install/script_generator"
|
579
581
|
installer = Mixlib::Install::ScriptGenerator.new(
|
580
582
|
config[:require_chef_omnibus], powershell_shell?, install_options
|
581
583
|
)
|
@@ -27,6 +27,10 @@ module Kitchen
|
|
27
27
|
|
28
28
|
plugin_version Kitchen::VERSION
|
29
29
|
|
30
|
+
# ChefSolo is dependent on Berkshelf, which is not thread-safe.
|
31
|
+
# See discussion on https://github.com/test-kitchen/test-kitchen/issues/1307
|
32
|
+
no_parallel_for :converge
|
33
|
+
|
30
34
|
default_config :solo_rb, {}
|
31
35
|
|
32
36
|
default_config :chef_solo_path do |provisioner|
|
data/lib/kitchen/ssh.rb
CHANGED
@@ -16,8 +16,9 @@
|
|
16
16
|
# limitations under the License.
|
17
17
|
|
18
18
|
require "logger"
|
19
|
-
|
20
|
-
|
19
|
+
module Net
|
20
|
+
autoload :SSH, "net/ssh"
|
21
|
+
end
|
21
22
|
require "socket" unless defined?(Socket)
|
22
23
|
|
23
24
|
require_relative "errors"
|
@@ -90,6 +91,7 @@ module Kitchen
|
|
90
91
|
# `Net::SCP.upload`
|
91
92
|
# @see http://net-ssh.github.io/net-scp/classes/Net/SCP.html#method-i-upload
|
92
93
|
def upload!(local, remote, options = {}, &progress)
|
94
|
+
require "net/scp" unless defined?(Net::SCP)
|
93
95
|
if progress.nil?
|
94
96
|
progress = lambda do |_ch, name, sent, total|
|
95
97
|
logger.debug("Uploaded #{name} (#{total} bytes)") if sent == total
|
@@ -100,6 +102,7 @@ module Kitchen
|
|
100
102
|
end
|
101
103
|
|
102
104
|
def upload(local, remote, options = {}, &progress)
|
105
|
+
require "net/scp" unless defined?(Net::SCP)
|
103
106
|
if progress.nil?
|
104
107
|
progress = lambda do |_ch, name, sent, total|
|
105
108
|
if sent == total
|
data/lib/kitchen/state_file.rb
CHANGED
@@ -15,7 +15,7 @@
|
|
15
15
|
# See the License for the specific language governing permissions and
|
16
16
|
# limitations under the License.
|
17
17
|
|
18
|
-
|
18
|
+
autoload :YAML, "yaml"
|
19
19
|
|
20
20
|
module Kitchen
|
21
21
|
# Exception class for any exceptions raised when reading and parsing a state
|
@@ -28,7 +28,7 @@ module Kitchen
|
|
28
28
|
plugin_version Kitchen::VERSION
|
29
29
|
|
30
30
|
def connection(state, &block)
|
31
|
-
options = config.to_hash.merge(state)
|
31
|
+
options = connection_options(config.to_hash.merge(state))
|
32
32
|
Kitchen::Transport::Exec::Connection.new(options, &block)
|
33
33
|
end
|
34
34
|
|
@@ -40,19 +40,105 @@ module Kitchen
|
|
40
40
|
def execute(command)
|
41
41
|
return if command.nil?
|
42
42
|
|
43
|
-
|
43
|
+
if host_os_windows?
|
44
|
+
run_command(run_from_file_command(command))
|
45
|
+
close
|
46
|
+
else
|
47
|
+
run_command(command)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def close
|
52
|
+
if host_os_windows?
|
53
|
+
FileUtils.remove(exec_script_file)
|
54
|
+
end
|
44
55
|
end
|
45
56
|
|
46
57
|
# "Upload" the files by copying them locally.
|
47
58
|
#
|
48
59
|
# @see Base#upload
|
49
60
|
def upload(locals, remote)
|
50
|
-
|
61
|
+
# evaluate $env:temp on Windows
|
62
|
+
real_remote = remote.to_s == "\$env:TEMP\\kitchen" ? kitchen_temp : remote
|
63
|
+
FileUtils.mkdir_p(real_remote)
|
51
64
|
Array(locals).each do |local|
|
52
|
-
FileUtils.cp_r(local,
|
65
|
+
FileUtils.cp_r(local, real_remote)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# (see Base#init_options)
|
70
|
+
def init_options(options)
|
71
|
+
super
|
72
|
+
@instance_name = @options.delete(:instance_name)
|
73
|
+
@kitchen_root = @options.delete(:kitchen_root)
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
# @return [String] display name for the associated instance
|
79
|
+
# @api private
|
80
|
+
attr_reader :instance_name
|
81
|
+
|
82
|
+
# @return [String] local path to the root of the project
|
83
|
+
# @api private
|
84
|
+
attr_reader :kitchen_root
|
85
|
+
|
86
|
+
# Takes a long command and saves it to a file and uploads it to
|
87
|
+
# the test instance. Windows has cli character limits.
|
88
|
+
#
|
89
|
+
# @param command [String] a long command to be saved and uploaded
|
90
|
+
# @return [String] a command that executes the uploaded script
|
91
|
+
# @api private
|
92
|
+
def run_from_file_command(command)
|
93
|
+
if logger.debug?
|
94
|
+
debug("Creating exec script for #{instance_name} (#{exec_script_file})")
|
95
|
+
debug("Executing #{exec_script_file}")
|
96
|
+
end
|
97
|
+
File.open(exec_script_file, "wb") { |file| file.write(command) }
|
98
|
+
%{powershell -file "#{exec_script_file}"}
|
99
|
+
end
|
100
|
+
|
101
|
+
# @return [String] evaluated $env:temp variable
|
102
|
+
# @api private
|
103
|
+
def kitchen_temp
|
104
|
+
"#{ENV["temp"]}/kitchen"
|
105
|
+
end
|
106
|
+
|
107
|
+
# @return [String] name of script using instance name
|
108
|
+
# @api private
|
109
|
+
def exec_script_name
|
110
|
+
"#{instance_name}-exec-script.ps1"
|
111
|
+
end
|
112
|
+
|
113
|
+
# @return [String] file path for exec script to be run
|
114
|
+
# @api private
|
115
|
+
def exec_script_file
|
116
|
+
File.join(kitchen_root, ".kitchen", exec_script_name)
|
117
|
+
end
|
118
|
+
|
119
|
+
def host_os_windows?
|
120
|
+
case RbConfig::CONFIG["host_os"]
|
121
|
+
when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
|
122
|
+
true
|
123
|
+
else
|
124
|
+
false
|
53
125
|
end
|
54
126
|
end
|
127
|
+
end
|
128
|
+
|
129
|
+
private
|
55
130
|
|
131
|
+
# Builds the hash of options needed by the Connection object on construction.
|
132
|
+
#
|
133
|
+
# @param data [Hash] merged configuration and mutable state data
|
134
|
+
# @return [Hash] hash of connection options
|
135
|
+
# @api private
|
136
|
+
def connection_options(data)
|
137
|
+
opts = {
|
138
|
+
instance_name: instance.name,
|
139
|
+
kitchen_root: Dir.pwd,
|
140
|
+
}
|
141
|
+
opts
|
56
142
|
end
|
57
143
|
end
|
58
144
|
end
|
@@ -308,16 +308,20 @@ module Kitchen
|
|
308
308
|
|
309
309
|
# Builds a `LoginCommand` for use by Linux-based platforms.
|
310
310
|
#
|
311
|
-
# TODO: determine whether or not `desktop` exists
|
312
|
-
#
|
313
311
|
# @return [LoginCommand] a login command
|
314
312
|
# @api private
|
315
313
|
def login_command_for_linux
|
316
|
-
|
317
|
-
|
318
|
-
|
314
|
+
xfreerdp = Util.command_exists? "xfreerdp"
|
315
|
+
unless xfreerdp
|
316
|
+
raise WinrmFailed, "xfreerdp binary not found. Please install freerdp2-x11 on Debian-based systems or freerdp on Redhat-based systems."
|
317
|
+
end
|
318
|
+
|
319
|
+
args = %W{/u:#{options[:user]}}
|
320
|
+
args += %W{/p:#{options[:password]}} if options.key?(:password)
|
321
|
+
args += %W{/v:#{URI.parse(options[:endpoint]).host}:#{rdp_port}}
|
322
|
+
args += %W{/cert-tofu} # always accept certificate
|
319
323
|
|
320
|
-
LoginCommand.new(
|
324
|
+
LoginCommand.new(xfreerdp, args)
|
321
325
|
end
|
322
326
|
|
323
327
|
# Builds a `LoginCommand` for use by Mac-based platforms.
|
data/lib/kitchen/util.rb
CHANGED
@@ -110,7 +110,7 @@ module Kitchen
|
|
110
110
|
def self.wrap_command(cmd)
|
111
111
|
cmd = "false" if cmd.nil?
|
112
112
|
cmd = "true" if cmd.to_s.empty?
|
113
|
-
cmd = cmd.sub(/\n\Z/, "") if
|
113
|
+
cmd = cmd.sub(/\n\Z/, "") if /\n\Z/.match?(cmd)
|
114
114
|
|
115
115
|
"sh -c '\n#{cmd}\n'"
|
116
116
|
end
|
@@ -215,5 +215,15 @@ module Kitchen
|
|
215
215
|
def self.snake_case(a_string)
|
216
216
|
Thor::Util.snake_case(a_string)
|
217
217
|
end
|
218
|
+
|
219
|
+
# Check if a cmd exists on the PATH
|
220
|
+
def self.command_exists?(cmd)
|
221
|
+
paths = ENV["PATH"].split(File::PATH_SEPARATOR) + [ "/bin", "/usr/bin", "/sbin", "/usr/sbin" ]
|
222
|
+
paths.each do |path|
|
223
|
+
filename = File.join(path, cmd)
|
224
|
+
return filename if File.executable?(filename)
|
225
|
+
end
|
226
|
+
false
|
227
|
+
end
|
218
228
|
end
|
219
229
|
end
|
data/lib/kitchen/version.rb
CHANGED
data/test-kitchen.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: test-kitchen
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Fletcher Nichol
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-01-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: mixlib-shellout
|
@@ -415,6 +415,9 @@ files:
|
|
415
415
|
- lib/kitchen/generator/init.rb
|
416
416
|
- lib/kitchen/instance.rb
|
417
417
|
- lib/kitchen/lazy_hash.rb
|
418
|
+
- lib/kitchen/lifecycle_hook/base.rb
|
419
|
+
- lib/kitchen/lifecycle_hook/local.rb
|
420
|
+
- lib/kitchen/lifecycle_hook/remote.rb
|
418
421
|
- lib/kitchen/lifecycle_hooks.rb
|
419
422
|
- lib/kitchen/loader/yaml.rb
|
420
423
|
- lib/kitchen/logger.rb
|
@@ -500,7 +503,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
500
503
|
- !ruby/object:Gem::Version
|
501
504
|
version: '0'
|
502
505
|
requirements: []
|
503
|
-
rubygems_version: 3.1.
|
506
|
+
rubygems_version: 3.1.4
|
504
507
|
signing_key:
|
505
508
|
specification_version: 4
|
506
509
|
summary: Test Kitchen is an integration tool for developing and testing infrastructure
|