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