test-kitchen 1.0.0.alpha.0 → 1.0.0.alpha.1
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/.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/lib/kitchen/driver.rb
CHANGED
@@ -26,11 +26,13 @@ module Kitchen
|
|
26
26
|
# @return [Driver::Base] a driver instance
|
27
27
|
# @raise [ClientError] if a driver instance could not be created
|
28
28
|
def self.for_plugin(plugin, config)
|
29
|
-
require
|
29
|
+
first_load = require("kitchen/driver/#{plugin}")
|
30
30
|
|
31
31
|
str_const = Util.to_camel_case(plugin)
|
32
32
|
klass = self.const_get(str_const)
|
33
|
-
klass.new(config)
|
33
|
+
object = klass.new(config)
|
34
|
+
object.verify_dependencies if first_load
|
35
|
+
object
|
34
36
|
rescue UserError
|
35
37
|
raise
|
36
38
|
rescue LoadError
|
data/lib/kitchen/driver/base.rb
CHANGED
@@ -20,6 +20,20 @@ module Kitchen
|
|
20
20
|
|
21
21
|
module Driver
|
22
22
|
|
23
|
+
# Value object to track a shell command that will be passed to Kernel.exec
|
24
|
+
# for execution.
|
25
|
+
#
|
26
|
+
# @author Fletcher Nichol <fnichol@nichol.ca>
|
27
|
+
class LoginCommand
|
28
|
+
|
29
|
+
attr_reader :cmd_array, :options
|
30
|
+
|
31
|
+
def initialize(cmd_array, options = {})
|
32
|
+
@cmd_array = cmd_array
|
33
|
+
@options = options
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
23
37
|
# Base class for a driver. A driver is responsible for carrying out the
|
24
38
|
# lifecycle activities of an instance, such as creating, converging, and
|
25
39
|
# destroying an instance.
|
@@ -84,16 +98,25 @@ module Kitchen
|
|
84
98
|
# @raise [ActionFailed] if the action could not be completed
|
85
99
|
def destroy(state) ; end
|
86
100
|
|
87
|
-
# Returns the shell command
|
101
|
+
# Returns the shell command that will log into an instance.
|
88
102
|
#
|
89
103
|
# @param state [Hash] mutable instance and driver state
|
90
|
-
# @return [
|
91
|
-
# fork/exec
|
104
|
+
# @return [LoginCommand] an object containing the array of command line
|
105
|
+
# tokens and exec options to be used in a fork/exec
|
92
106
|
# @raise [ActionFailed] if the action could not be completed
|
93
107
|
def login_command(state)
|
94
108
|
raise ActionFailed, "Remote login is not supported in this driver."
|
95
109
|
end
|
96
110
|
|
111
|
+
# Performs whatever tests that may be required to ensure that this driver
|
112
|
+
# will be able to function in the current environment. This may involve
|
113
|
+
# checking for the presence of certain directories, software installed,
|
114
|
+
# etc.
|
115
|
+
#
|
116
|
+
# @raise [UserError] if the driver will not be able to perform or if a
|
117
|
+
# documented dependency is missing from the system
|
118
|
+
def verify_dependencies ; end
|
119
|
+
|
97
120
|
protected
|
98
121
|
|
99
122
|
attr_reader :config, :instance
|
@@ -102,7 +125,7 @@ module Kitchen
|
|
102
125
|
map(&:to_sym).freeze
|
103
126
|
|
104
127
|
def logger
|
105
|
-
instance.logger
|
128
|
+
instance ? instance.logger : Kitchen.logger
|
106
129
|
end
|
107
130
|
|
108
131
|
def puts(msg)
|
@@ -113,11 +136,12 @@ module Kitchen
|
|
113
136
|
info(msg)
|
114
137
|
end
|
115
138
|
|
116
|
-
def run_command(cmd,
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
139
|
+
def run_command(cmd, options = {})
|
140
|
+
base_options = {
|
141
|
+
:use_sudo => config[:use_sudo],
|
142
|
+
:log_subject => Util.to_snake_case(self.class.to_s)
|
143
|
+
}.merge(options)
|
144
|
+
super(cmd, base_options)
|
121
145
|
end
|
122
146
|
|
123
147
|
def kb_setup_cmd
|
@@ -71,7 +71,7 @@ module Kitchen
|
|
71
71
|
args += %W{ -i #{config[:ssh_key]}} if config[:ssh_key]
|
72
72
|
args += %W{ #{config[:username]}@#{state[:hostname]}}
|
73
73
|
|
74
|
-
["ssh", *args]
|
74
|
+
Driver::LoginCommand.new(["ssh", *args])
|
75
75
|
end
|
76
76
|
|
77
77
|
protected
|
@@ -91,9 +91,9 @@ module Kitchen
|
|
91
91
|
end
|
92
92
|
|
93
93
|
def install_omnibus(ssh_args)
|
94
|
-
flag = config[:require_chef_omnibus]
|
94
|
+
flag = config[:require_chef_omnibus]
|
95
95
|
version = if flag.is_a?(String) && flag != "latest"
|
96
|
-
"-s -- -v #{flag}"
|
96
|
+
"-s -- -v #{flag.downcase}"
|
97
97
|
else
|
98
98
|
""
|
99
99
|
end
|
@@ -0,0 +1,196 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# Author:: Fletcher Nichol (<fnichol@nichol.ca>)
|
4
|
+
#
|
5
|
+
# Copyright (C) 2013, Fletcher Nichol
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
|
19
|
+
require 'thor/group'
|
20
|
+
|
21
|
+
module Kitchen
|
22
|
+
|
23
|
+
module Generator
|
24
|
+
|
25
|
+
# A project initialization generator, to help prepare a cookbook project
|
26
|
+
# for testing with Kitchen.
|
27
|
+
#
|
28
|
+
# @author Fletcher Nichol <fnichol@nichol.ca>
|
29
|
+
class Init < Thor::Group
|
30
|
+
|
31
|
+
include Thor::Actions
|
32
|
+
|
33
|
+
class_option :driver, :type => :array, :aliases => "-D",
|
34
|
+
:default => "kitchen-vagrant",
|
35
|
+
:desc => <<-D.gsub(/^\s+/, '').gsub(/\n/, ' ')
|
36
|
+
One or more Kitchen Driver gems to be installed or added to a
|
37
|
+
Gemfile
|
38
|
+
D
|
39
|
+
|
40
|
+
class_option :create_gemfile, :type => :boolean, :default => false,
|
41
|
+
:desc => <<-D.gsub(/^\s+/, '').gsub(/\n/, ' ')
|
42
|
+
Whether or not to create a Gemfile if one does not exist.
|
43
|
+
Default: false
|
44
|
+
D
|
45
|
+
|
46
|
+
def init
|
47
|
+
create_file ".kitchen.yml", default_yaml
|
48
|
+
|
49
|
+
rakedoc = <<-RAKE.gsub(/^ {10}/, '')
|
50
|
+
|
51
|
+
begin
|
52
|
+
require 'kitchen/rake_tasks'
|
53
|
+
Kitchen::RakeTasks.new
|
54
|
+
rescue LoadError
|
55
|
+
puts ">>>>> Kitchen gem not loaded, omitting tasks" unless ENV['CI']
|
56
|
+
end
|
57
|
+
RAKE
|
58
|
+
append_to_file("Rakefile", rakedoc) if init_rakefile?
|
59
|
+
|
60
|
+
thordoc = <<-THOR.gsub(/^ {10}/, '')
|
61
|
+
|
62
|
+
begin
|
63
|
+
require 'kitchen/thor_tasks'
|
64
|
+
Kitchen::ThorTasks.new
|
65
|
+
rescue LoadError
|
66
|
+
puts ">>>>> Kitchen gem not loaded, omitting tasks" unless ENV['CI']
|
67
|
+
end
|
68
|
+
THOR
|
69
|
+
append_to_file("Thorfile", thordoc) if init_thorfile?
|
70
|
+
|
71
|
+
empty_directory "test/integration/default" if init_test_dir?
|
72
|
+
append_to_gitignore(".kitchen/")
|
73
|
+
append_to_gitignore(".kitchen.local.yml")
|
74
|
+
prepare_gemfile if File.exists?("Gemfile") || options[:create_gemfile]
|
75
|
+
add_drivers
|
76
|
+
|
77
|
+
if @display_bundle_msg
|
78
|
+
say "You must run `bundle install' to fetch any new gems.", :red
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def default_yaml
|
85
|
+
cookbook_name = if File.exists?(File.expand_path('metadata.rb'))
|
86
|
+
MetadataChopper.extract('metadata.rb').first
|
87
|
+
else
|
88
|
+
nil
|
89
|
+
end
|
90
|
+
run_list = cookbook_name ? "recipe[#{cookbook_name}]" : nil
|
91
|
+
driver_plugin = Array(options[:driver]).first || 'dummy'
|
92
|
+
|
93
|
+
{ 'driver_plugin' => driver_plugin.sub(/^kitchen-/, ''),
|
94
|
+
'platforms' => platforms_hash,
|
95
|
+
'suites' => [
|
96
|
+
{ 'name' => 'default',
|
97
|
+
'run_list' => Array(run_list),
|
98
|
+
'attributes' => Hash.new
|
99
|
+
},
|
100
|
+
]
|
101
|
+
}.to_yaml
|
102
|
+
end
|
103
|
+
|
104
|
+
def platforms_hash
|
105
|
+
url_base = "https://opscode-vm.s3.amazonaws.com/vagrant/boxes"
|
106
|
+
platforms = [
|
107
|
+
{ :n => 'ubuntu', :vers => %w(12.04 10.04), :rl => "recipe[apt]" },
|
108
|
+
{ :n => 'centos', :vers => %w(6.3 5.8), :rl => "recipe[yum::epel]" },
|
109
|
+
]
|
110
|
+
platforms = platforms.map do |p|
|
111
|
+
p[:vers].map do |v|
|
112
|
+
{ 'name' => "#{p[:n]}-#{v}",
|
113
|
+
'driver_config' => {
|
114
|
+
'box' => "opscode-#{p[:n]}-#{v}",
|
115
|
+
'box_url' => "#{url_base}/opscode-#{p[:n]}-#{v}.box"
|
116
|
+
},
|
117
|
+
'run_list' => Array(p[:rl])
|
118
|
+
}
|
119
|
+
end
|
120
|
+
end.flatten
|
121
|
+
end
|
122
|
+
|
123
|
+
def init_rakefile?
|
124
|
+
File.exists?("Rakefile") &&
|
125
|
+
not_in_file?("Rakefile", %r{require 'kitchen/rake_tasks'})
|
126
|
+
end
|
127
|
+
|
128
|
+
def init_thorfile?
|
129
|
+
File.exists?("Thorfile") &&
|
130
|
+
not_in_file?("Thorfile", %r{require 'kitchen/thor_tasks'})
|
131
|
+
end
|
132
|
+
|
133
|
+
def init_test_dir?
|
134
|
+
Dir.glob("test/integration/*").select { |d| File.directory?(d) }.empty?
|
135
|
+
end
|
136
|
+
|
137
|
+
def append_to_gitignore(line)
|
138
|
+
create_file(".gitignore") unless File.exists?(".gitignore")
|
139
|
+
|
140
|
+
if IO.readlines(".gitignore").grep(%r{^#{line}}).empty?
|
141
|
+
append_to_file(".gitignore", "#{line}\n")
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def prepare_gemfile
|
146
|
+
create_gemfile_if_missing
|
147
|
+
add_gem_to_gemfile
|
148
|
+
end
|
149
|
+
|
150
|
+
def create_gemfile_if_missing
|
151
|
+
unless File.exists?("Gemfile")
|
152
|
+
create_file("Gemfile", %{source 'https://rubygems.org'\n\n})
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def add_gem_to_gemfile
|
157
|
+
if not_in_file?("Gemfile", %r{gem 'test-kitchen'})
|
158
|
+
append_to_file("Gemfile",
|
159
|
+
%{gem 'test-kitchen', :group => :integration\n})
|
160
|
+
@display_bundle_msg = true
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def add_drivers
|
165
|
+
return if options[:driver].nil? || options[:driver].empty?
|
166
|
+
display_warning = false
|
167
|
+
|
168
|
+
Array(options[:driver]).each do |driver_gem|
|
169
|
+
if File.exists?("Gemfile") || options[:create_gemfile]
|
170
|
+
add_driver_to_gemfile(driver_gem)
|
171
|
+
else
|
172
|
+
install_gem(driver_gem)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def add_driver_to_gemfile(driver_gem)
|
178
|
+
if not_in_file?("Gemfile", %r{gem '#{driver_gem}'})
|
179
|
+
append_to_file("Gemfile",
|
180
|
+
%{gem '#{driver_gem}', :group => :integration\n})
|
181
|
+
@display_bundle_msg = true
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def install_gem(driver_gem)
|
186
|
+
Bundler.with_clean_env do
|
187
|
+
run "gem install #{driver_gem}"
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def not_in_file?(filename, regexp)
|
192
|
+
IO.readlines(filename).grep(regexp).empty?
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# Author:: Fletcher Nichol (<fnichol@nichol.ca>)
|
4
|
+
#
|
5
|
+
# Copyright (C) 2013, Fletcher Nichol
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
|
19
|
+
require 'thor/group'
|
20
|
+
|
21
|
+
module Kitchen
|
22
|
+
|
23
|
+
module Generator
|
24
|
+
|
25
|
+
# A generator to create a new Kitchen Driver gem project.
|
26
|
+
#
|
27
|
+
# @author Fletcher Nichol <fnichol@nichol.ca>
|
28
|
+
class NewPlugin < Thor::Group
|
29
|
+
|
30
|
+
include Thor::Actions
|
31
|
+
|
32
|
+
argument :plugin_name
|
33
|
+
|
34
|
+
class_option :license, :aliases => "-l", :default => "apachev2",
|
35
|
+
:desc => "License type for gem (apachev2, mit, gplv3, gplv2, reserved)"
|
36
|
+
|
37
|
+
def new_plugin
|
38
|
+
if ! run("command -v bundle", :verbose => false)
|
39
|
+
die "Bundler must be installed and on your PATH: `gem install bundler'"
|
40
|
+
end
|
41
|
+
|
42
|
+
@plugin_name = plugin_name
|
43
|
+
@gem_name = "kitchen-#{plugin_name}"
|
44
|
+
@gemspec = "#{gem_name}.gemspec"
|
45
|
+
@klass_name = Util.to_camel_case(plugin_name)
|
46
|
+
@constant = Util.to_snake_case(plugin_name).upcase
|
47
|
+
@license = options[:license]
|
48
|
+
@author = %x{git config user.name}.chomp
|
49
|
+
@email = %x{git config user.email}.chomp
|
50
|
+
@year = Time.now.year
|
51
|
+
|
52
|
+
create_plugin
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
attr_reader :plugin_name, :gem_name, :gemspec, :klass_name,
|
58
|
+
:constant, :license, :author, :email, :year
|
59
|
+
|
60
|
+
def create_plugin
|
61
|
+
run("bundle gem #{gem_name}") unless File.directory?(gem_name)
|
62
|
+
|
63
|
+
inside(gem_name) do
|
64
|
+
update_gemspec
|
65
|
+
update_gemfile
|
66
|
+
update_rakefile
|
67
|
+
create_src_files
|
68
|
+
cleanup
|
69
|
+
create_license
|
70
|
+
add_git_files
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def update_gemspec
|
75
|
+
gsub_file(gemspec, %r{require '#{gem_name}/version'},
|
76
|
+
%{require 'kitchen/driver/#{plugin_name}_version.rb'})
|
77
|
+
gsub_file(gemspec, %r{Kitchen::#{klass_name}::VERSION},
|
78
|
+
%{Kitchen::Driver::#{constant}_VERSION})
|
79
|
+
gsub_file(gemspec, %r{(gem\.executables\s*) =.*$},
|
80
|
+
'\1 = []')
|
81
|
+
gsub_file(gemspec, %r{(gem\.description\s*) =.*$},
|
82
|
+
'\1 = "' + "Kitchen::Driver::#{klass_name} - " +
|
83
|
+
"A Kitchen Driver for #{klass_name}\"")
|
84
|
+
gsub_file(gemspec, %r{(gem\.summary\s*) =.*$},
|
85
|
+
'\1 = gem.description')
|
86
|
+
gsub_file(gemspec, %r{(gem\.homepage\s*) =.*$},
|
87
|
+
'\1 = "https://github.com/opscode/' +
|
88
|
+
"#{gem_name}/\"")
|
89
|
+
insert_into_file(gemspec,
|
90
|
+
"\n gem.add_dependency 'test-kitchen'\n", :before => "end\n")
|
91
|
+
insert_into_file(gemspec,
|
92
|
+
"\n gem.add_development_dependency 'cane'\n", :before => "end\n")
|
93
|
+
insert_into_file(gemspec,
|
94
|
+
" gem.add_development_dependency 'tailor'\n", :before => "end\n")
|
95
|
+
end
|
96
|
+
|
97
|
+
def update_gemfile
|
98
|
+
append_to_file("Gemfile", "\ngroup :test do\n gem 'rake'\nend\n")
|
99
|
+
end
|
100
|
+
|
101
|
+
def update_rakefile
|
102
|
+
append_to_file("Rakefile", <<-RAKEFILE.gsub(/^ {10}/, ''))
|
103
|
+
require 'cane/rake_task'
|
104
|
+
require 'tailor/rake_task'
|
105
|
+
|
106
|
+
desc "Run cane to check quality metrics"
|
107
|
+
Cane::RakeTask.new
|
108
|
+
|
109
|
+
Tailor::RakeTask.new
|
110
|
+
|
111
|
+
task :default => [ :cane, :tailor ]
|
112
|
+
RAKEFILE
|
113
|
+
end
|
114
|
+
|
115
|
+
def create_src_files
|
116
|
+
license_comments = rendered_license.gsub(/^/, '# ').gsub(/\s+$/, '')
|
117
|
+
|
118
|
+
empty_directory("lib/kitchen/driver")
|
119
|
+
create_template("plugin/version.rb",
|
120
|
+
"lib/kitchen/driver/#{plugin_name}_version.rb",
|
121
|
+
:klass_name => klass_name, :constant => constant,
|
122
|
+
:license => license_comments)
|
123
|
+
create_template("plugin/driver.rb",
|
124
|
+
"lib/kitchen/driver/#{plugin_name}.rb",
|
125
|
+
:klass_name => klass_name, :license => license_comments,
|
126
|
+
:author => author, :email => email)
|
127
|
+
end
|
128
|
+
|
129
|
+
def rendered_license
|
130
|
+
TemplateRenderer.render("plugin/license_#{license}",
|
131
|
+
:author => author, :email => email, :year => year)
|
132
|
+
end
|
133
|
+
|
134
|
+
def create_license
|
135
|
+
dest_file = case license
|
136
|
+
when "mit" then "LICENSE.txt"
|
137
|
+
when "apachev2", "reserved" then "LICENSE"
|
138
|
+
when "gplv2", "gplv3" then "COPYING"
|
139
|
+
else
|
140
|
+
raise ArgumentError, "No such license #{license}"
|
141
|
+
end
|
142
|
+
|
143
|
+
create_file(dest_file, rendered_license)
|
144
|
+
end
|
145
|
+
|
146
|
+
def cleanup
|
147
|
+
%W(LICENSE.txt lib/#{gem_name}/version.rb lib/#{gem_name}.rb).each do |f|
|
148
|
+
run("git rm -f #{f}") if File.exists?(f)
|
149
|
+
end
|
150
|
+
remove_dir("lib/#{gem_name}")
|
151
|
+
end
|
152
|
+
|
153
|
+
def add_git_files
|
154
|
+
run("git add .")
|
155
|
+
end
|
156
|
+
|
157
|
+
def create_template(template, destination, data = {})
|
158
|
+
create_file(destination, TemplateRenderer.render(template, data))
|
159
|
+
end
|
160
|
+
|
161
|
+
# Renders an ERB template with a hash of template variables.
|
162
|
+
#
|
163
|
+
# @author Fletcher Nichol <fnichol@nichol.ca>
|
164
|
+
class TemplateRenderer < OpenStruct
|
165
|
+
|
166
|
+
def self.render(template, data = {})
|
167
|
+
renderer = new(template, data)
|
168
|
+
yield renderer if block_given?
|
169
|
+
renderer.render
|
170
|
+
end
|
171
|
+
|
172
|
+
def initialize(template, data = {})
|
173
|
+
super()
|
174
|
+
data[:template] = template
|
175
|
+
data.each { |key, value| send("#{key}=", value) }
|
176
|
+
end
|
177
|
+
|
178
|
+
def render
|
179
|
+
ERB.new(IO.read(template_file)).result(binding)
|
180
|
+
end
|
181
|
+
|
182
|
+
private
|
183
|
+
|
184
|
+
def template_file
|
185
|
+
Kitchen.source_root.join("templates", "#{template}.erb").to_s
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|