test-kitchen 0.5.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.
- data/bin/kitchen +7 -0
- data/config/Cheffile +55 -0
- data/config/Kitchenfile +39 -0
- data/config/Vagrantfile +109 -0
- data/cookbooks/test-kitchen/attributes/default.rb +25 -0
- data/cookbooks/test-kitchen/libraries/helpers.rb +25 -0
- data/cookbooks/test-kitchen/metadata.rb +27 -0
- data/cookbooks/test-kitchen/recipes/chef.rb +19 -0
- data/cookbooks/test-kitchen/recipes/compat.rb +39 -0
- data/cookbooks/test-kitchen/recipes/default.rb +51 -0
- data/cookbooks/test-kitchen/recipes/erlang.rb +19 -0
- data/cookbooks/test-kitchen/recipes/ruby.rb +40 -0
- data/lib/test-kitchen.rb +34 -0
- data/lib/test-kitchen/cli.rb +267 -0
- data/lib/test-kitchen/cli/destroy.rb +42 -0
- data/lib/test-kitchen/cli/init.rb +37 -0
- data/lib/test-kitchen/cli/platform_list.rb +37 -0
- data/lib/test-kitchen/cli/project_info.rb +44 -0
- data/lib/test-kitchen/cli/ssh.rb +36 -0
- data/lib/test-kitchen/cli/status.rb +36 -0
- data/lib/test-kitchen/cli/test.rb +60 -0
- data/lib/test-kitchen/dsl.rb +59 -0
- data/lib/test-kitchen/environment.rb +164 -0
- data/lib/test-kitchen/platform.rb +63 -0
- data/lib/test-kitchen/project.rb +23 -0
- data/lib/test-kitchen/project/base.rb +159 -0
- data/lib/test-kitchen/project/cookbook.rb +87 -0
- data/lib/test-kitchen/project/cookbook_copy.rb +58 -0
- data/lib/test-kitchen/project/ruby.rb +37 -0
- data/lib/test-kitchen/project/supported_platforms.rb +75 -0
- data/lib/test-kitchen/runner.rb +20 -0
- data/lib/test-kitchen/runner/base.rb +144 -0
- data/lib/test-kitchen/runner/vagrant.rb +92 -0
- data/lib/test-kitchen/scaffold.rb +73 -0
- data/lib/test-kitchen/ui.rb +73 -0
- data/lib/test-kitchen/version.rb +21 -0
- metadata +196 -0
data/lib/test-kitchen.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Seth Chisamore (<schisamo@opscode.com>)
|
3
|
+
# Copyright:: Copyright (c) 2012 Opscode, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'ripper'
|
20
|
+
require 'test-kitchen/cli'
|
21
|
+
require 'test-kitchen/dsl'
|
22
|
+
require 'test-kitchen/project'
|
23
|
+
require 'test-kitchen/runner'
|
24
|
+
require 'test-kitchen/version'
|
25
|
+
|
26
|
+
module TestKitchen
|
27
|
+
|
28
|
+
# The source root is the path to the root directory of
|
29
|
+
# the test-kitchen gem.
|
30
|
+
def self.source_root
|
31
|
+
@source_root ||= Pathname.new(File.expand_path('../../', __FILE__))
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,267 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Seth Chisamore (<schisamo@opscode.com>)
|
3
|
+
# Copyright:: Copyright (c) 2012 Opscode, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'test-kitchen/environment'
|
20
|
+
require 'test-kitchen/ui'
|
21
|
+
require 'mixlib/cli'
|
22
|
+
|
23
|
+
module TestKitchen
|
24
|
+
module CLI
|
25
|
+
class Kitchen
|
26
|
+
include Mixlib::CLI
|
27
|
+
|
28
|
+
option :platform,
|
29
|
+
:long => "--platform PLATFORM",
|
30
|
+
:description => "The platform to use. If not specified tests will be run against all platforms."
|
31
|
+
|
32
|
+
option :configuration,
|
33
|
+
:long => "--configuration CONFIG",
|
34
|
+
:description => "The project configuration to test. Defaults to all configurations."
|
35
|
+
|
36
|
+
option :runner,
|
37
|
+
:short => "-r RUNNER",
|
38
|
+
:long => "--runner RUNNER",
|
39
|
+
:description => "The underlying virtualization platform to test with."
|
40
|
+
|
41
|
+
option :teardown,
|
42
|
+
:boolean => true,
|
43
|
+
:default => false,
|
44
|
+
:long => "--teardown",
|
45
|
+
:description => "Teardown test nodes between runs."
|
46
|
+
|
47
|
+
option :help,
|
48
|
+
:short => "-h",
|
49
|
+
:long => "--help",
|
50
|
+
:description => "Show this message",
|
51
|
+
:on => :tail,
|
52
|
+
:boolean => true,
|
53
|
+
:show_options => true,
|
54
|
+
:exit => 0
|
55
|
+
|
56
|
+
attr_accessor :runner
|
57
|
+
attr_accessor :env
|
58
|
+
attr_accessor :ui
|
59
|
+
|
60
|
+
def command_help?
|
61
|
+
ARGV.last == '--help'
|
62
|
+
end
|
63
|
+
|
64
|
+
def scaffolding?
|
65
|
+
ARGV == ['init']
|
66
|
+
end
|
67
|
+
|
68
|
+
def run
|
69
|
+
validate_and_parse_options
|
70
|
+
quiet_traps
|
71
|
+
Kitchen.run(ARGV, options)
|
72
|
+
exit 0
|
73
|
+
end
|
74
|
+
|
75
|
+
def initialize(argv=[])
|
76
|
+
$stdout.sync = true
|
77
|
+
$stderr.sync = true
|
78
|
+
super()
|
79
|
+
parse_options(argv)
|
80
|
+
@ui = TestKitchen::UI.new(STDOUT, STDERR, STDIN, {})
|
81
|
+
|
82
|
+
# TODO: Move this out of the constructor
|
83
|
+
load_environment unless command_help? || scaffolding?
|
84
|
+
end
|
85
|
+
|
86
|
+
def load_environment
|
87
|
+
@env = TestKitchen::Environment.new(:ui => @ui).tap{|e| e.load!}
|
88
|
+
end
|
89
|
+
|
90
|
+
def runner
|
91
|
+
@runner ||= begin
|
92
|
+
# CLI option takes precedence, then project
|
93
|
+
runner_name = config[:runner] || env.project.runner || 'vagrant'
|
94
|
+
runner_class = TestKitchen::Runner.targets[runner_name]
|
95
|
+
runner = runner_class.new(env, config)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Class Methods
|
100
|
+
|
101
|
+
def self.run(argv, options={})
|
102
|
+
load_commands
|
103
|
+
subcommand_class = Kitchen.subcommand_class_from(argv)
|
104
|
+
subcommand_class.options = options.merge!(subcommand_class.options)
|
105
|
+
|
106
|
+
instance = subcommand_class.new(ARGV)
|
107
|
+
instance.run
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.load_commands
|
111
|
+
@commands_loaded ||= begin
|
112
|
+
if subcommand_files = Dir[File.join(TestKitchen.source_root, 'lib', 'test-kitchen', 'cli', '*.rb')]
|
113
|
+
subcommand_files.each { |subcommand| Kernel.load subcommand.to_s }
|
114
|
+
end
|
115
|
+
true
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
NO_COMMAND_GIVEN = "You need to pass a sub-command (e.g., kitchen SUB-COMMAND)\n"
|
120
|
+
|
121
|
+
# BEGIN CARGO CULT FROM Chef::Knife
|
122
|
+
def self.inherited(subclass)
|
123
|
+
unless subclass.unnamed?
|
124
|
+
subcommands[subclass.snake_case_name] = subclass
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.subcommands
|
129
|
+
@@subcommands ||= {}
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.subcommand_category
|
133
|
+
@category || snake_case_name.split('_').first unless unnamed?
|
134
|
+
end
|
135
|
+
|
136
|
+
def self.snake_case_name
|
137
|
+
convert_to_snake_case(name.split('::').last) unless unnamed?
|
138
|
+
end
|
139
|
+
|
140
|
+
def self.convert_to_snake_case(str, namespace=nil)
|
141
|
+
str = str.dup
|
142
|
+
str.sub!(/^#{namespace}(\:\:)?/, '') if namespace
|
143
|
+
str.gsub!(/[A-Z]/) {|s| "_" + s}
|
144
|
+
str.downcase!
|
145
|
+
str.sub!(/^\_/, "")
|
146
|
+
str
|
147
|
+
end
|
148
|
+
|
149
|
+
# Does this class have a name? (Classes created via Class.new don't)
|
150
|
+
def self.unnamed?
|
151
|
+
name.nil? || name.empty?
|
152
|
+
end
|
153
|
+
|
154
|
+
def self.subcommands_by_category
|
155
|
+
unless @subcommands_by_category
|
156
|
+
@subcommands_by_category = Hash.new { |hash, key| hash[key] = [] }
|
157
|
+
subcommands.each do |snake_cased, klass|
|
158
|
+
@subcommands_by_category[klass.subcommand_category] << snake_cased
|
159
|
+
end
|
160
|
+
end
|
161
|
+
@subcommands_by_category
|
162
|
+
end
|
163
|
+
|
164
|
+
# Print the list of subcommands knife knows about. If +preferred_category+
|
165
|
+
# is given, only subcommands in that category are shown
|
166
|
+
def self.list_commands(preferred_category=nil)
|
167
|
+
load_commands
|
168
|
+
|
169
|
+
category_desc = preferred_category ? preferred_category + " " : ''
|
170
|
+
$stdout.puts "Available #{category_desc}subcommands: (for details, kitchen SUB-COMMAND --help)\n\n"
|
171
|
+
|
172
|
+
if preferred_category && subcommands_by_category.key?(preferred_category)
|
173
|
+
commands_to_show = {preferred_category => subcommands_by_category[preferred_category]}
|
174
|
+
else
|
175
|
+
commands_to_show = subcommands_by_category
|
176
|
+
end
|
177
|
+
|
178
|
+
commands_to_show.sort.each do |category, commands|
|
179
|
+
next if category =~ /deprecated/i
|
180
|
+
$stdout.puts "** #{category.upcase} COMMANDS **"
|
181
|
+
commands.each do |command|
|
182
|
+
$stdout.puts subcommands[command].banner if subcommands[command]
|
183
|
+
end
|
184
|
+
$stdout.puts
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def self.subcommand_class_from(args)
|
189
|
+
command_words = args.select {|arg| arg =~ /^(([[:alnum:]])[[:alnum:]\_\-]+)$/ }
|
190
|
+
|
191
|
+
subcommand_class = nil
|
192
|
+
|
193
|
+
while ( !subcommand_class ) && ( !command_words.empty? )
|
194
|
+
snake_case_class_name = command_words.join("_")
|
195
|
+
unless subcommand_class = subcommands[snake_case_class_name]
|
196
|
+
command_words.pop
|
197
|
+
end
|
198
|
+
end
|
199
|
+
# see if we got the command as e.g., knife node-list
|
200
|
+
subcommand_class ||= subcommands[args.first.gsub('-', '_')]
|
201
|
+
subcommand_class || subcommand_not_found!(args)
|
202
|
+
end
|
203
|
+
|
204
|
+
# :nodoc:
|
205
|
+
# Error out and print usage. probably becuase the arguments given by the
|
206
|
+
# user could not be resolved to a subcommand.
|
207
|
+
def self.subcommand_not_found!(args)
|
208
|
+
$stderr.puts "Cannot find sub command for: '#{args.join(' ')}'"
|
209
|
+
list_commands
|
210
|
+
exit 10
|
211
|
+
end
|
212
|
+
# END CARGO CULT FROM Chef::Knife
|
213
|
+
|
214
|
+
private
|
215
|
+
|
216
|
+
# BEGIN CARGO CULT FROM Chef::Application::Knife
|
217
|
+
def quiet_traps
|
218
|
+
trap("TERM") do
|
219
|
+
exit 1
|
220
|
+
end
|
221
|
+
|
222
|
+
trap("INT") do
|
223
|
+
exit 2
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def validate_and_parse_options
|
228
|
+
# Checking ARGV validity *before* parse_options because parse_options
|
229
|
+
# mangles ARGV in some situations
|
230
|
+
if no_command_given?
|
231
|
+
print_help_and_exit(1, NO_COMMAND_GIVEN)
|
232
|
+
elsif no_subcommand_given?
|
233
|
+
if (want_help? || want_version?)
|
234
|
+
print_help_and_exit
|
235
|
+
else
|
236
|
+
print_help_and_exit(2, NO_COMMAND_GIVEN)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
def no_subcommand_given?
|
242
|
+
ARGV[0] =~ /^-/
|
243
|
+
end
|
244
|
+
|
245
|
+
def no_command_given?
|
246
|
+
ARGV.empty?
|
247
|
+
end
|
248
|
+
|
249
|
+
def want_help?
|
250
|
+
ARGV[0] =~ /^(--help|-h)$/
|
251
|
+
end
|
252
|
+
|
253
|
+
def want_version?
|
254
|
+
ARGV[0] =~ /^(--version|-v)$/
|
255
|
+
end
|
256
|
+
|
257
|
+
def print_help_and_exit(exitcode=1, fatal_message=nil)
|
258
|
+
$stderr.puts(fatal_message) if fatal_message
|
259
|
+
puts self.opt_parser
|
260
|
+
puts
|
261
|
+
TestKitchen::CLI::Kitchen.list_commands
|
262
|
+
exit exitcode
|
263
|
+
end
|
264
|
+
# END CARGO CULT FROM Chef::Application::Knife
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Seth Chisamore (<schisamo@opscode.com>)
|
3
|
+
# Copyright:: Copyright (c) 2012 Opscode, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'test-kitchen/cli'
|
20
|
+
require 'test-kitchen/runner'
|
21
|
+
|
22
|
+
module TestKitchen
|
23
|
+
module CLI
|
24
|
+
class Kitchen
|
25
|
+
class Destroy < Kitchen
|
26
|
+
|
27
|
+
banner "kitchen destroy (options)"
|
28
|
+
|
29
|
+
def run
|
30
|
+
options = {
|
31
|
+
:platform => config[:platform],
|
32
|
+
:configuration => config[:configuration]
|
33
|
+
}
|
34
|
+
|
35
|
+
runner = TestKitchen::Runner.targets['vagrant'].new(env, options)
|
36
|
+
runner.destroy
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Seth Chisamore (<schisamo@opscode.com>)
|
3
|
+
# Copyright:: Copyright (c) 2012 Opscode, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'test-kitchen/cli'
|
20
|
+
require 'test-kitchen/scaffold'
|
21
|
+
|
22
|
+
module TestKitchen
|
23
|
+
module CLI
|
24
|
+
class Kitchen
|
25
|
+
class Init < Kitchen
|
26
|
+
|
27
|
+
banner "kitchen init"
|
28
|
+
|
29
|
+
def run
|
30
|
+
scaffold = TestKitchen::Scaffold.new
|
31
|
+
scaffold.generate(Dir.pwd)
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Seth Chisamore (<schisamo@opscode.com>)
|
3
|
+
# Copyright:: Copyright (c) 2012 Opscode, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'test-kitchen/cli'
|
20
|
+
|
21
|
+
module TestKitchen
|
22
|
+
module CLI
|
23
|
+
class Kitchen
|
24
|
+
class PlatformList < Kitchen
|
25
|
+
|
26
|
+
banner "kitchen platform list (options)"
|
27
|
+
|
28
|
+
def run
|
29
|
+
env.platform_names.each do |platform|
|
30
|
+
ui.info " #{platform}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Seth Chisamore (<schisamo@opscode.com>)
|
3
|
+
# Copyright:: Copyright (c) 2012 Opscode, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'test-kitchen/cli'
|
20
|
+
|
21
|
+
module TestKitchen
|
22
|
+
module CLI
|
23
|
+
class Kitchen
|
24
|
+
class ProjectInfo < Kitchen
|
25
|
+
|
26
|
+
banner "kitchen project info (options)"
|
27
|
+
|
28
|
+
def run
|
29
|
+
ui.info("Project: ", :green, :bold)
|
30
|
+
ui.info(" #{env.project.name}\n")
|
31
|
+
ui.info("Configurations: ", :yellow)
|
32
|
+
if env.project.configurations.any?
|
33
|
+
env.project.configurations.each_key do |config_name|
|
34
|
+
ui.info(" #{config_name}")
|
35
|
+
end
|
36
|
+
else
|
37
|
+
ui.info(" default")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|