test-kitchen 1.2.1 → 1.3.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/.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
data/lib/kitchen/driver/proxy.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
1
2
|
#
|
2
3
|
# Author:: Seth Chisamore <schisamo@opscode.com>
|
3
4
|
#
|
@@ -17,13 +18,17 @@
|
|
17
18
|
# limitations under the License.
|
18
19
|
#
|
19
20
|
|
20
|
-
require
|
21
|
+
require "kitchen"
|
21
22
|
|
22
23
|
module Kitchen
|
23
24
|
|
24
25
|
module Driver
|
25
26
|
|
26
|
-
#
|
27
|
+
# Simple driver that proxies commands through to a test instance whose
|
28
|
+
# lifecycle is not managed by Test Kitchen. This driver is useful for long-
|
29
|
+
# lived non-ephemeral test instances that are simply "reset" between test
|
30
|
+
# runs. Think executing against devices like network switches--this is why
|
31
|
+
# the driver was created.
|
27
32
|
#
|
28
33
|
# @author Seth Chisamore <schisamo@opscode.com>
|
29
34
|
class Proxy < Kitchen::Driver::SSHBase
|
@@ -33,11 +38,13 @@ module Kitchen
|
|
33
38
|
|
34
39
|
no_parallel_for :create, :destroy
|
35
40
|
|
41
|
+
# (see Base#create)
|
36
42
|
def create(state)
|
37
43
|
state[:hostname] = config[:host]
|
38
44
|
reset_instance(state)
|
39
45
|
end
|
40
46
|
|
47
|
+
# (see Base#destroy)
|
41
48
|
def destroy(state)
|
42
49
|
return if state[:hostname].nil?
|
43
50
|
reset_instance(state)
|
@@ -46,13 +53,17 @@ module Kitchen
|
|
46
53
|
|
47
54
|
private
|
48
55
|
|
56
|
+
# Resets the non-Kitchen managed instance using by issuing a command
|
57
|
+
# over SSH.
|
58
|
+
#
|
59
|
+
# @param state [Hash] the state hash
|
60
|
+
# @api private
|
49
61
|
def reset_instance(state)
|
50
62
|
if cmd = config[:reset_command]
|
51
63
|
info("Resetting instance state with command: #{cmd}")
|
52
64
|
ssh(build_ssh_args(state), cmd)
|
53
65
|
end
|
54
66
|
end
|
55
|
-
|
56
67
|
end
|
57
68
|
end
|
58
69
|
end
|
@@ -31,10 +31,12 @@ module Kitchen
|
|
31
31
|
default_config :sudo, true
|
32
32
|
default_config :port, 22
|
33
33
|
|
34
|
-
|
34
|
+
# (see Base#create)
|
35
|
+
def create(state) # rubocop:disable Lint/UnusedMethodArgument
|
35
36
|
raise ClientError, "#{self.class}#create must be implemented"
|
36
37
|
end
|
37
38
|
|
39
|
+
# (see Base#converge)
|
38
40
|
def converge(state)
|
39
41
|
provisioner = instance.provisioner
|
40
42
|
provisioner.create_sandbox
|
@@ -51,35 +53,61 @@ module Kitchen
|
|
51
53
|
provisioner && provisioner.cleanup_sandbox
|
52
54
|
end
|
53
55
|
|
56
|
+
# (see Base#setup)
|
54
57
|
def setup(state)
|
55
58
|
Kitchen::SSH.new(*build_ssh_args(state)) do |conn|
|
56
|
-
run_remote(
|
59
|
+
run_remote(busser.setup_cmd, conn)
|
57
60
|
end
|
58
61
|
end
|
59
62
|
|
63
|
+
# (see Base#verify)
|
60
64
|
def verify(state)
|
61
65
|
Kitchen::SSH.new(*build_ssh_args(state)) do |conn|
|
62
|
-
run_remote(
|
63
|
-
run_remote(
|
66
|
+
run_remote(busser.sync_cmd, conn)
|
67
|
+
run_remote(busser.run_cmd, conn)
|
64
68
|
end
|
65
69
|
end
|
66
70
|
|
67
|
-
|
71
|
+
# (see Base#destroy)
|
72
|
+
def destroy(state) # rubocop:disable Lint/UnusedMethodArgument
|
68
73
|
raise ClientError, "#{self.class}#destroy must be implemented"
|
69
74
|
end
|
70
75
|
|
76
|
+
# (see Base#login_command)
|
71
77
|
def login_command(state)
|
72
78
|
SSH.new(*build_ssh_args(state)).login_command
|
73
79
|
end
|
74
80
|
|
81
|
+
# Executes an arbitrary command on an instance over an SSH connection.
|
82
|
+
#
|
83
|
+
# @param state [Hash] mutable instance and driver state
|
84
|
+
# @param command [String] the command to be executed
|
85
|
+
# @raise [ActionFailed] if the command could not be successfully completed
|
86
|
+
def remote_command(state, command)
|
87
|
+
Kitchen::SSH.new(*build_ssh_args(state)) do |conn|
|
88
|
+
run_remote(command, conn)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# **(Deprecated)** Executes a remote command over SSH.
|
93
|
+
#
|
94
|
+
# @param ssh_args [Array] ssh arguments
|
95
|
+
# @param command [String] remote command to invoke
|
96
|
+
# @deprecated This method should no longer be called directly and exists
|
97
|
+
# to support very old drivers. This will be removed in the future.
|
75
98
|
def ssh(ssh_args, command)
|
76
99
|
Kitchen::SSH.new(*ssh_args) do |conn|
|
77
100
|
run_remote(command, conn)
|
78
101
|
end
|
79
102
|
end
|
80
103
|
|
81
|
-
|
104
|
+
private
|
82
105
|
|
106
|
+
# Builds arguments for constructing a `Kitchen::SSH` instance.
|
107
|
+
#
|
108
|
+
# @param state [Hash] state hash
|
109
|
+
# @return [Array] SSH constructor arguments
|
110
|
+
# @api private
|
83
111
|
def build_ssh_args(state)
|
84
112
|
combined = config.to_hash.merge(state)
|
85
113
|
|
@@ -96,6 +124,12 @@ module Kitchen
|
|
96
124
|
[combined[:hostname], combined[:username], opts]
|
97
125
|
end
|
98
126
|
|
127
|
+
# Adds http and https proxy environment variables to a command, if set
|
128
|
+
# in configuration data.
|
129
|
+
#
|
130
|
+
# @param cmd [String] command string
|
131
|
+
# @return [String] command string
|
132
|
+
# @api private
|
99
133
|
def env_cmd(cmd)
|
100
134
|
env = "env"
|
101
135
|
env << " http_proxy=#{config[:http_proxy]}" if config[:http_proxy]
|
@@ -104,6 +138,12 @@ module Kitchen
|
|
104
138
|
env == "env" ? cmd : "#{env} #{cmd}"
|
105
139
|
end
|
106
140
|
|
141
|
+
# Executes a remote command over SSH.
|
142
|
+
#
|
143
|
+
# @param command [String] remove command to run
|
144
|
+
# @param connection [Kitchen::SSH] an SSH connection
|
145
|
+
# @raise [ActionFailed] if an exception occurs
|
146
|
+
# @api private
|
107
147
|
def run_remote(command, connection)
|
108
148
|
return if command.nil?
|
109
149
|
|
@@ -112,16 +152,30 @@ module Kitchen
|
|
112
152
|
raise ActionFailed, ex.message
|
113
153
|
end
|
114
154
|
|
155
|
+
# Transfers one or more local paths over SSH.
|
156
|
+
#
|
157
|
+
# @param locals [Array<String>] array of local paths
|
158
|
+
# @param remote [String] remote destination path
|
159
|
+
# @param connection [Kitchen::SSH] an SSH connection
|
160
|
+
# @raise [ActionFailed] if an exception occurs
|
161
|
+
# @api private
|
115
162
|
def transfer_path(locals, remote, connection)
|
116
163
|
return if locals.nil? || Array(locals).empty?
|
117
164
|
|
118
|
-
info("
|
165
|
+
info("Transferring files to #{instance.to_str}")
|
119
166
|
locals.each { |local| connection.upload_path!(local, remote) }
|
120
167
|
debug("Transfer complete")
|
121
168
|
rescue SSHFailed, Net::SSH::Exception => ex
|
122
169
|
raise ActionFailed, ex.message
|
123
170
|
end
|
124
171
|
|
172
|
+
# Blocks until a TCP socket is available where a remote SSH server
|
173
|
+
# should be listening.
|
174
|
+
#
|
175
|
+
# @param hostname [String] remote SSH server host
|
176
|
+
# @param username [String] SSH username (default: `nil`)
|
177
|
+
# @param options [Hash] configuration hash (default: `{}`)
|
178
|
+
# @api private
|
125
179
|
def wait_for_sshd(hostname, username = nil, options = {})
|
126
180
|
SSH.new(hostname, username, { :logger => logger }.merge(options)).wait
|
127
181
|
end
|
data/lib/kitchen/errors.rb
CHANGED
@@ -16,6 +16,8 @@
|
|
16
16
|
# See the License for the specific language governing permissions and
|
17
17
|
# limitations under the License.
|
18
18
|
|
19
|
+
require "English"
|
20
|
+
|
19
21
|
module Kitchen
|
20
22
|
|
21
23
|
# All Kitchen errors and exceptions.
|
@@ -23,6 +25,24 @@ module Kitchen
|
|
23
25
|
# @author Fletcher Nichol <fnichol@nichol.ca>
|
24
26
|
module Error
|
25
27
|
|
28
|
+
# Creates an array of strings, representing a formatted exception,
|
29
|
+
# containing backtrace and nested exception info as necessary, that can
|
30
|
+
# be viewed by a human.
|
31
|
+
#
|
32
|
+
# For example:
|
33
|
+
#
|
34
|
+
# ------Exception-------
|
35
|
+
# Class: Kitchen::StandardError
|
36
|
+
# Message: Failure starting the party
|
37
|
+
# ---Nested Exception---
|
38
|
+
# Class: IOError
|
39
|
+
# Message: not enough directories for a party
|
40
|
+
# ------Backtrace-------
|
41
|
+
# nil
|
42
|
+
# ----------------------
|
43
|
+
#
|
44
|
+
# @param exception [::StandardError] an exception
|
45
|
+
# @return [Array<String>] a formatted message
|
26
46
|
def self.formatted_trace(exception)
|
27
47
|
arr = formatted_exception(exception).dup
|
28
48
|
last = arr.pop
|
@@ -34,12 +54,27 @@ module Kitchen
|
|
34
54
|
arr
|
35
55
|
end
|
36
56
|
|
57
|
+
# Creates an array of strings, representing a formatted exception that
|
58
|
+
# can be viewed by a human. Thanks to MiniTest for the inspiration
|
59
|
+
# upon which this output has been designed.
|
60
|
+
#
|
61
|
+
# For example:
|
62
|
+
#
|
63
|
+
# ------Exception-------
|
64
|
+
# Class: Kitchen::StandardError
|
65
|
+
# Message: I have failed you
|
66
|
+
# ----------------------
|
67
|
+
#
|
68
|
+
# @param exception [::StandardError] an exception
|
69
|
+
# @param title [String] a custom title for the message
|
70
|
+
# (default: `"Exception"`)
|
71
|
+
# @return [Array<String>] a formatted message
|
37
72
|
def self.formatted_exception(exception, title = "Exception")
|
38
73
|
[
|
39
74
|
title.center(22, "-"),
|
40
75
|
"Class: #{exception.class}",
|
41
76
|
"Message: #{exception.message}",
|
42
|
-
"".center(22, "-")
|
77
|
+
"".center(22, "-")
|
43
78
|
]
|
44
79
|
end
|
45
80
|
end
|
@@ -50,9 +85,16 @@ module Kitchen
|
|
50
85
|
|
51
86
|
include Error
|
52
87
|
|
88
|
+
# @return [::StandardError] the original (wrapped) exception
|
53
89
|
attr_reader :original
|
54
90
|
|
55
|
-
|
91
|
+
# Creates a new StandardError exception which optionally wraps an original
|
92
|
+
# exception if given or detected by checking the `$!` global variable.
|
93
|
+
#
|
94
|
+
# @param msg [String] exception message
|
95
|
+
# @param original [::StandardError] an original exception which will be
|
96
|
+
# wrapped (default: `$ERROR_INFO`)
|
97
|
+
def initialize(msg, original = $ERROR_INFO)
|
56
98
|
super(msg)
|
57
99
|
@original = original
|
58
100
|
end
|
@@ -60,23 +102,54 @@ module Kitchen
|
|
60
102
|
|
61
103
|
# Base exception class for all exceptions that are caused by user input
|
62
104
|
# errors.
|
63
|
-
class UserError < StandardError
|
105
|
+
class UserError < StandardError; end
|
64
106
|
|
65
107
|
# Base exception class for all exceptions that are caused by incorrect use
|
66
108
|
# of an API.
|
67
|
-
class ClientError < StandardError
|
109
|
+
class ClientError < StandardError; end
|
68
110
|
|
69
111
|
# Base exception class for exceptions that are caused by external library
|
70
112
|
# failures which may be temporary.
|
71
|
-
class TransientFailure < StandardError
|
113
|
+
class TransientFailure < StandardError; end
|
72
114
|
|
73
115
|
# Exception class for any exceptions raised when performing an instance
|
74
116
|
# action.
|
75
|
-
class ActionFailed < TransientFailure
|
117
|
+
class ActionFailed < TransientFailure; end
|
76
118
|
|
77
119
|
# Exception class capturing what caused an instance to die.
|
78
|
-
class InstanceFailure < TransientFailure
|
120
|
+
class InstanceFailure < TransientFailure; end
|
79
121
|
|
122
|
+
# Yields to a code block in order to consistently emit a useful crash/error
|
123
|
+
# message and exit appropriately. There are two primary failure conditions:
|
124
|
+
# an expected instance failure, and any other unexpected failures.
|
125
|
+
#
|
126
|
+
# **Note** This method may call `Kernel.exit` so may not return if the
|
127
|
+
# yielded code block raises an exception.
|
128
|
+
#
|
129
|
+
# ## Instance Failure
|
130
|
+
#
|
131
|
+
# This is an expected failure scenario which could happen if an instance
|
132
|
+
# couldn't be created, a Chef run didn't successfully converge, a
|
133
|
+
# post-convergence test suite failed, etc. In other words, you can count on
|
134
|
+
# encountering these failures all the time--this is Kitchen's worldview:
|
135
|
+
# crash early and often. In this case a cleanly formatted exception is
|
136
|
+
# written to `STDERR` and the exception message is written to
|
137
|
+
# the common Kitchen file logger.
|
138
|
+
#
|
139
|
+
# ## Unexpected Failure
|
140
|
+
#
|
141
|
+
# All other forms of `Kitchen::Error` exceptions are considered unexpected
|
142
|
+
# or unplanned exceptions, typically from user configuration errors, driver
|
143
|
+
# or provisioner coding issues or bugs, or internal code issues. Given
|
144
|
+
# a stable release of Kitchen and a solid set of drivers and provisioners,
|
145
|
+
# the most likely cause of this is user configuration error originating in
|
146
|
+
# the `.kitchen.yml` setup. For this reason, the exception is written to
|
147
|
+
# `STDERR`, a full formatted exception trace is written to the common
|
148
|
+
# Kitchen file logger, and a message is displayed on `STDERR` to the user
|
149
|
+
# informing them to check the log files and check their configuration with
|
150
|
+
# the `kitchen diagnose` subcommand.
|
151
|
+
#
|
152
|
+
# @raise [SystemExit] if an exception is raised in the yielded block
|
80
153
|
def self.with_friendly_errors
|
81
154
|
yield
|
82
155
|
rescue Kitchen::InstanceFailure => e
|
@@ -93,6 +166,13 @@ module Kitchen
|
|
93
166
|
|
94
167
|
private
|
95
168
|
|
169
|
+
# Writes an array of lines to the common Kitchen logger's file device at the
|
170
|
+
# given severity level. If the Kitchen logger is set to debug severity, then
|
171
|
+
# the array of lines will also be written to the console output.
|
172
|
+
#
|
173
|
+
# @param level [Symbol,String] the desired log level
|
174
|
+
# @param lines [Array<String>] an array of strings to log
|
175
|
+
# @api private
|
96
176
|
def self.file_log(level, lines)
|
97
177
|
Array(lines).each do |line|
|
98
178
|
if Kitchen.logger.debug?
|
@@ -103,16 +183,31 @@ module Kitchen
|
|
103
183
|
end
|
104
184
|
end
|
105
185
|
|
186
|
+
# Writes an array of lines to the `STDERR` device.
|
187
|
+
#
|
188
|
+
# @param lines [Array<String>] an array of strings to log
|
189
|
+
# @api private
|
106
190
|
def self.stderr_log(lines)
|
107
|
-
Array(lines).each do |line|
|
108
|
-
|
191
|
+
Array(lines).map { |line| ">>>>>> #{line}" }.each do |line|
|
192
|
+
line = Color.colorize(line, :red) if Kitchen.tty?
|
193
|
+
$stderr.puts(line)
|
109
194
|
end
|
110
195
|
end
|
111
196
|
|
197
|
+
# Writes an array of lines to the common Kitchen debugger with debug
|
198
|
+
# severity.
|
199
|
+
#
|
200
|
+
# @param lines [Array<String>] an array of strings to log
|
201
|
+
# @api private
|
112
202
|
def self.debug_log(lines)
|
113
203
|
Array(lines).each { |line| Kitchen.logger.debug(line) }
|
114
204
|
end
|
115
205
|
|
206
|
+
# Handles an instance failure exception.
|
207
|
+
#
|
208
|
+
# @param e [StandardError] an exception to handle
|
209
|
+
# @see Kitchen.with_friendly_errors
|
210
|
+
# @api private
|
116
211
|
def self.handle_instance_failure(e)
|
117
212
|
stderr_log(e.message.split(/\s{2,}/))
|
118
213
|
stderr_log(Error.formatted_exception(e.original))
|
@@ -120,6 +215,11 @@ module Kitchen
|
|
120
215
|
debug_log(Error.formatted_trace(e))
|
121
216
|
end
|
122
217
|
|
218
|
+
# Handles an unexpected failure exception.
|
219
|
+
#
|
220
|
+
# @param e [StandardError] an exception to handle
|
221
|
+
# @see Kitchen.with_friendly_errors
|
222
|
+
# @api private
|
123
223
|
def self.handle_error(e)
|
124
224
|
stderr_log(Error.formatted_exception(e))
|
125
225
|
stderr_log("Please see .kitchen/logs/kitchen.log for more details")
|
@@ -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 "thor/group"
|
20
|
+
require "thor/util"
|
21
21
|
|
22
22
|
module Kitchen
|
23
23
|
|
@@ -32,9 +32,12 @@ module Kitchen
|
|
32
32
|
|
33
33
|
argument :name, :type => :string
|
34
34
|
|
35
|
-
class_option :license,
|
35
|
+
class_option :license,
|
36
|
+
:aliases => "-l",
|
37
|
+
:default => "apachev2",
|
36
38
|
:desc => "License type for gem (apachev2, mit, lgplv3, reserved)"
|
37
39
|
|
40
|
+
# Invoke the command.
|
38
41
|
def create
|
39
42
|
self.class.source_root(Kitchen.source_root.join("templates", "driver"))
|
40
43
|
|
@@ -45,6 +48,9 @@ module Kitchen
|
|
45
48
|
|
46
49
|
private
|
47
50
|
|
51
|
+
# Creates top-level project files.
|
52
|
+
#
|
53
|
+
# @api private
|
48
54
|
def create_core_files
|
49
55
|
empty_directory(target_dir)
|
50
56
|
|
@@ -60,6 +66,9 @@ module Kitchen
|
|
60
66
|
create_file(File.join(target_dir, ".cane"))
|
61
67
|
end
|
62
68
|
|
69
|
+
# Creates source code files.
|
70
|
+
#
|
71
|
+
# @api private
|
63
72
|
def create_source_files
|
64
73
|
empty_directory(File.join(target_dir, "lib/kitchen/driver"))
|
65
74
|
|
@@ -73,6 +82,9 @@ module Kitchen
|
|
73
82
|
)
|
74
83
|
end
|
75
84
|
|
85
|
+
# Initialize a git repository.
|
86
|
+
#
|
87
|
+
# @api private
|
76
88
|
def initialize_git
|
77
89
|
inside(target_dir) do
|
78
90
|
run("git init")
|
@@ -80,14 +92,24 @@ module Kitchen
|
|
80
92
|
end
|
81
93
|
end
|
82
94
|
|
95
|
+
# Render an ERb template to a destination file.
|
96
|
+
#
|
97
|
+
# @param erb [String] path to an ERb file
|
98
|
+
# @param dest [String] destination path for the rendered template
|
99
|
+
# @api private
|
83
100
|
def create_template(erb, dest)
|
84
101
|
template(erb, File.join(target_dir, dest), config)
|
85
102
|
end
|
86
103
|
|
104
|
+
# @return [String] the path to the gem skeleton project
|
105
|
+
# @api private
|
87
106
|
def target_dir
|
88
107
|
File.join(Dir.pwd, "kitchen-#{name}")
|
89
108
|
end
|
90
109
|
|
110
|
+
# @return [Hash] a configuration hash which can be used by templates as
|
111
|
+
# context
|
112
|
+
# @api private
|
91
113
|
def config
|
92
114
|
@config ||= {
|
93
115
|
:name => name,
|
@@ -99,20 +121,28 @@ module Kitchen
|
|
99
121
|
:email => email,
|
100
122
|
:license => options[:license],
|
101
123
|
:license_string => license_string,
|
102
|
-
:year => Time.now.year
|
124
|
+
:year => Time.now.year
|
103
125
|
}
|
104
126
|
end
|
105
127
|
|
128
|
+
# @return [String] a default author name taken from git configuration if
|
129
|
+
# found
|
130
|
+
# @api private
|
106
131
|
def author
|
107
132
|
git_user_name = %x{git config user.name}.chomp
|
108
133
|
git_user_name.empty? ? "TODO: Write your name" : git_user_name
|
109
134
|
end
|
110
135
|
|
136
|
+
# @return [String] a default email address taken from git configuration
|
137
|
+
# if found
|
138
|
+
# @api private
|
111
139
|
def email
|
112
140
|
git_user_email = %x{git config user.email}.chomp
|
113
141
|
git_user_email.empty? ? "TODO: Write your email" : git_user_email
|
114
142
|
end
|
115
143
|
|
144
|
+
# @return [String] a rendered license string for a given license
|
145
|
+
# @api private
|
116
146
|
def license_string
|
117
147
|
case options[:license]
|
118
148
|
when "mit" then "MIT"
|
@@ -124,6 +154,8 @@ module Kitchen
|
|
124
154
|
end
|
125
155
|
end
|
126
156
|
|
157
|
+
# @return [String] the filename to use for the license file
|
158
|
+
# @api private
|
127
159
|
def license_filename
|
128
160
|
case options[:license]
|
129
161
|
when "mit" then "LICENSE.txt"
|
@@ -134,9 +166,11 @@ module Kitchen
|
|
134
166
|
end
|
135
167
|
end
|
136
168
|
|
169
|
+
# @return [String] the license comment/preamble
|
170
|
+
# @api private
|
137
171
|
def license_comment
|
138
172
|
@license_comment ||= IO.read(File.join(target_dir, license_filename)).
|
139
|
-
gsub(/^/,
|
173
|
+
gsub(/^/, "# ").gsub(/\s+$/, "")
|
140
174
|
end
|
141
175
|
end
|
142
176
|
end
|