test-kitchen 1.0.0.alpha.0 → 1.0.0.alpha.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +9 -0
- data/CHANGELOG.md +35 -0
- data/Rakefile +11 -8
- data/features/kitchen_command.feature +15 -0
- data/features/kitchen_driver_discover_command.feature +19 -0
- data/features/kitchen_init_command.feature +110 -0
- data/features/step_definitions/gem_steps.rb +26 -0
- data/features/support/env.rb +26 -1
- data/lib/kitchen/cli.rb +88 -313
- data/lib/kitchen/driver.rb +4 -2
- data/lib/kitchen/driver/base.rb +33 -9
- data/lib/kitchen/driver/ssh_base.rb +3 -3
- data/lib/kitchen/generator/init.rb +196 -0
- data/lib/kitchen/generator/new_plugin.rb +190 -0
- data/lib/kitchen/instance.rb +6 -3
- data/lib/kitchen/loader/yaml.rb +1 -1
- data/lib/kitchen/shell_out.rb +38 -8
- data/lib/kitchen/state_file.rb +1 -1
- data/lib/kitchen/version.rb +1 -1
- data/spec/kitchen/config_spec.rb +2 -1
- data/spec/kitchen/state_file_spec.rb +1 -1
- metadata +13 -6
- data/features/cli.feature +0 -17
- data/features/cli_init.feature +0 -156
data/.travis.yml
CHANGED
@@ -2,9 +2,18 @@ language: ruby
|
|
2
2
|
|
3
3
|
rvm:
|
4
4
|
- 1.9.3
|
5
|
+
- 2.0.0
|
5
6
|
- jruby-19mode
|
6
7
|
- ruby-head
|
7
8
|
|
9
|
+
env:
|
10
|
+
- RUBYGEMS_VERSION=2.0.3
|
11
|
+
- RUBYGEMS_VERSION=1.8.25
|
12
|
+
|
13
|
+
before_install:
|
14
|
+
- gem update --system $RUBYGEMS_VERSION
|
15
|
+
- gem --version
|
16
|
+
|
8
17
|
matrix:
|
9
18
|
allow_failures:
|
10
19
|
- rvm: ruby-head
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
## 1.0.0.alpha.1 / 2013-03-22
|
2
|
+
|
3
|
+
### Bug fixes
|
4
|
+
|
5
|
+
* Support (and test) for Rubygems 2.0.x and 1.8.x. ([@fnichol][])
|
6
|
+
|
7
|
+
### New features
|
8
|
+
|
9
|
+
* Pull request [#71][]: Updates to `kitchen init` to be non-interactive (add `--driver` flag), add subcommand support, and introduce `kitchen driver discover`. ([@fnichol][])
|
10
|
+
* Add `Driver#verify_dependencies` to be invoked once when Driver is loaded. ([@fnichol][])
|
11
|
+
|
12
|
+
### Improvements
|
13
|
+
|
14
|
+
* Pull request [#73][]: [Breaking] Modify `ShellOut#run_command` to take an options Hash. ([@fnichol][])
|
15
|
+
* Add :quiet option on `ShellOut#run_command`. ([@fnichol][])
|
16
|
+
* [Breaking] `Driver#login_command` returns a Driver::LoginCommand object. ([@fnichol][])
|
17
|
+
* Pull request [#74][]: Switch driver alias (-d) to (-D) in Init generator ([@reset][])
|
18
|
+
* Pull request [#64][]: Make `require_chef_omnibus: true` safe. ([@mattray][])
|
19
|
+
* Pull request [#65][]: Fix for line length and style (tailor). ([@ChrisLundquist][])
|
20
|
+
|
21
|
+
|
22
|
+
## 1.0.0.alpha.0 / 2013-03-02
|
23
|
+
|
24
|
+
The initial release.
|
25
|
+
|
26
|
+
<!--- The following link definition list is generated by PimpMyChangelog --->
|
27
|
+
[#64]: https://github.com/opscode/test/issues/64
|
28
|
+
[#65]: https://github.com/opscode/test/issues/65
|
29
|
+
[#71]: https://github.com/opscode/test/issues/71
|
30
|
+
[#73]: https://github.com/opscode/test/issues/73
|
31
|
+
[#74]: https://github.com/opscode/test/issues/74
|
32
|
+
[@ChrisLundquist]: https://github.com/ChrisLundquist
|
33
|
+
[@fnichol]: https://github.com/fnichol
|
34
|
+
[@mattray]: https://github.com/mattray
|
35
|
+
[@reset]: https://github.com/reset
|
data/Rakefile
CHANGED
@@ -1,19 +1,26 @@
|
|
1
1
|
require 'bundler/gem_tasks'
|
2
2
|
require 'rake/testtask'
|
3
|
+
require 'cucumber'
|
4
|
+
require 'cucumber/rake/task'
|
3
5
|
|
4
|
-
Rake::TestTask.new do |t|
|
6
|
+
Rake::TestTask.new(:unit) do |t|
|
5
7
|
t.libs.push "lib"
|
6
8
|
t.test_files = FileList['spec/**/*_spec.rb']
|
7
9
|
t.verbose = true
|
8
10
|
end
|
9
11
|
|
12
|
+
Cucumber::Rake::Task.new(:features) do |t|
|
13
|
+
t.cucumber_opts = ['features', '-x', '--format progress']
|
14
|
+
end
|
15
|
+
|
16
|
+
desc "Run all test suites"
|
17
|
+
task :test => [:unit, :features]
|
18
|
+
|
10
19
|
task :default => [:test]
|
11
20
|
|
12
21
|
unless RUBY_ENGINE == 'jruby'
|
13
22
|
require 'cane/rake_task'
|
14
23
|
require 'tailor/rake_task'
|
15
|
-
require 'cucumber'
|
16
|
-
require 'cucumber/rake/task'
|
17
24
|
|
18
25
|
desc "Run cane to check quality metrics"
|
19
26
|
Cane::RakeTask.new do |cane|
|
@@ -51,11 +58,7 @@ unless RUBY_ENGINE == 'jruby'
|
|
51
58
|
end
|
52
59
|
end
|
53
60
|
|
54
|
-
|
55
|
-
t.cucumber_opts = ['features', '-x', '--format progress']
|
56
|
-
end
|
57
|
-
|
58
|
-
Rake::Task[:default].enhance [:cane, :features, :tailor]
|
61
|
+
Rake::Task[:default].enhance [:cane, :tailor]
|
59
62
|
end
|
60
63
|
|
61
64
|
desc "Display LOC stats"
|
@@ -0,0 +1,15 @@
|
|
1
|
+
Feature: A command line interface for Test Kitchen
|
2
|
+
In order to provide a quick and response development workflow
|
3
|
+
As a Test Kitchen user
|
4
|
+
I want a command line interface that has sane defaults and built in help
|
5
|
+
|
6
|
+
Scenario: Displaying help
|
7
|
+
When I run `kitchen help`
|
8
|
+
Then the exit status should be 0
|
9
|
+
And the output should contain "kitchen console"
|
10
|
+
And the output should contain "kitchen version"
|
11
|
+
|
12
|
+
Scenario: Displaying the version of Test Kitchen
|
13
|
+
When I run `kitchen version`
|
14
|
+
Then the exit status should be 0
|
15
|
+
And the output should contain "Test Kitchen version"
|
@@ -0,0 +1,19 @@
|
|
1
|
+
Feature: Search RubyGems to discover new Test Kitchen Driver gems
|
2
|
+
In order to periodically check for new/updated Kitchen drivers
|
3
|
+
As a Test Kitchen user
|
4
|
+
I want to run a command which returns candidate Kitchen drivers
|
5
|
+
|
6
|
+
Scenario: Displaying help
|
7
|
+
When I run `kitchen help driver discover`
|
8
|
+
Then the output should contain:
|
9
|
+
"""
|
10
|
+
Usage:
|
11
|
+
kitchen driver discover
|
12
|
+
"""
|
13
|
+
And the exit status should be 0
|
14
|
+
|
15
|
+
Scenario: Running driver discover returns live results
|
16
|
+
When I run `kitchen driver discover`
|
17
|
+
Then the exit status should be 0
|
18
|
+
And the output should contain "kitchen-vagrant"
|
19
|
+
And the output should contain "kitchen-bluebox"
|
@@ -0,0 +1,110 @@
|
|
1
|
+
Feature: Add Test Kitchen support to an existing project
|
2
|
+
In order to add Test Kitchen to a project with minimal effort
|
3
|
+
As an operator
|
4
|
+
I want to run a command to initialize my project
|
5
|
+
|
6
|
+
Scenario: Displaying help
|
7
|
+
When I run `kitchen help init`
|
8
|
+
Then the output should contain:
|
9
|
+
"""
|
10
|
+
Usage:
|
11
|
+
kitchen init
|
12
|
+
"""
|
13
|
+
And the exit status should be 0
|
14
|
+
|
15
|
+
# Scenario: Running init with default values
|
16
|
+
# Given a sandboxed GEM_HOME directory named "kitchen-init"
|
17
|
+
# When I run `kitchen init`
|
18
|
+
# Then the exit status should be 0
|
19
|
+
# And a directory named ".kitchen" should exist
|
20
|
+
# And a directory named "test/integration/default" should exist
|
21
|
+
# And the file ".gitignore" should contain ".kitchen/"
|
22
|
+
# And the file ".gitignore" should contain ".kitchen.local.yml"
|
23
|
+
# And the file ".kitchen.yml" should contain "driver_plugin: vagrant"
|
24
|
+
# And a file named "Gemfile" should not exist
|
25
|
+
# And a file named "Rakefile" should not exist
|
26
|
+
# And a file named "Thorfile" should not exist
|
27
|
+
# And a gem named "kitchen-vagrant" is installed
|
28
|
+
|
29
|
+
Scenario: Running init that creates a Gemfile
|
30
|
+
When I successfully run `kitchen init --create-gemfile`
|
31
|
+
Then the file "Gemfile" should contain "https://rubygems.org"
|
32
|
+
And the file "Gemfile" should contain "gem 'test-kitchen'"
|
33
|
+
And the file "Gemfile" should contain "gem 'kitchen-vagrant'"
|
34
|
+
And the output should contain "You must run `bundle install'"
|
35
|
+
|
36
|
+
Scenario: Running init with an existing Gemfile appends to the Gemfile
|
37
|
+
Given an empty file named "Gemfile"
|
38
|
+
When I successfully run `kitchen init`
|
39
|
+
And the file "Gemfile" should contain "gem 'test-kitchen'"
|
40
|
+
And the file "Gemfile" should contain "gem 'kitchen-vagrant'"
|
41
|
+
And the output should contain "You must run `bundle install'"
|
42
|
+
|
43
|
+
Scenario: Running init with multiple drivers appends to the Gemfile
|
44
|
+
Given an empty file named "Gemfile"
|
45
|
+
When I successfully run `kitchen init --driver=kitchen-bluebox kitchen-wakka`
|
46
|
+
Then the file "Gemfile" should contain "gem 'kitchen-bluebox'"
|
47
|
+
And the file "Gemfile" should contain "gem 'kitchen-wakka'"
|
48
|
+
And the output should contain "You must run `bundle install'"
|
49
|
+
|
50
|
+
Scenario: Running init with multiple driver sets the plugin_driver to the
|
51
|
+
first driver given
|
52
|
+
Given an empty file named "Gemfile"
|
53
|
+
When I successfully run `kitchen init --driver=kitchen-bluebox kitchen-wakka`
|
54
|
+
Then the file ".kitchen.yml" should contain "driver_plugin: bluebox"
|
55
|
+
|
56
|
+
Scenario: Running init with no drivers sets the plugin_driver to the
|
57
|
+
dummy driver
|
58
|
+
Given an empty file named "Gemfile"
|
59
|
+
When I successfully run `kitchen init --no-driver`
|
60
|
+
Then the file ".kitchen.yml" should contain "driver_plugin: dummy"
|
61
|
+
|
62
|
+
Scenario: Running with a Rakefile file appends Kitchen tasks
|
63
|
+
Given an empty file named "Gemfile"
|
64
|
+
And an empty file named "Rakefile"
|
65
|
+
When I successfully run `kitchen init`
|
66
|
+
Then the file "Rakefile" should contain:
|
67
|
+
"""
|
68
|
+
begin
|
69
|
+
require 'kitchen/rake_tasks'
|
70
|
+
Kitchen::RakeTasks.new
|
71
|
+
rescue LoadError
|
72
|
+
puts ">>>>> Kitchen gem not loaded, omitting tasks" unless ENV['CI']
|
73
|
+
end
|
74
|
+
"""
|
75
|
+
|
76
|
+
Scenario: Running with a Thorfile file appends Kitchen tasks
|
77
|
+
Given an empty file named "Gemfile"
|
78
|
+
Given an empty file named "Thorfile"
|
79
|
+
When I successfully run `kitchen init`
|
80
|
+
Then the file "Thorfile" should contain:
|
81
|
+
"""
|
82
|
+
begin
|
83
|
+
require 'kitchen/thor_tasks'
|
84
|
+
Kitchen::ThorTasks.new
|
85
|
+
rescue LoadError
|
86
|
+
puts ">>>>> Kitchen gem not loaded, omitting tasks" unless ENV['CI']
|
87
|
+
end
|
88
|
+
"""
|
89
|
+
|
90
|
+
Scenario: Running init with a the name attribute metadata.rb sets a run list
|
91
|
+
Given an empty file named "Gemfile"
|
92
|
+
Given a file named "metadata.rb" with:
|
93
|
+
"""
|
94
|
+
name "ntp"
|
95
|
+
license "Apache 2.0"
|
96
|
+
description "Installs and configures ntp as a client or server"
|
97
|
+
version "0.1.0"
|
98
|
+
|
99
|
+
support "ubuntu"
|
100
|
+
support "centos"
|
101
|
+
"""
|
102
|
+
When I successfully run `kitchen init`
|
103
|
+
Then the file ".kitchen.yml" should contain:
|
104
|
+
"""
|
105
|
+
suites:
|
106
|
+
- name: default
|
107
|
+
run_list:
|
108
|
+
- recipe[ntp]
|
109
|
+
attributes: {}
|
110
|
+
"""
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'tmpdir'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
Given(/^a sandboxed GEM_HOME directory named "(.*?)"$/) do |name|
|
5
|
+
backup_envvar('GEM_HOME')
|
6
|
+
backup_envvar('GEM_PATH')
|
7
|
+
|
8
|
+
@aruba_timeout_seconds = 30
|
9
|
+
|
10
|
+
gem_home = Pathname.new(Dir.mktmpdir(name))
|
11
|
+
ENV['GEM_HOME'] = gem_home.to_s
|
12
|
+
ENV['GEM_PATH'] = [gem_home.to_s, ENV['GEM_PATH']].join(':')
|
13
|
+
@cleanup_dirs << gem_home
|
14
|
+
end
|
15
|
+
|
16
|
+
Then(/^a gem named "(.*?)" is installed with version "(.*?)"$/) do |name, version|
|
17
|
+
unbundlerize do
|
18
|
+
run_simple(unescape("gem list #{name} --version #{version} -i"), true, nil)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
Then(/^a gem named "(.*?)" is installed$/) do |name|
|
23
|
+
unbundlerize do
|
24
|
+
run_simple(unescape("gem list #{name} -i"), true, nil)
|
25
|
+
end
|
26
|
+
end
|
data/features/support/env.rb
CHANGED
@@ -4,11 +4,36 @@ require 'kitchen'
|
|
4
4
|
|
5
5
|
Before do
|
6
6
|
@aruba_timeout_seconds = 5
|
7
|
+
@cleanup_dirs = []
|
7
8
|
end
|
8
9
|
|
9
|
-
After do |s|
|
10
|
+
After do |s|
|
10
11
|
# Tell Cucumber to quit after this scenario is done - if it failed.
|
11
12
|
# This is useful to inspect the 'tmp/aruba' directory before any other
|
12
13
|
# steps are executed and clear it out.
|
13
14
|
Cucumber.wants_to_quit = true if s.failed?
|
15
|
+
|
16
|
+
# Restore environment variables to their original settings, if they have
|
17
|
+
# been saved off
|
18
|
+
ENV.keys.select { |key| key =~ /^_CUKE_/ }.each do |backup_key|
|
19
|
+
ENV[backup_key.sub(/^_CUKE_/, '')] = ENV.delete(backup_key)
|
20
|
+
end
|
21
|
+
|
22
|
+
@cleanup_dirs.each { |dir| FileUtils.rm_rf(dir) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def backup_envvar(key)
|
26
|
+
ENV["_CUKE_#{key}"] = ENV[key]
|
27
|
+
end
|
28
|
+
|
29
|
+
def restore_envvar(key)
|
30
|
+
ENV[key] = ENV.delete("_CUKE_#{key}")
|
31
|
+
end
|
32
|
+
|
33
|
+
def unbundlerize
|
34
|
+
keys = %w[BUNDLER_EDITOR BUNDLE_BIN_PATH BUNDLE_GEMFILE RUBYOPT]
|
35
|
+
|
36
|
+
keys.each { |key| backup_envvar(key) ; ENV.delete(key) }
|
37
|
+
yield
|
38
|
+
keys.each { |key| restore_envvar(key) }
|
14
39
|
end
|
data/lib/kitchen/cli.rb
CHANGED
@@ -22,6 +22,8 @@ require 'ostruct'
|
|
22
22
|
require 'thor'
|
23
23
|
|
24
24
|
require 'kitchen'
|
25
|
+
require 'kitchen/generator/init'
|
26
|
+
require 'kitchen/generator/new_plugin'
|
25
27
|
|
26
28
|
module Kitchen
|
27
29
|
|
@@ -120,7 +122,7 @@ module Kitchen
|
|
120
122
|
|
121
123
|
desc "version", "Print Kitchen's version information"
|
122
124
|
def version
|
123
|
-
say "Kitchen version #{Kitchen::VERSION}"
|
125
|
+
say "Test Kitchen version #{Kitchen::VERSION}"
|
124
126
|
end
|
125
127
|
map %w(-v --version) => :version
|
126
128
|
|
@@ -134,18 +136,91 @@ module Kitchen
|
|
134
136
|
exit 1
|
135
137
|
end
|
136
138
|
|
137
|
-
|
138
|
-
|
139
|
-
|
139
|
+
register Kitchen::Generator::Init, "init",
|
140
|
+
"init", "Adds some configuration to your cookbook so Kitchen can rock"
|
141
|
+
long_desc <<-D, :for => "init"
|
142
|
+
Init will add Test Kitchen support to an existing project for
|
143
|
+
convergence integration testing. A default .kitchen.yml file (which is
|
144
|
+
intended to be customized) is created in the project's root directory
|
145
|
+
and one or more gems will be added to the project's Gemfile.
|
146
|
+
D
|
147
|
+
tasks["init"].options = Kitchen::Generator::Init.class_options
|
148
|
+
|
149
|
+
# Thor class for kitchen driver commands.
|
150
|
+
#
|
151
|
+
# @author Fletcher Nichol <fnichol@nichol.ca>
|
152
|
+
class Driver < Thor
|
153
|
+
|
154
|
+
register Kitchen::Generator::NewPlugin, "create",
|
155
|
+
"create [NAME]", "Create a new Kitchen Driver gem project"
|
156
|
+
long_desc <<-D, :for => "create"
|
157
|
+
Create will generate a project scaffold for a brand new Test Kitchen
|
158
|
+
Driver RubyGem. For example:
|
159
|
+
|
160
|
+
> kitchen driver create foobar
|
161
|
+
|
162
|
+
will create a project scaffold for a RubyGem called `kitchen-foobar'.
|
163
|
+
D
|
164
|
+
tasks["create"].options = Kitchen::Generator::NewPlugin.class_options
|
165
|
+
|
166
|
+
desc "discover", "Discover Test Kitchen drivers published on RubyGems"
|
167
|
+
long_desc <<-D
|
168
|
+
Discover will perform a search aginst the RubyGems service for any
|
169
|
+
published gems of the form: "kitchen-*". Note that it it cannot be
|
170
|
+
guarenteed that every result is a driver, but chances are good most
|
171
|
+
relevant drivers will be returned.
|
172
|
+
D
|
173
|
+
def discover
|
174
|
+
specs = fetch_gem_specs.sort { |x, y| x[0] <=> y[0] }
|
175
|
+
specs = specs[0, 49].push(["...", "..."]) if specs.size > 49
|
176
|
+
specs = specs.unshift(["Gem Name", "Latest Stable Release"])
|
177
|
+
print_table(specs, :indent => 4)
|
178
|
+
end
|
179
|
+
|
180
|
+
def self.basename
|
181
|
+
super + " driver"
|
182
|
+
end
|
183
|
+
|
184
|
+
private
|
185
|
+
|
186
|
+
def fetch_gem_specs
|
187
|
+
require 'rubygems/spec_fetcher'
|
188
|
+
SafeYAML::OPTIONS[:suppress_warnings] = true
|
189
|
+
req = Gem::Requirement.default
|
190
|
+
dep = Gem::Deprecate.skip_during do
|
191
|
+
Gem::Dependency.new(/kitchen-/i, req)
|
192
|
+
end
|
193
|
+
fetcher = Gem::SpecFetcher.fetcher
|
194
|
+
|
195
|
+
specs = if fetcher.respond_to?(:find_matching)
|
196
|
+
fetch_gem_specs_pre_rubygems_2(fetcher, dep)
|
197
|
+
else
|
198
|
+
fetch_gem_specs_post_rubygems_2(fetcher, dep)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def fetch_gem_specs_pre_rubygems_2(fetcher, dep)
|
203
|
+
specs = fetcher.find_matching(dep, false, false, false)
|
204
|
+
specs.map { |t| t.first }.map { |t| t[0, 2] }
|
205
|
+
end
|
206
|
+
|
207
|
+
def fetch_gem_specs_post_rubygems_2(fetcher, dep)
|
208
|
+
specs = fetcher.spec_for_dependency(dep, false)
|
209
|
+
specs.first.map { |t| [t.first.name, t.first.version] }
|
210
|
+
end
|
140
211
|
end
|
141
212
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
213
|
+
register Kitchen::CLI::Driver, "driver",
|
214
|
+
"driver", "Driver subcommands"
|
215
|
+
|
216
|
+
no_tasks do
|
217
|
+
def invoke_task(task, *args)
|
218
|
+
if task.name == "help" && args.first.first == "driver"
|
219
|
+
Kitchen::CLI::Driver.task_help(shell, args.first.last)
|
220
|
+
else
|
221
|
+
super
|
222
|
+
end
|
223
|
+
end
|
149
224
|
end
|
150
225
|
|
151
226
|
private
|
@@ -224,317 +299,17 @@ module Kitchen
|
|
224
299
|
[
|
225
300
|
proc { |target_self, nest_level, pry|
|
226
301
|
["[#{pry.input_array.size}] ",
|
227
|
-
"
|
302
|
+
"kc(#{Pry.view_clip(target_self.class)})",
|
228
303
|
"#{":#{nest_level}" unless nest_level.zero?}> "
|
229
304
|
].join
|
230
305
|
},
|
231
306
|
proc { |target_self, nest_level, pry|
|
232
307
|
["[#{pry.input_array.size}] ",
|
233
|
-
"
|
308
|
+
"kc(#{Pry.view_clip(target_self.class)})",
|
234
309
|
"#{":#{nest_level}" unless nest_level.zero?}* "
|
235
310
|
].join
|
236
311
|
},
|
237
312
|
]
|
238
313
|
end
|
239
314
|
end
|
240
|
-
|
241
|
-
# A project initialization generator, to help prepare a cookbook project for
|
242
|
-
# testing with Kitchen.
|
243
|
-
#
|
244
|
-
# @author Fletcher Nichol <fnichol@nichol.ca>
|
245
|
-
class InitGenerator < Thor
|
246
|
-
|
247
|
-
include Thor::Actions
|
248
|
-
|
249
|
-
desc "init", "Adds some configuration to your cookbook so Kitchen can rock"
|
250
|
-
def init
|
251
|
-
create_file ".kitchen.yml", default_yaml
|
252
|
-
|
253
|
-
rakedoc = <<-RAKE.gsub(/^ {8}/, '')
|
254
|
-
|
255
|
-
begin
|
256
|
-
require 'kitchen/rake_tasks'
|
257
|
-
Kitchen::RakeTasks.new
|
258
|
-
rescue LoadError
|
259
|
-
puts ">>>>> Kitchen gem not loaded, omitting tasks" unless ENV['CI']
|
260
|
-
end
|
261
|
-
RAKE
|
262
|
-
append_to_file("Rakefile", rakedoc) if init_rakefile?
|
263
|
-
|
264
|
-
thordoc = <<-THOR.gsub(/^ {8}/, '')
|
265
|
-
|
266
|
-
begin
|
267
|
-
require 'kitchen/thor_tasks'
|
268
|
-
Kitchen::ThorTasks.new
|
269
|
-
rescue LoadError
|
270
|
-
puts ">>>>> Kitchen gem not loaded, omitting tasks" unless ENV['CI']
|
271
|
-
end
|
272
|
-
THOR
|
273
|
-
append_to_file("Thorfile", thordoc) if init_thorfile?
|
274
|
-
|
275
|
-
empty_directory "test/integration/default" if init_test_dir?
|
276
|
-
append_to_gitignore(".kitchen/")
|
277
|
-
append_to_gitignore(".kitchen.local.yml")
|
278
|
-
add_plugins
|
279
|
-
end
|
280
|
-
|
281
|
-
private
|
282
|
-
|
283
|
-
def default_yaml
|
284
|
-
url_base = "https://opscode-vm.s3.amazonaws.com/vagrant/boxes"
|
285
|
-
platforms = [
|
286
|
-
{ :n => 'ubuntu', :vers => %w(12.04 10.04), :rl => "recipe[apt]" },
|
287
|
-
{ :n => 'centos', :vers => %w(6.3 5.8), :rl => "recipe[yum::epel]" },
|
288
|
-
]
|
289
|
-
platforms = platforms.map do |p|
|
290
|
-
p[:vers].map do |v|
|
291
|
-
{ 'name' => "#{p[:n]}-#{v}",
|
292
|
-
'driver_config' => {
|
293
|
-
'box' => "opscode-#{p[:n]}-#{v}",
|
294
|
-
'box_url' => "#{url_base}/opscode-#{p[:n]}-#{v}.box"
|
295
|
-
},
|
296
|
-
'run_list' => Array(p[:rl])
|
297
|
-
}
|
298
|
-
end
|
299
|
-
end.flatten
|
300
|
-
cookbook_name = if File.exists?(File.expand_path('metadata.rb'))
|
301
|
-
MetadataChopper.extract('metadata.rb').first
|
302
|
-
else
|
303
|
-
nil
|
304
|
-
end
|
305
|
-
run_list = cookbook_name ? "recipe[#{cookbook_name}]" : nil
|
306
|
-
|
307
|
-
{ 'driver_plugin' => 'vagrant',
|
308
|
-
'platforms' => platforms,
|
309
|
-
'suites' => [
|
310
|
-
{ 'name' => 'default',
|
311
|
-
'run_list' => Array(run_list),
|
312
|
-
'attributes' => Hash.new
|
313
|
-
},
|
314
|
-
]
|
315
|
-
}.to_yaml
|
316
|
-
end
|
317
|
-
|
318
|
-
def init_rakefile?
|
319
|
-
File.exists?("Rakefile") &&
|
320
|
-
IO.readlines("Rakefile").grep(%r{require 'kitchen/rake_tasks'}).empty?
|
321
|
-
end
|
322
|
-
|
323
|
-
def init_thorfile?
|
324
|
-
File.exists?("Thorfile") &&
|
325
|
-
IO.readlines("Thorfile").grep(%r{require 'kitchen/thor_tasks'}).empty?
|
326
|
-
end
|
327
|
-
|
328
|
-
def init_test_dir?
|
329
|
-
Dir.glob("test/integration/*").select { |d| File.directory?(d) }.empty?
|
330
|
-
end
|
331
|
-
|
332
|
-
def append_to_gitignore(line)
|
333
|
-
create_file(".gitignore") unless File.exists?(".gitignore")
|
334
|
-
|
335
|
-
if IO.readlines(".gitignore").grep(%r{^#{line}}).empty?
|
336
|
-
append_to_file(".gitignore", "#{line}\n")
|
337
|
-
end
|
338
|
-
end
|
339
|
-
|
340
|
-
def add_plugins
|
341
|
-
prompt_add = "Add a Driver plugin to your Gemfile? (y/n)>"
|
342
|
-
prompt_name = "Enter gem name, `list', or `skip'>"
|
343
|
-
|
344
|
-
if yes?(prompt_add, :green)
|
345
|
-
list_plugins while (plugin = ask(prompt_name, :green)) == "list"
|
346
|
-
return if plugin == "skip"
|
347
|
-
begin
|
348
|
-
append_to_file(
|
349
|
-
"Gemfile", %{gem '#{plugin}', :group => :integration\n}
|
350
|
-
)
|
351
|
-
say "You must run `bundle install' to fetch any new gems.", :red
|
352
|
-
rescue Errno::ENOENT
|
353
|
-
warn %{You do not have an existing Gemfile}
|
354
|
-
warn %{Exiting...}
|
355
|
-
exit 1
|
356
|
-
end
|
357
|
-
end
|
358
|
-
end
|
359
|
-
|
360
|
-
def list_plugins
|
361
|
-
specs = fetch_gem_specs.map { |t| t.first }.map { |t| t[0, 2] }.
|
362
|
-
sort { |x, y| x[0] <=> y[0] }
|
363
|
-
specs = specs[0, 49].push(["...", "..."]) if specs.size > 49
|
364
|
-
specs = specs.unshift(["Gem Name", "Latest Stable Release"])
|
365
|
-
print_table(specs, :indent => 4)
|
366
|
-
end
|
367
|
-
|
368
|
-
def fetch_gem_specs
|
369
|
-
require 'rubygems/spec_fetcher'
|
370
|
-
req = Gem::Requirement.default
|
371
|
-
dep = Gem::Deprecate.skip_during { Gem::Dependency.new(/kitchen-/i, req) }
|
372
|
-
fetcher = Gem::SpecFetcher.fetcher
|
373
|
-
|
374
|
-
specs = fetcher.find_matching(dep, false, false, false)
|
375
|
-
end
|
376
|
-
end
|
377
|
-
|
378
|
-
# A generator to create a new Kitchen driver plugin.
|
379
|
-
#
|
380
|
-
# @author Fletcher Nichol <fnichol@nichol.ca>
|
381
|
-
class NewPluginGenerator < Thor
|
382
|
-
|
383
|
-
include Thor::Actions
|
384
|
-
|
385
|
-
desc "new_plugin [NAME]", "Generate a new Kitchen Driver plugin gem project"
|
386
|
-
method_option :license, :aliases => "-l", :default => "apachev2",
|
387
|
-
:desc => "Type of license for gem (apachev2, mit, gplv3, gplv2, reserved)"
|
388
|
-
def new_plugin(plugin_name)
|
389
|
-
if ! run("command -v bundle", :verbose => false)
|
390
|
-
die "Bundler must be installed and on your PATH: `gem install bundler'"
|
391
|
-
end
|
392
|
-
|
393
|
-
@plugin_name = plugin_name
|
394
|
-
@gem_name = "kitchen-#{plugin_name}"
|
395
|
-
@gemspec = "#{gem_name}.gemspec"
|
396
|
-
@klass_name = Util.to_camel_case(plugin_name)
|
397
|
-
@constant = Util.to_snake_case(plugin_name).upcase
|
398
|
-
@license = options[:license]
|
399
|
-
@author = %x{git config user.name}.chomp
|
400
|
-
@email = %x{git config user.email}.chomp
|
401
|
-
@year = Time.now.year
|
402
|
-
|
403
|
-
create_plugin
|
404
|
-
end
|
405
|
-
|
406
|
-
private
|
407
|
-
|
408
|
-
attr_reader :plugin_name, :gem_name, :gemspec, :klass_name,
|
409
|
-
:constant, :license, :author, :email, :year
|
410
|
-
|
411
|
-
def create_plugin
|
412
|
-
run("bundle gem #{gem_name}") unless File.directory?(gem_name)
|
413
|
-
|
414
|
-
inside(gem_name) do
|
415
|
-
update_gemspec
|
416
|
-
update_gemfile
|
417
|
-
update_rakefile
|
418
|
-
create_src_files
|
419
|
-
cleanup
|
420
|
-
create_license
|
421
|
-
add_git_files
|
422
|
-
end
|
423
|
-
end
|
424
|
-
|
425
|
-
def update_gemspec
|
426
|
-
gsub_file(gemspec, %r{require '#{gem_name}/version'},
|
427
|
-
%{require 'kitchen/driver/#{plugin_name}_version.rb'})
|
428
|
-
gsub_file(gemspec, %r{Kitchen::#{klass_name}::VERSION},
|
429
|
-
%{Kitchen::Driver::#{constant}_VERSION})
|
430
|
-
gsub_file(gemspec, %r{(gem\.executables\s*) =.*$},
|
431
|
-
'\1 = []')
|
432
|
-
gsub_file(gemspec, %r{(gem\.description\s*) =.*$},
|
433
|
-
'\1 = "' + "Kitchen::Driver::#{klass_name} - " +
|
434
|
-
"A Kitchen Driver for #{klass_name}\"")
|
435
|
-
gsub_file(gemspec, %r{(gem\.summary\s*) =.*$},
|
436
|
-
'\1 = gem.description')
|
437
|
-
gsub_file(gemspec, %r{(gem\.homepage\s*) =.*$},
|
438
|
-
'\1 = "https://github.com/opscode/' +
|
439
|
-
"#{gem_name}/\"")
|
440
|
-
insert_into_file(gemspec,
|
441
|
-
"\n gem.add_dependency 'test-kitchen'\n", :before => "end\n")
|
442
|
-
insert_into_file(gemspec,
|
443
|
-
"\n gem.add_development_dependency 'cane'\n", :before => "end\n")
|
444
|
-
insert_into_file(gemspec,
|
445
|
-
" gem.add_development_dependency 'tailor'\n", :before => "end\n")
|
446
|
-
end
|
447
|
-
|
448
|
-
def update_gemfile
|
449
|
-
append_to_file("Gemfile", "\ngroup :test do\n gem 'rake'\nend\n")
|
450
|
-
end
|
451
|
-
|
452
|
-
def update_rakefile
|
453
|
-
append_to_file("Rakefile", <<-RAKEFILE.gsub(/^ {8}/, ''))
|
454
|
-
require 'cane/rake_task'
|
455
|
-
require 'tailor/rake_task'
|
456
|
-
|
457
|
-
desc "Run cane to check quality metrics"
|
458
|
-
Cane::RakeTask.new
|
459
|
-
|
460
|
-
Tailor::RakeTask.new
|
461
|
-
|
462
|
-
task :default => [ :cane, :tailor ]
|
463
|
-
RAKEFILE
|
464
|
-
end
|
465
|
-
|
466
|
-
def create_src_files
|
467
|
-
license_comments = rendered_license.gsub(/^/, '# ').gsub(/\s+$/, '')
|
468
|
-
|
469
|
-
empty_directory("lib/kitchen/driver")
|
470
|
-
create_template("plugin/version.rb",
|
471
|
-
"lib/kitchen/driver/#{plugin_name}_version.rb",
|
472
|
-
:klass_name => klass_name, :constant => constant,
|
473
|
-
:license => license_comments)
|
474
|
-
create_template("plugin/driver.rb",
|
475
|
-
"lib/kitchen/driver/#{plugin_name}.rb",
|
476
|
-
:klass_name => klass_name, :license => license_comments,
|
477
|
-
:author => author, :email => email)
|
478
|
-
end
|
479
|
-
|
480
|
-
def rendered_license
|
481
|
-
TemplateRenderer.render("plugin/license_#{license}",
|
482
|
-
:author => author, :email => email, :year => year)
|
483
|
-
end
|
484
|
-
|
485
|
-
def create_license
|
486
|
-
dest_file = case license
|
487
|
-
when "mit" then "LICENSE.txt"
|
488
|
-
when "apachev2", "reserved" then "LICENSE"
|
489
|
-
when "gplv2", "gplv3" then "COPYING"
|
490
|
-
else
|
491
|
-
raise ArgumentError, "No such license #{license}"
|
492
|
-
end
|
493
|
-
|
494
|
-
create_file(dest_file, rendered_license)
|
495
|
-
end
|
496
|
-
|
497
|
-
def cleanup
|
498
|
-
%W(LICENSE.txt lib/#{gem_name}/version.rb lib/#{gem_name}.rb).each do |f|
|
499
|
-
run("git rm -f #{f}") if File.exists?(f)
|
500
|
-
end
|
501
|
-
remove_dir("lib/#{gem_name}")
|
502
|
-
end
|
503
|
-
|
504
|
-
def add_git_files
|
505
|
-
run("git add .")
|
506
|
-
end
|
507
|
-
|
508
|
-
def create_template(template, destination, data = {})
|
509
|
-
create_file(destination, TemplateRenderer.render(template, data))
|
510
|
-
end
|
511
|
-
|
512
|
-
# Renders an ERB template with a hash of template variables.
|
513
|
-
#
|
514
|
-
# @author Fletcher Nichol <fnichol@nichol.ca>
|
515
|
-
class TemplateRenderer < OpenStruct
|
516
|
-
|
517
|
-
def self.render(template, data = {})
|
518
|
-
renderer = new(template, data)
|
519
|
-
yield renderer if block_given?
|
520
|
-
renderer.render
|
521
|
-
end
|
522
|
-
|
523
|
-
def initialize(template, data = {})
|
524
|
-
super()
|
525
|
-
data[:template] = template
|
526
|
-
data.each { |key, value| send("#{key}=", value) }
|
527
|
-
end
|
528
|
-
|
529
|
-
def render
|
530
|
-
ERB.new(IO.read(template_file)).result(binding)
|
531
|
-
end
|
532
|
-
|
533
|
-
private
|
534
|
-
|
535
|
-
def template_file
|
536
|
-
Kitchen.source_root.join("templates", "#{template}.erb").to_s
|
537
|
-
end
|
538
|
-
end
|
539
|
-
end
|
540
315
|
end
|