test-kitchen 1.2.1 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.cane +1 -1
- data/.rubocop.yml +3 -0
- data/.travis.yml +20 -9
- data/CHANGELOG.md +219 -108
- data/Gemfile +10 -6
- data/Guardfile +38 -9
- data/README.md +11 -1
- data/Rakefile +21 -37
- data/bin/kitchen +4 -4
- data/features/kitchen_action_commands.feature +161 -0
- data/features/kitchen_console_command.feature +34 -0
- data/features/kitchen_diagnose_command.feature +64 -0
- data/features/kitchen_init_command.feature +29 -17
- data/features/kitchen_list_command.feature +2 -2
- data/features/kitchen_login_command.feature +56 -0
- data/features/{sink_command.feature → kitchen_sink_command.feature} +0 -0
- data/features/kitchen_test_command.feature +88 -0
- data/features/step_definitions/gem_steps.rb +8 -6
- data/features/step_definitions/git_steps.rb +4 -2
- data/features/step_definitions/output_steps.rb +5 -0
- data/features/support/env.rb +12 -9
- data/lib/kitchen.rb +60 -38
- data/lib/kitchen/base64_stream.rb +55 -0
- data/lib/kitchen/busser.rb +124 -58
- data/lib/kitchen/cli.rb +121 -38
- data/lib/kitchen/collection.rb +3 -3
- data/lib/kitchen/color.rb +4 -4
- data/lib/kitchen/command.rb +78 -11
- data/lib/kitchen/command/action.rb +3 -2
- data/lib/kitchen/command/console.rb +12 -5
- data/lib/kitchen/command/diagnose.rb +17 -3
- data/lib/kitchen/command/driver_discover.rb +26 -7
- data/lib/kitchen/command/exec.rb +41 -0
- data/lib/kitchen/command/list.rb +44 -14
- data/lib/kitchen/command/login.rb +2 -1
- data/lib/kitchen/command/sink.rb +2 -1
- data/lib/kitchen/command/test.rb +5 -4
- data/lib/kitchen/config.rb +146 -14
- data/lib/kitchen/configurable.rb +314 -0
- data/lib/kitchen/data_munger.rb +522 -18
- data/lib/kitchen/diagnostic.rb +43 -4
- data/lib/kitchen/driver.rb +4 -4
- data/lib/kitchen/driver/base.rb +80 -115
- data/lib/kitchen/driver/dummy.rb +34 -6
- data/lib/kitchen/driver/proxy.rb +14 -3
- data/lib/kitchen/driver/ssh_base.rb +61 -7
- data/lib/kitchen/errors.rb +109 -9
- data/lib/kitchen/generator/driver_create.rb +39 -5
- data/lib/kitchen/generator/init.rb +130 -45
- data/lib/kitchen/instance.rb +162 -28
- data/lib/kitchen/lazy_hash.rb +79 -7
- data/lib/kitchen/loader/yaml.rb +159 -27
- data/lib/kitchen/logger.rb +267 -21
- data/lib/kitchen/logging.rb +30 -3
- data/lib/kitchen/login_command.rb +11 -2
- data/lib/kitchen/metadata_chopper.rb +2 -2
- data/lib/kitchen/provisioner.rb +4 -4
- data/lib/kitchen/provisioner/base.rb +107 -103
- data/lib/kitchen/provisioner/chef/berkshelf.rb +36 -8
- data/lib/kitchen/provisioner/chef/librarian.rb +40 -11
- data/lib/kitchen/provisioner/chef_base.rb +206 -167
- data/lib/kitchen/provisioner/chef_solo.rb +25 -7
- data/lib/kitchen/provisioner/chef_zero.rb +105 -29
- data/lib/kitchen/provisioner/dummy.rb +1 -1
- data/lib/kitchen/provisioner/shell.rb +21 -6
- data/lib/kitchen/rake_tasks.rb +8 -3
- data/lib/kitchen/shell_out.rb +15 -18
- data/lib/kitchen/ssh.rb +122 -27
- data/lib/kitchen/state_file.rb +24 -7
- data/lib/kitchen/thor_tasks.rb +9 -4
- data/lib/kitchen/util.rb +43 -118
- data/lib/kitchen/version.rb +1 -1
- data/lib/vendor/hash_recursive_merge.rb +10 -2
- data/spec/kitchen/base64_stream_spec.rb +77 -0
- data/spec/kitchen/busser_spec.rb +490 -0
- data/spec/kitchen/collection_spec.rb +10 -10
- data/spec/kitchen/color_spec.rb +2 -2
- data/spec/kitchen/config_spec.rb +234 -62
- data/spec/kitchen/configurable_spec.rb +490 -0
- data/spec/kitchen/data_munger_spec.rb +1070 -862
- data/spec/kitchen/diagnostic_spec.rb +79 -0
- data/spec/kitchen/driver/base_spec.rb +80 -85
- data/spec/kitchen/driver/dummy_spec.rb +43 -14
- data/spec/kitchen/driver/proxy_spec.rb +134 -0
- data/spec/kitchen/driver/ssh_base_spec.rb +644 -0
- data/spec/kitchen/driver_spec.rb +15 -15
- data/spec/kitchen/errors_spec.rb +309 -0
- data/spec/kitchen/instance_spec.rb +143 -46
- data/spec/kitchen/lazy_hash_spec.rb +36 -9
- data/spec/kitchen/loader/yaml_spec.rb +237 -226
- data/spec/kitchen/logger_spec.rb +419 -0
- data/spec/kitchen/logging_spec.rb +59 -0
- data/spec/kitchen/login_command_spec.rb +49 -0
- data/spec/kitchen/metadata_chopper_spec.rb +82 -0
- data/spec/kitchen/platform_spec.rb +4 -4
- data/spec/kitchen/provisioner/base_spec.rb +65 -125
- data/spec/kitchen/provisioner/chef_base_spec.rb +798 -0
- data/spec/kitchen/provisioner/chef_solo_spec.rb +316 -0
- data/spec/kitchen/provisioner/chef_zero_spec.rb +624 -0
- data/spec/kitchen/provisioner/shell_spec.rb +269 -0
- data/spec/kitchen/provisioner_spec.rb +6 -6
- data/spec/kitchen/shell_out_spec.rb +143 -0
- data/spec/kitchen/ssh_spec.rb +683 -0
- data/spec/kitchen/state_file_spec.rb +28 -21
- data/spec/kitchen/suite_spec.rb +7 -7
- data/spec/kitchen/util_spec.rb +68 -10
- data/spec/kitchen_spec.rb +107 -0
- data/spec/spec_helper.rb +18 -13
- data/support/chef-client-zero.rb +10 -9
- data/support/chef_helpers.sh +16 -0
- data/support/download_helpers.sh +109 -0
- data/test-kitchen.gemspec +42 -33
- metadata +107 -33
@@ -0,0 +1,55 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# Author:: Fletcher Nichol (<fnichol@nichol.ca>)
|
4
|
+
#
|
5
|
+
# Copyright (C) 2014, Fletcher Nichol
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
|
19
|
+
module Kitchen
|
20
|
+
|
21
|
+
# Base64 encoder/decoder that operates on IO objects so as to minimize
|
22
|
+
# memory allocations on large payloads.
|
23
|
+
#
|
24
|
+
# @author Fletcher Nichol <fnichol@nichol.ca>
|
25
|
+
module Base64Stream
|
26
|
+
|
27
|
+
# Encodes an input stream into a Base64 output stream. The input and ouput
|
28
|
+
# objects must be opened IO resources. In other words, opening and closing
|
29
|
+
# the resources are not the responsibilty of this method.
|
30
|
+
#
|
31
|
+
# @param io_in [#read] input stream
|
32
|
+
# @param io_out [#write] output stream
|
33
|
+
def self.strict_encode(io_in, io_out)
|
34
|
+
buffer = "" # rubocop:disable Lint/UselessAssignment
|
35
|
+
while io_in.read(3 * 1000, buffer)
|
36
|
+
io_out.write([buffer].pack("m0"))
|
37
|
+
end
|
38
|
+
buffer = nil # rubocop:disable Lint/UselessAssignment
|
39
|
+
end
|
40
|
+
|
41
|
+
# Decodes a Base64 input stream into an output stream. The input and ouput
|
42
|
+
# objects must be opened IO resources. In other words, opening and closing
|
43
|
+
# the resources are not the responsibilty of this method.
|
44
|
+
#
|
45
|
+
# @param io_in [#read] input stream
|
46
|
+
# @param io_out [#write] output stream
|
47
|
+
def self.strict_decode(io_in, io_out)
|
48
|
+
buffer = "" # rubocop:disable Lint/UselessAssignment
|
49
|
+
while io_in.read(3 * 1000, buffer)
|
50
|
+
io_out.write(buffer.unpack("m0").first)
|
51
|
+
end
|
52
|
+
buffer = nil # rubocop:disable Lint/UselessAssignment
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/kitchen/busser.rb
CHANGED
@@ -16,8 +16,8 @@
|
|
16
16
|
# See the License for the specific language governing permissions and
|
17
17
|
# limitations under the License.
|
18
18
|
|
19
|
-
require
|
20
|
-
require
|
19
|
+
require "base64"
|
20
|
+
require "digest"
|
21
21
|
|
22
22
|
module Kitchen
|
23
23
|
|
@@ -95,21 +95,23 @@ module Kitchen
|
|
95
95
|
# @return [String] a command string to setup the test suite, or nil if no
|
96
96
|
# work needs to be performed
|
97
97
|
def setup_cmd
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
98
|
+
return if local_suite_files.empty?
|
99
|
+
|
100
|
+
ruby = "#{config[:ruby_bindir]}/ruby"
|
101
|
+
gem = sudo("#{config[:ruby_bindir]}/gem")
|
102
|
+
busser = sudo(config[:busser_bin])
|
103
|
+
|
104
|
+
cmd = <<-CMD.gsub(/^ {8}/, "")
|
105
|
+
#{busser_setup_env}
|
106
|
+
gem_bindir=`#{ruby} -rrubygems -e "puts Gem.bindir"`
|
107
|
+
|
108
|
+
if ! #{gem} list busser -i >/dev/null; then
|
109
|
+
#{gem} install #{gem_install_args}
|
110
|
+
fi
|
111
|
+
#{sudo("${gem_bindir}")}/busser setup
|
112
|
+
#{busser} plugin install #{plugins.join(" ")}
|
113
|
+
CMD
|
114
|
+
Util.wrap_command(cmd)
|
113
115
|
end
|
114
116
|
|
115
117
|
# Returns a command string which transfers all suite test files to the
|
@@ -121,18 +123,22 @@ module Kitchen
|
|
121
123
|
# @return [String] a command string to transfer all suite test files, or
|
122
124
|
# nil if no work needs to be performed.
|
123
125
|
def sync_cmd
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
126
|
+
return if local_suite_files.empty?
|
127
|
+
|
128
|
+
cmd = <<-CMD.gsub(/^ {8}/, "")
|
129
|
+
#{busser_setup_env}
|
130
|
+
|
131
|
+
#{sudo(config[:busser_bin])} suite cleanup
|
132
|
+
|
133
|
+
CMD
|
134
|
+
local_suite_files.each do |f|
|
135
|
+
cmd << stream_file(f, remote_file(f, config[:suite_name])).concat("\n")
|
136
|
+
end
|
137
|
+
helper_files.each do |f|
|
138
|
+
cmd << stream_file(f, remote_file(f, "helpers")).concat("\n")
|
135
139
|
end
|
140
|
+
|
141
|
+
Util.wrap_command(cmd)
|
136
142
|
end
|
137
143
|
|
138
144
|
# Returns a command string which runs all Busser suite tests for the suite.
|
@@ -143,16 +149,15 @@ module Kitchen
|
|
143
149
|
# @return [String] a command string to run the test suites, or nil if no
|
144
150
|
# work needs to be performed
|
145
151
|
def run_cmd
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
end
|
152
|
+
return if local_suite_files.empty?
|
153
|
+
|
154
|
+
cmd = <<-CMD.gsub(/^ {8}/, "")
|
155
|
+
#{busser_setup_env}
|
156
|
+
|
157
|
+
#{sudo(config[:busser_bin])} test
|
158
|
+
CMD
|
159
|
+
|
160
|
+
Util.wrap_command(cmd)
|
156
161
|
end
|
157
162
|
|
158
163
|
private
|
@@ -160,83 +165,144 @@ module Kitchen
|
|
160
165
|
DEFAULT_RUBY_BINDIR = "/opt/chef/embedded/bin".freeze
|
161
166
|
DEFAULT_ROOT_PATH = "/tmp/busser".freeze
|
162
167
|
|
168
|
+
# @return [Hash] a configuration hash
|
169
|
+
# @api private
|
163
170
|
attr_reader :config
|
164
171
|
|
172
|
+
# Ensures that the object is internally consistent and otherwise raising
|
173
|
+
# an exception.
|
174
|
+
#
|
175
|
+
# @param suite_name [String] the suite name
|
176
|
+
# @raise [ClientError] if a suite name is missing
|
177
|
+
# @raise [UserError] if the suite name is invalid
|
178
|
+
# @api private
|
165
179
|
def validate_options(suite_name)
|
166
180
|
if suite_name.nil?
|
167
181
|
raise ClientError, "Busser#new requires a suite_name"
|
168
182
|
end
|
169
183
|
|
170
|
-
if suite_name ==
|
184
|
+
if suite_name == "helper"
|
171
185
|
raise UserError,
|
172
186
|
"Suite name invalid: 'helper' is a reserved directory name."
|
173
187
|
end
|
174
188
|
end
|
175
189
|
|
190
|
+
# Returns a uniquely sorted Array of Busser plugin gems that need to
|
191
|
+
# be installed for the related suite.
|
192
|
+
#
|
193
|
+
# @return [Array<String>] a lexically sorted, unique item array of Busser
|
194
|
+
# plugin gem names
|
195
|
+
# @api private
|
176
196
|
def plugins
|
197
|
+
non_suite_dirs = %w[data data_bags environments nodes roles]
|
177
198
|
glob = File.join(config[:test_base_path], config[:suite_name], "*")
|
178
199
|
Dir.glob(glob).reject { |d|
|
179
|
-
!
|
200
|
+
!File.directory?(d) || non_suite_dirs.include?(File.basename(d))
|
180
201
|
}.map { |d| "busser-#{File.basename(d)}" }.sort.uniq
|
181
202
|
end
|
182
203
|
|
204
|
+
# Returns an Array of test suite filenames for the related suite currently
|
205
|
+
# residing on the local workstation. Any special provisioner-specific
|
206
|
+
# directories (such as a Chef roles/ directory) are excluded.
|
207
|
+
#
|
208
|
+
# @return [Array<String>] array of suite files
|
209
|
+
# @api private
|
183
210
|
def local_suite_files
|
184
211
|
base = File.join(config[:test_base_path], config[:suite_name])
|
185
212
|
glob = File.join(base, "*/**/*")
|
186
213
|
Dir.glob(glob).reject do |f|
|
187
|
-
|
214
|
+
chef_data_dir?(base, f) || File.directory?(f)
|
188
215
|
end
|
189
216
|
end
|
190
217
|
|
191
|
-
|
192
|
-
|
218
|
+
# Determines whether or not a local workstation file exists under a
|
219
|
+
# Chef-related directory.
|
220
|
+
#
|
221
|
+
# @return [truthy,falsey] whether or not a given file is some kind of
|
222
|
+
# Chef-related file
|
223
|
+
# @api private
|
224
|
+
def chef_data_dir?(base, file)
|
225
|
+
file =~ %r{^#{base}/(data|data_bags|environments|nodes|roles)/}
|
193
226
|
end
|
194
227
|
|
228
|
+
# Returns an Array of common helper filenames currently residing on the
|
229
|
+
# local workstation.
|
230
|
+
#
|
231
|
+
# @return [Array<String>] array of helper files
|
232
|
+
# @api private
|
195
233
|
def helper_files
|
196
|
-
|
234
|
+
glob = File.join(config[:test_base_path], "helpers", "*/**/*")
|
235
|
+
Dir.glob(glob).reject { |f| File.directory?(f) }
|
197
236
|
end
|
198
237
|
|
238
|
+
# Returns a command string that will, once evaluated, result in the
|
239
|
+
# fully qualified destination path of a file on an instance.
|
240
|
+
#
|
241
|
+
# @param file [String] absolute path to the local file
|
242
|
+
# @param dir [String] suite directory or helper directory name
|
243
|
+
# @return [String] command string
|
244
|
+
# @api private
|
199
245
|
def remote_file(file, dir)
|
200
246
|
local_prefix = File.join(config[:test_base_path], dir)
|
201
|
-
"
|
247
|
+
"`#{sudo(config[:busser_bin])} suite path`/".
|
248
|
+
concat(file.sub(%r{^#{local_prefix}/}, ""))
|
202
249
|
end
|
203
250
|
|
251
|
+
# Returns a command string that will, once evaluated, result in the copying
|
252
|
+
# of a local file to a remote instance.
|
253
|
+
#
|
254
|
+
# @param local_path [String] the path to a local source file for copying
|
255
|
+
# @param remote_path [String] the destrination path on the remote instance
|
256
|
+
# @return [String] command string
|
257
|
+
# @api private
|
204
258
|
def stream_file(local_path, remote_path)
|
205
259
|
local_file = IO.read(local_path)
|
260
|
+
encoded_file = Base64.encode64(local_file).gsub("\n", "")
|
206
261
|
md5 = Digest::MD5.hexdigest(local_file)
|
207
|
-
perms =
|
262
|
+
perms = format("%o", File.stat(local_path).mode)[2, 4]
|
208
263
|
stream_cmd = [
|
209
|
-
|
264
|
+
sudo(config[:busser_bin]),
|
210
265
|
"deserialize",
|
211
266
|
"--destination=#{remote_path}",
|
212
267
|
"--md5sum=#{md5}",
|
213
268
|
"--perms=#{perms}"
|
214
269
|
].join(" ")
|
215
270
|
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
end
|
221
|
-
|
222
|
-
def sudo
|
223
|
-
config[:sudo] ? "sudo -E " : ""
|
271
|
+
[
|
272
|
+
%{echo "Uploading #{remote_path} (mode=#{perms})"},
|
273
|
+
%{echo "#{encoded_file}" | #{stream_cmd}}
|
274
|
+
].join("\n").concat("\n")
|
224
275
|
end
|
225
276
|
|
226
|
-
|
227
|
-
|
277
|
+
# Conditionally prefixes a command with a sudo command.
|
278
|
+
#
|
279
|
+
# @param command [String] command to be prefixed
|
280
|
+
# @return [String] the command, conditionaly prefixed with sudo
|
281
|
+
# @api private
|
282
|
+
def sudo(command)
|
283
|
+
config[:sudo] ? "sudo -E #{command}" : command
|
228
284
|
end
|
229
285
|
|
286
|
+
# Returns a command string that sets appropriate environment variables for
|
287
|
+
# busser commands.
|
288
|
+
#
|
289
|
+
# @return [String] command string
|
290
|
+
# @api private
|
230
291
|
def busser_setup_env
|
231
292
|
[
|
232
293
|
%{BUSSER_ROOT="#{config[:root_path]}"},
|
233
294
|
%{GEM_HOME="#{config[:root_path]}/gems"},
|
234
295
|
%{GEM_PATH="#{config[:root_path]}/gems"},
|
235
296
|
%{GEM_CACHE="#{config[:root_path]}/gems/cache"},
|
236
|
-
%{
|
297
|
+
%{\nexport BUSSER_ROOT GEM_HOME GEM_PATH GEM_CACHE}
|
237
298
|
].join(" ")
|
238
299
|
end
|
239
300
|
|
301
|
+
# Returns arguments to a `gem install` command, suitable to install the
|
302
|
+
# Busser gem.
|
303
|
+
#
|
304
|
+
# @return [String] arguments string
|
305
|
+
# @api private
|
240
306
|
def gem_install_args
|
241
307
|
gem, version = config[:version].split("@")
|
242
308
|
gem, version = "busser", gem if gem =~ /^\d+\.\d+\.\d+/
|
data/lib/kitchen/cli.rb
CHANGED
@@ -16,11 +16,11 @@
|
|
16
16
|
# See the License for the specific language governing permissions and
|
17
17
|
# limitations under the License.
|
18
18
|
|
19
|
-
require
|
19
|
+
require "thor"
|
20
20
|
|
21
|
-
require
|
22
|
-
require
|
23
|
-
require
|
21
|
+
require "kitchen"
|
22
|
+
require "kitchen/generator/driver_create"
|
23
|
+
require "kitchen/generator/init"
|
24
24
|
|
25
25
|
module Kitchen
|
26
26
|
|
@@ -32,12 +32,21 @@ module Kitchen
|
|
32
32
|
# Common module to load and invoke a CLI-implementation agnostic command.
|
33
33
|
module PerformCommand
|
34
34
|
|
35
|
+
# Perform a CLI subcommand.
|
36
|
+
#
|
37
|
+
# @param task [String] action to take, usually corresponding to the
|
38
|
+
# subcommand name
|
39
|
+
# @param command [String] command class to create and invoke]
|
40
|
+
# @param args [Array] remainder arguments from processed ARGV
|
41
|
+
# (default: `nil`)
|
42
|
+
# @param additional_options [Hash] additional configuration needed to
|
43
|
+
# set up the command class (default: `{}`)
|
35
44
|
def perform(task, command, args = nil, additional_options = {})
|
36
45
|
require "kitchen/command/#{command}"
|
37
46
|
|
38
47
|
command_options = {
|
39
48
|
:action => task,
|
40
|
-
:help =>
|
49
|
+
:help => -> { help(task) },
|
41
50
|
:config => @config,
|
42
51
|
:shell => shell
|
43
52
|
}.merge(additional_options)
|
@@ -51,6 +60,8 @@ module Kitchen
|
|
51
60
|
include Logging
|
52
61
|
include PerformCommand
|
53
62
|
|
63
|
+
# The maximum number of concurrent instances that can run--which is a bit
|
64
|
+
# high
|
54
65
|
MAX_CONCURRENCY = 9999
|
55
66
|
|
56
67
|
# Constructs a new instance.
|
@@ -59,22 +70,27 @@ module Kitchen
|
|
59
70
|
$stdout.sync = true
|
60
71
|
Kitchen.logger = Kitchen.default_file_logger
|
61
72
|
@loader = Kitchen::Loader::YAML.new(
|
62
|
-
:project_config => ENV[
|
63
|
-
:local_config => ENV[
|
64
|
-
:global_config => ENV[
|
73
|
+
:project_config => ENV["KITCHEN_YAML"],
|
74
|
+
:local_config => ENV["KITCHEN_LOCAL_YAML"],
|
75
|
+
:global_config => ENV["KITCHEN_GLOBAL_YAML"]
|
65
76
|
)
|
66
77
|
@config = Kitchen::Config.new(
|
67
78
|
:loader => @loader,
|
68
|
-
:log_level => ENV.fetch(
|
79
|
+
:log_level => ENV.fetch("KITCHEN_LOG", "info").downcase.to_sym
|
69
80
|
)
|
70
81
|
end
|
71
82
|
|
72
83
|
desc "list [INSTANCE|REGEXP|all]", "Lists one or more instances"
|
73
|
-
method_option :bare,
|
84
|
+
method_option :bare,
|
85
|
+
:aliases => "-b",
|
86
|
+
:type => :boolean,
|
74
87
|
:desc => "List the name of each instance only, one per line"
|
75
|
-
method_option :debug,
|
88
|
+
method_option :debug,
|
89
|
+
:aliases => "-d",
|
90
|
+
:type => :boolean,
|
76
91
|
:desc => "[Deprecated] Please use `kitchen diagnose'"
|
77
|
-
method_option :log_level,
|
92
|
+
method_option :log_level,
|
93
|
+
:aliases => "-l",
|
78
94
|
:desc => "Set the log level (debug, info, warn, error, fatal)"
|
79
95
|
def list(*args)
|
80
96
|
update_config!
|
@@ -82,36 +98,63 @@ module Kitchen
|
|
82
98
|
end
|
83
99
|
|
84
100
|
desc "diagnose [INSTANCE|REGEXP|all]", "Show computed diagnostic configuration"
|
85
|
-
method_option :log_level,
|
101
|
+
method_option :log_level,
|
102
|
+
:aliases => "-l",
|
86
103
|
:desc => "Set the log level (debug, info, warn, error, fatal)"
|
87
|
-
method_option :loader,
|
104
|
+
method_option :loader,
|
105
|
+
:type => :boolean,
|
88
106
|
:desc => "Include data loader diagnostics"
|
89
|
-
method_option :instances,
|
107
|
+
method_option :instances,
|
108
|
+
:type => :boolean,
|
109
|
+
:default => true,
|
90
110
|
:desc => "Include instances diagnostics"
|
91
|
-
method_option :all,
|
111
|
+
method_option :all,
|
112
|
+
:type => :boolean,
|
92
113
|
:desc => "Include all diagnostics"
|
93
114
|
def diagnose(*args)
|
94
115
|
update_config!
|
95
116
|
perform("diagnose", "diagnose", args, :loader => @loader)
|
96
117
|
end
|
97
118
|
|
98
|
-
|
119
|
+
{
|
120
|
+
:create => "Change instance state to create. " \
|
121
|
+
"Start one or more instances",
|
122
|
+
:converge => "Change instance state to converge. " \
|
123
|
+
"Use a provisioner to configure one or more instances",
|
124
|
+
:setup => "Change instance state to setup. " \
|
125
|
+
"Prepare to run automated tests. " \
|
126
|
+
"Install busser and related gems on one or more instances",
|
127
|
+
:verify => "Change instance state to verify. " \
|
128
|
+
"Run automated tests on one or more instances",
|
129
|
+
:destroy => "Change instance state to destroy. " \
|
130
|
+
"Delete all information for one or more instances"
|
131
|
+
}.each do |action, short_desc|
|
99
132
|
desc(
|
100
133
|
"#{action} [INSTANCE|REGEXP|all]",
|
101
|
-
|
134
|
+
short_desc
|
102
135
|
)
|
103
|
-
|
104
|
-
|
105
|
-
|
136
|
+
long_desc <<-DESC
|
137
|
+
The instance states are in order: destroy, create, converge, setup, verify, destroy.
|
138
|
+
Change one or more instances from the current state to the #{action} state. Actions for all
|
139
|
+
intermediate states will be executed. See http://kitchen.ci for further explanation.
|
140
|
+
DESC
|
141
|
+
method_option :concurrency,
|
142
|
+
:aliases => "-c",
|
143
|
+
:type => :numeric,
|
144
|
+
:lazy_default => MAX_CONCURRENCY,
|
145
|
+
:desc => <<-DESC.gsub(/^\s+/, "").gsub(/\n/, " ")
|
106
146
|
Run a #{action} against all matching instances concurrently. Only N
|
107
147
|
instances will run at the same time if a number is given.
|
108
148
|
DESC
|
109
|
-
method_option :parallel,
|
110
|
-
:
|
149
|
+
method_option :parallel,
|
150
|
+
:aliases => "-p",
|
151
|
+
:type => :boolean,
|
152
|
+
:desc => <<-DESC.gsub(/^\s+/, "").gsub(/\n/, " ")
|
111
153
|
[Future DEPRECATION, use --concurrency]
|
112
154
|
Run a #{action} against all matching instances concurrently.
|
113
155
|
DESC
|
114
|
-
method_option :log_level,
|
156
|
+
method_option :log_level,
|
157
|
+
:aliases => "-l",
|
115
158
|
:desc => "Set the log level (debug, info, warn, error, fatal)"
|
116
159
|
define_method(action) do |*args|
|
117
160
|
update_config!
|
@@ -119,9 +162,13 @@ module Kitchen
|
|
119
162
|
end
|
120
163
|
end
|
121
164
|
|
122
|
-
desc "test [INSTANCE|REGEXP|all]",
|
165
|
+
desc "test [INSTANCE|REGEXP|all]",
|
166
|
+
"Test (destroy, create, converge, setup, verify and destroy) one or more instances"
|
123
167
|
long_desc <<-DESC
|
124
|
-
|
168
|
+
The instance states are in order: destroy, create, converge, setup, verify, destroy.
|
169
|
+
Test changes the state of one or more instances to destroyed, then executes
|
170
|
+
the actions for each state up to destroy. At any sign of failure, executing the
|
171
|
+
actions stops and the instance is left in the last successful execution state.
|
125
172
|
|
126
173
|
There are 3 post-verify modes for instance cleanup, triggered with
|
127
174
|
the `--destroy' flag:
|
@@ -130,22 +177,31 @@ module Kitchen
|
|
130
177
|
* always: instances will always be destroyed afterwards.\n
|
131
178
|
* never: instances will never be destroyed afterwards.
|
132
179
|
DESC
|
133
|
-
method_option :concurrency,
|
134
|
-
:
|
135
|
-
:
|
180
|
+
method_option :concurrency,
|
181
|
+
:aliases => "-c",
|
182
|
+
:type => :numeric,
|
183
|
+
:lazy_default => MAX_CONCURRENCY,
|
184
|
+
:desc => <<-DESC.gsub(/^\s+/, "").gsub(/\n/, " ")
|
136
185
|
Run a test against all matching instances concurrently. Only N
|
137
186
|
instances will run at the same time if a number is given.
|
138
187
|
DESC
|
139
|
-
method_option :parallel,
|
140
|
-
:
|
188
|
+
method_option :parallel,
|
189
|
+
:aliases => "-p",
|
190
|
+
:type => :boolean,
|
191
|
+
:desc => <<-DESC.gsub(/^\s+/, "").gsub(/\n/, " ")
|
141
192
|
[Future DEPRECATION, use --concurrency]
|
142
193
|
Run a test against all matching instances concurrently.
|
143
194
|
DESC
|
144
|
-
method_option :log_level,
|
195
|
+
method_option :log_level,
|
196
|
+
:aliases => "-l",
|
145
197
|
:desc => "Set the log level (debug, info, warn, error, fatal)"
|
146
|
-
method_option :destroy,
|
198
|
+
method_option :destroy,
|
199
|
+
:aliases => "-d",
|
200
|
+
:default => "passing",
|
147
201
|
:desc => "Destroy strategy to use after testing (passing, always, never)."
|
148
|
-
method_option :auto_init,
|
202
|
+
method_option :auto_init,
|
203
|
+
:type => :boolean,
|
204
|
+
:default => false,
|
149
205
|
:desc => "Invoke init command if .kitchen.yml is missing"
|
150
206
|
def test(*args)
|
151
207
|
update_config!
|
@@ -154,18 +210,32 @@ module Kitchen
|
|
154
210
|
end
|
155
211
|
|
156
212
|
desc "login INSTANCE|REGEXP", "Log in to one instance"
|
157
|
-
method_option :log_level,
|
213
|
+
method_option :log_level,
|
214
|
+
:aliases => "-l",
|
158
215
|
:desc => "Set the log level (debug, info, warn, error, fatal)"
|
159
216
|
def login(*args)
|
160
217
|
update_config!
|
161
218
|
perform("login", "login", args)
|
162
219
|
end
|
163
220
|
|
221
|
+
desc "exec INSTANCE|REGEXP -c REMOTE_COMMAND",
|
222
|
+
"Execute command on one or more instance"
|
223
|
+
method_option :log_level,
|
224
|
+
:aliases => "-l",
|
225
|
+
:desc => "Set the log level (debug, info, warn, error, fatal)"
|
226
|
+
method_option :command,
|
227
|
+
:aliases => "-c",
|
228
|
+
:desc => "execute via ssh"
|
229
|
+
def exec(*args)
|
230
|
+
update_config!
|
231
|
+
perform("exec", "exec", args)
|
232
|
+
end
|
233
|
+
|
164
234
|
desc "version", "Print Kitchen's version information"
|
165
235
|
def version
|
166
236
|
puts "Test Kitchen version #{Kitchen::VERSION}"
|
167
237
|
end
|
168
|
-
map %w
|
238
|
+
map %w[-v --version] => :version
|
169
239
|
|
170
240
|
desc "sink", "Show the Kitchen sink!", :hide => true
|
171
241
|
def sink
|
@@ -217,6 +287,7 @@ module Kitchen
|
|
217
287
|
perform("discover", "driver_discover", args)
|
218
288
|
end
|
219
289
|
|
290
|
+
# @return [String] basename
|
220
291
|
def self.basename
|
221
292
|
super + " driver"
|
222
293
|
end
|
@@ -238,14 +309,23 @@ module Kitchen
|
|
238
309
|
|
239
310
|
private
|
240
311
|
|
312
|
+
# Ensure the any failing commands exit non-zero.
|
313
|
+
#
|
314
|
+
# @return [true] you die always on failure
|
315
|
+
# @api private
|
241
316
|
def self.exit_on_failure?
|
242
317
|
true
|
243
318
|
end
|
244
319
|
|
320
|
+
# @return [Logger] the common logger
|
321
|
+
# @api private
|
245
322
|
def logger
|
246
323
|
Kitchen.logger
|
247
324
|
end
|
248
325
|
|
326
|
+
# Update and finalize options for logging, concurrency, and other concerns.
|
327
|
+
#
|
328
|
+
# @api private
|
249
329
|
def update_config!
|
250
330
|
if options[:log_level]
|
251
331
|
level = options[:log_level].downcase.to_sym
|
@@ -264,10 +344,13 @@ module Kitchen
|
|
264
344
|
end
|
265
345
|
end
|
266
346
|
|
347
|
+
# If auto_init option is active, invoke the init generator.
|
348
|
+
#
|
349
|
+
# @api private
|
267
350
|
def ensure_initialized
|
268
|
-
yaml = ENV[
|
351
|
+
yaml = ENV["KITCHEN_YAML"] || ".kitchen.yml"
|
269
352
|
|
270
|
-
if options[:auto_init] && !
|
353
|
+
if options[:auto_init] && !File.exist?(yaml)
|
271
354
|
banner "Invoking init as '#{yaml}' file is missing"
|
272
355
|
invoke "init"
|
273
356
|
end
|