test-kitchen 1.1.1 → 1.2.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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.cane +5 -0
  3. data/.travis.yml +8 -3
  4. data/CHANGELOG.md +66 -0
  5. data/Guardfile +3 -1
  6. data/README.md +3 -1
  7. data/Rakefile +1 -16
  8. data/features/kitchen_command.feature +1 -0
  9. data/features/kitchen_driver_create_command.feature +1 -0
  10. data/features/kitchen_driver_discover_command.feature +1 -0
  11. data/features/kitchen_help_command.feature +16 -0
  12. data/features/kitchen_init_command.feature +2 -0
  13. data/features/kitchen_list_command.feature +42 -0
  14. data/features/support/env.rb +25 -0
  15. data/lib/kitchen.rb +0 -1
  16. data/lib/kitchen/busser.rb +2 -2
  17. data/lib/kitchen/cli.rb +80 -233
  18. data/lib/kitchen/command.rb +117 -0
  19. data/lib/kitchen/command/action.rb +44 -0
  20. data/lib/kitchen/command/console.rb +51 -0
  21. data/lib/kitchen/command/diagnose.rb +51 -0
  22. data/lib/kitchen/command/driver_discover.rb +72 -0
  23. data/lib/kitchen/command/list.rb +86 -0
  24. data/lib/kitchen/command/login.rb +42 -0
  25. data/lib/kitchen/command/sink.rb +53 -0
  26. data/lib/kitchen/command/test.rb +50 -0
  27. data/lib/kitchen/driver/ssh_base.rb +2 -1
  28. data/lib/kitchen/loader/yaml.rb +67 -29
  29. data/lib/kitchen/provisioner/base.rb +67 -4
  30. data/lib/kitchen/provisioner/chef_base.rb +50 -65
  31. data/lib/kitchen/provisioner/chef_solo.rb +3 -2
  32. data/lib/kitchen/provisioner/chef_zero.rb +11 -9
  33. data/lib/kitchen/provisioner/shell.rb +88 -0
  34. data/lib/kitchen/state_file.rb +7 -2
  35. data/lib/kitchen/util.rb +1 -1
  36. data/lib/kitchen/version.rb +1 -1
  37. data/spec/kitchen/loader/yaml_spec.rb +327 -13
  38. data/spec/spec_helper.rb +2 -7
  39. data/support/chef-client-zero.rb +1 -0
  40. data/templates/driver/README.md.erb +1 -1
  41. data/test-kitchen.gemspec +2 -2
  42. metadata +59 -46
@@ -0,0 +1,53 @@
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 'kitchen/command'
20
+
21
+ module Kitchen
22
+
23
+ module Command
24
+
25
+ # Command to... include the sink.
26
+ #
27
+ # @author Seth Vargo <sethvargo@gmail.com>
28
+ class Sink < Kitchen::Command::Base
29
+
30
+ def call
31
+ puts [
32
+ "",
33
+ " ___ ",
34
+ " ' _ '. ",
35
+ " / /` `\\ \\ ",
36
+ " | | [__] ",
37
+ " | | {{ ",
38
+ " | | }} ",
39
+ " _ | | _ {{ ",
40
+ " ___________<_>_| |_<_>}}________ ",
41
+ " .=======^=(___)=^={{====. ",
42
+ " / .----------------}}---. \\ ",
43
+ " / / {{ \\ \\ ",
44
+ " / / }} \\ \\ ",
45
+ " ( '=========================' ) ",
46
+ " '-----------------------------' ",
47
+ " ", # necessary newline
48
+ ""
49
+ ].map(&:rstrip).join("\n")
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,50 @@
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 'kitchen/command'
20
+
21
+ require 'benchmark'
22
+
23
+ module Kitchen
24
+
25
+ module Command
26
+
27
+ # Command to test one or more instances.
28
+ #
29
+ # @author Fletcher Nichol <fnichol@nichol.ca>
30
+ class Test < Kitchen::Command::Base
31
+
32
+ include RunAction
33
+
34
+ def call
35
+ if ! %w{passing always never}.include?(options[:destroy])
36
+ raise ArgumentError, "Destroy mode must be passing, always, or never."
37
+ end
38
+
39
+ banner "Starting Kitchen (v#{Kitchen::VERSION})"
40
+ elapsed = Benchmark.measure do
41
+ destroy_mode = options[:destroy].to_sym
42
+ results = parse_subcommand(args.join('|'))
43
+
44
+ run_action(:test, results, destroy_mode)
45
+ end
46
+ banner "Kitchen is finished. #{Util.duration(elapsed.real)}"
47
+ end
48
+ end
49
+ end
50
+ end
@@ -37,7 +37,8 @@ module Kitchen
37
37
 
38
38
  def converge(state)
39
39
  provisioner = instance.provisioner
40
- sandbox_dirs = Dir.glob("#{provisioner.create_sandbox}/*")
40
+ provisioner.create_sandbox
41
+ sandbox_dirs = Dir.glob("#{provisioner.sandbox_path}/*")
41
42
 
42
43
  Kitchen::SSH.new(*build_ssh_args(state)) do |conn|
43
44
  run_remote(provisioner.install_command, conn)
@@ -18,7 +18,13 @@
18
18
 
19
19
  require 'erb'
20
20
  require 'vendor/hash_recursive_merge'
21
- require 'safe_yaml'
21
+
22
+ if RUBY_VERSION <= "1.9.3"
23
+ # ensure that Psych and not Syck is used for Ruby 1.9.2
24
+ require 'yaml'
25
+ YAML::ENGINE.yamler = 'psych'
26
+ end
27
+ require 'safe_yaml/load'
22
28
 
23
29
  module Kitchen
24
30
 
@@ -33,18 +39,27 @@ module Kitchen
33
39
  # @author Fletcher Nichol <fnichol@nichol.ca>
34
40
  class YAML
35
41
 
36
- attr_reader :config_file
37
-
38
42
  # Creates a new loader that can parse and load YAML files.
39
43
  #
40
- # @param config_file [String] path to Kitchen config YAML file
41
44
  # @param options [Hash] configuration for a new loader
45
+ # @option options [String] :project_config path to the Kitchen
46
+ # config YAML file (default: `./.kitchen.yml`)
47
+ # @option options [String] :local_config path to the Kitchen local
48
+ # config YAML file (default: `./.kitchen.local.yml`)
49
+ # @option options [String] :local_config path to the Kitchen global
50
+ # config YAML file (default: `$HOME/.kitchen/config.yml`)
42
51
  # @option options [String] :process_erb whether or not to process YAML
43
52
  # through an ERB processor (default: `true`)
44
53
  # @option options [String] :process_local whether or not to process a
45
54
  # local kitchen YAML file, if it exists (default: `true`)
46
- def initialize(config_file = nil, options = {})
47
- @config_file = File.expand_path(config_file || default_config_file)
55
+ def initialize(options = {})
56
+ @config_file =
57
+ File.expand_path(options[:project_config] || default_config_file)
58
+ @local_config_file =
59
+ File.expand_path(options[:local_config] || default_local_config_file)
60
+ @global_config_file =
61
+ File.expand_path(options[:global_config] || default_global_config_file)
62
+
48
63
  @process_erb = options.fetch(:process_erb, true)
49
64
  @process_local = options.fetch(:process_local, true)
50
65
  @process_global = options.fetch(:process_global, true)
@@ -67,26 +82,20 @@ module Kitchen
67
82
  # @return [Hash] a diagnostic hash
68
83
  def diagnose
69
84
  result = Hash.new
70
- result[:proces_erb] = @process_erb
85
+ result[:process_erb] = @process_erb
71
86
  result[:process_local] = @process_local
72
87
  result[:process_global] = @process_global
73
- if File.exists?(global_config_file)
74
- result[:global_config] =
75
- { :filename => global_config_file, :raw_data => global_yaml }
76
- end
77
- result[:project_config] =
78
- { :filename => config_file, :raw_data => yaml }
79
- if File.exists?(local_config_file)
80
- result[:local_config] =
81
- { :filename => local_config_file, :raw_data => local_yaml }
82
- end
83
- combined = begin ; combined_hash ; rescue => e ; failure_hash(e) ; end
84
- result[:combined_config] = { :raw_data => combined }
88
+ result[:global_config] = diagnose_component(:global_yaml, global_config_file)
89
+ result[:project_config] = diagnose_component(:yaml, config_file)
90
+ result[:local_config] = diagnose_component(:local_yaml, local_config_file)
91
+ result[:combined_config] = diagnose_component(:combined_hash)
85
92
  result
86
93
  end
87
94
 
88
95
  protected
89
96
 
97
+ attr_reader :config_file, :local_config_file, :global_config_file
98
+
90
99
  def default_config_file
91
100
  File.join(Dir.pwd, '.kitchen.yml')
92
101
  end
@@ -98,9 +107,6 @@ module Kitchen
98
107
  normalize(yaml)
99
108
  end
100
109
  @process_global ? y.rmerge(normalize(global_yaml)) : y
101
- rescue NoMethodError
102
- raise UserError, "Error merging #{File.basename(config_file)} and" +
103
- "#{File.basename(local_config_file)}"
104
110
  end
105
111
 
106
112
  def yaml
@@ -118,29 +124,52 @@ module Kitchen
118
124
  def yaml_string(file)
119
125
  string = read_file(file)
120
126
 
121
- @process_erb ? ERB.new(string).result : string
127
+ @process_erb ? process_erb(string, file) : string
128
+ end
129
+
130
+ def process_erb(string, file)
131
+ ERB.new(string).result
132
+ rescue => e
133
+ raise UserError, "Error parsing ERB content in #{file} " +
134
+ "(#{e.class}: #{e.message}).\n" +
135
+ "Please run `kitchen diagnose --no-instances --loader' to help " +
136
+ "debug your issue."
122
137
  end
123
138
 
124
139
  def read_file(file)
125
140
  File.exists?(file.to_s) ? IO.read(file) : ""
126
141
  end
127
142
 
128
- def local_config_file
143
+ def default_local_config_file
129
144
  config_file.sub(/(#{File.extname(config_file)})$/, '.local\1')
130
145
  end
131
146
 
132
- def global_config_file
147
+ def default_global_config_file
133
148
  File.join(File.expand_path(ENV["HOME"]), ".kitchen", "config.yml")
134
149
  end
135
150
 
136
- def failure_hash(e)
137
- {
151
+ def diagnose_component(component, file = nil)
152
+ return if file && !File.exists?(file)
153
+
154
+ hash = begin
155
+ send(component)
156
+ rescue => e
157
+ failure_hash(e, file)
158
+ end
159
+
160
+ { :filename => file, :raw_data => hash }
161
+ end
162
+
163
+ def failure_hash(e, file = nil)
164
+ result = {
138
165
  :error => {
139
166
  :exception => e.inspect,
140
167
  :message => e.message,
141
168
  :backtrace => e.backtrace
142
169
  }
143
170
  }
171
+ result[:error][:raw_file] = IO.read(file) unless file.nil?
172
+ result
144
173
  end
145
174
 
146
175
  def normalize(obj)
@@ -170,9 +199,18 @@ module Kitchen
170
199
  def parse_yaml_string(string, file_name)
171
200
  return Hash.new if string.nil? || string.empty?
172
201
 
173
- ::YAML.safe_load(string) || Hash.new
202
+ result = SafeYAML.load(string) || Hash.new
203
+ unless result.is_a?(Hash)
204
+ raise UserError, "Error parsing #{file_name} as YAML " +
205
+ "(Result of parse was not a Hash, but was a #{result.class}).\n" +
206
+ "Please run `kitchen diagnose --no-instances --loader' to help " +
207
+ "debug your issue."
208
+ end
209
+ result
174
210
  rescue SyntaxError, Psych::SyntaxError
175
- raise UserError, "Error parsing #{file_name}"
211
+ raise UserError, "Error parsing #{file_name} as YAML.\n" +
212
+ "Please run `kitchen diagnose --no-instances --loader' to help " +
213
+ "debug your issue."
176
214
  end
177
215
  end
178
216
  end
@@ -38,6 +38,11 @@ module Kitchen
38
38
  end
39
39
  end
40
40
 
41
+ def instance=(instance)
42
+ @instance = instance
43
+ expand_paths!
44
+ end
45
+
41
46
  # Returns the name of this driver, suitable for display in a CLI.
42
47
  #
43
48
  # @return [String] name of this driver
@@ -60,17 +65,33 @@ module Kitchen
60
65
  config.keys
61
66
  end
62
67
 
63
- def install_command ; end
64
-
65
68
  def init_command ; end
66
69
 
67
- def create_sandbox ; end
70
+ def install_command ; end
68
71
 
69
72
  def prepare_command ; end
70
73
 
71
74
  def run_command ; end
72
75
 
73
- def cleanup_sandbox ; end
76
+ def create_sandbox
77
+ @sandbox_path = Dir.mktmpdir("#{instance.name}-sandbox-")
78
+ File.chmod(0755, sandbox_path)
79
+ info("Preparing files for transfer")
80
+ debug("Creating local sandbox in #{sandbox_path}")
81
+ end
82
+
83
+ def sandbox_path
84
+ @sandbox_path || (raise ClientError, "Sandbox directory has not yet " +
85
+ "been created. Please run #{self.class}#create_sandox before " +
86
+ "trying to access the path.")
87
+ end
88
+
89
+ def cleanup_sandbox
90
+ return if sandbox_path.nil?
91
+
92
+ debug("Cleaning up local sandbox in #{sandbox_path}")
93
+ FileUtils.rmtree(sandbox_path)
94
+ end
74
95
 
75
96
  # Returns a Hash of configuration and other useful diagnostic information.
76
97
  #
@@ -81,10 +102,32 @@ module Kitchen
81
102
  result
82
103
  end
83
104
 
105
+ def calculate_path(path, type = :directory)
106
+ base = config[:test_base_path]
107
+ candidates = []
108
+ candidates << File.join(base, instance.suite.name, path)
109
+ candidates << File.join(base, path)
110
+ candidates << File.join(Dir.pwd, path)
111
+
112
+ candidates.find do |c|
113
+ type == :directory ? File.directory?(c) : File.file?(c)
114
+ end
115
+ end
116
+
84
117
  protected
85
118
 
86
119
  attr_reader :config
87
120
 
121
+ def expand_paths!
122
+ expanded_paths = LazyHash.new(self.class.expanded_paths, self).to_hash
123
+
124
+ expanded_paths.each do |key, should_expand|
125
+ if should_expand && !config[key].nil?
126
+ config[key] = File.expand_path(config[key], config[:kitchen_root])
127
+ end
128
+ end
129
+ end
130
+
88
131
  def logger
89
132
  instance ? instance.logger : Kitchen.logger
90
133
  end
@@ -111,8 +154,28 @@ module Kitchen
111
154
  defaults[attr] = block_given? ? block : value
112
155
  end
113
156
 
157
+ def self.expanded_paths
158
+ @expanded_paths ||= Hash.new.merge(super_expanded_paths)
159
+ end
160
+
161
+ def self.super_expanded_paths
162
+ klass = self.superclass
163
+
164
+ if klass.respond_to?(:expanded_paths)
165
+ klass.expanded_paths
166
+ else
167
+ Hash.new
168
+ end
169
+ end
170
+
171
+ def self.expand_path_for(attr, value = true, &block)
172
+ expanded_paths[attr] = block_given? ? block : value
173
+ end
174
+
114
175
  default_config :root_path, "/tmp/kitchen"
115
176
  default_config :sudo, true
177
+
178
+ expand_path_for :test_base_path
116
179
  end
117
180
  end
118
181
  end
@@ -34,7 +34,7 @@ module Kitchen
34
34
  class ChefBase < Base
35
35
 
36
36
  default_config :require_chef_omnibus, true
37
- default_config :chef_omnibus_url, "https://www.opscode.com/chef/install.sh"
37
+ default_config :chef_omnibus_url, "https://www.getchef.com/chef/install.sh"
38
38
  default_config :run_list, []
39
39
  default_config :attributes, {}
40
40
  default_config :cookbook_files_glob, %w[README.* metadata.{json,rb}
@@ -44,31 +44,37 @@ module Kitchen
44
44
  default_config :data_path do |provisioner|
45
45
  provisioner.calculate_path("data")
46
46
  end
47
+ expand_path_for :data_path
47
48
 
48
49
  default_config :data_bags_path do |provisioner|
49
50
  provisioner.calculate_path("data_bags")
50
51
  end
52
+ expand_path_for :data_bags_path
51
53
 
52
54
  default_config :environments_path do |provisioner|
53
55
  provisioner.calculate_path("environments")
54
56
  end
57
+ expand_path_for :environments_path
55
58
 
56
59
  default_config :nodes_path do |provisioner|
57
60
  provisioner.calculate_path("nodes")
58
61
  end
62
+ expand_path_for :nodes_path
59
63
 
60
64
  default_config :roles_path do |provisioner|
61
65
  provisioner.calculate_path("roles")
62
66
  end
67
+ expand_path_for :roles_path
63
68
 
64
- default_config :encrypted_data_bag_secret_key_path do |provisioner|
65
- provisioner.calculate_path("encrypted_data_bag_secret_key", :file)
69
+ default_config :clients_path do |provisioner|
70
+ provisioner.calculate_path("clients")
66
71
  end
72
+ expand_path_for :clients_path
67
73
 
68
- def instance=(instance)
69
- @instance = instance
70
- expand_paths!
74
+ default_config :encrypted_data_bag_secret_key_path do |provisioner|
75
+ provisioner.calculate_path("encrypted_data_bag_secret_key", :file)
71
76
  end
77
+ expand_path_for :encrypted_data_bag_secret_key_path
72
78
 
73
79
  def install_command
74
80
  return unless config[:require_chef_omnibus]
@@ -102,44 +108,27 @@ module Kitchen
102
108
  end
103
109
 
104
110
  def init_command
105
- dirs = %w{cookbooks data data_bags environments roles}.
111
+ dirs = %w{cookbooks data data_bags environments roles clients}.
106
112
  map { |dir| File.join(config[:root_path], dir) }.join(" ")
107
113
  "#{sudo('rm')} -rf #{dirs} ; mkdir -p #{config[:root_path]}"
108
114
  end
109
115
 
110
- def cleanup_sandbox
111
- return if tmpdir.nil?
112
-
113
- debug("Cleaning up local sandbox in #{tmpdir}")
114
- FileUtils.rmtree(tmpdir)
115
- end
116
-
117
- def calculate_path(path, type = :directory)
118
- base = config[:test_base_path]
119
- candidates = []
120
- candidates << File.join(base, instance.suite.name, path)
121
- candidates << File.join(base, path)
122
- candidates << File.join(Dir.pwd, path)
123
-
124
- candidates.find do |c|
125
- type == :directory ? File.directory?(c) : File.file?(c)
126
- end
116
+ def create_sandbox
117
+ super
118
+ prepare_json
119
+ prepare_cache
120
+ prepare_cookbooks
121
+ prepare_data
122
+ prepare_data_bags
123
+ prepare_environments
124
+ prepare_nodes
125
+ prepare_roles
126
+ prepare_clients
127
+ prepare_secret
127
128
  end
128
129
 
129
130
  protected
130
131
 
131
- attr_reader :tmpdir
132
-
133
- def expand_paths!
134
- paths = %w{test_base data data_bags encrypted_data_bag_secret_key
135
- environments nodes roles}
136
- paths.map{ |p| "#{p}_path".to_sym }.each do |key|
137
- unless config[key].nil?
138
- config[key] = File.expand_path(config[key], config[:kitchen_root])
139
- end
140
- end
141
- end
142
-
143
132
  def format_config_file(data)
144
133
  data.each.map { |attr, value|
145
134
  [attr, (value.is_a?(Array) ? value.to_s : %{"#{value}"})].join(" ")
@@ -168,29 +157,10 @@ module Kitchen
168
157
  }
169
158
  end
170
159
 
171
- def create_chef_sandbox
172
- @tmpdir = Dir.mktmpdir("#{instance.name}-sandbox-")
173
- File.chmod(0755, @tmpdir)
174
- info("Preparing files for transfer")
175
- debug("Creating local sandbox in #{tmpdir}")
176
-
177
- yield if block_given?
178
- prepare_json
179
- prepare_cache
180
- prepare_cookbooks
181
- prepare_data
182
- prepare_data_bags
183
- prepare_environments
184
- prepare_nodes
185
- prepare_roles
186
- prepare_secret
187
- tmpdir
188
- end
189
-
190
160
  def prepare_json
191
161
  dna = config[:attributes].merge({ :run_list => config[:run_list] })
192
162
 
193
- File.open(File.join(tmpdir, "dna.json"), "wb") do |file|
163
+ File.open(File.join(sandbox_path, "dna.json"), "wb") do |file|
194
164
  file.write(dna.to_json)
195
165
  end
196
166
  end
@@ -201,7 +171,7 @@ module Kitchen
201
171
  info("Preparing data")
202
172
  debug("Using data from #{data}")
203
173
 
204
- tmpdata_dir = File.join(tmpdir, "data")
174
+ tmpdata_dir = File.join(sandbox_path, "data")
205
175
  FileUtils.mkdir_p(tmpdata_dir)
206
176
  FileUtils.cp_r(Dir.glob("#{data}/*"), tmpdata_dir)
207
177
  end
@@ -212,7 +182,7 @@ module Kitchen
212
182
  info("Preparing data bags")
213
183
  debug("Using data bags from #{data_bags}")
214
184
 
215
- tmpbags_dir = File.join(tmpdir, "data_bags")
185
+ tmpbags_dir = File.join(sandbox_path, "data_bags")
216
186
  FileUtils.mkdir_p(tmpbags_dir)
217
187
  FileUtils.cp_r(Dir.glob("#{data_bags}/*"), tmpbags_dir)
218
188
  end
@@ -223,18 +193,29 @@ module Kitchen
223
193
  info("Preparing roles")
224
194
  debug("Using roles from #{roles}")
225
195
 
226
- tmproles_dir = File.join(tmpdir, "roles")
196
+ tmproles_dir = File.join(sandbox_path, "roles")
227
197
  FileUtils.mkdir_p(tmproles_dir)
228
198
  FileUtils.cp_r(Dir.glob("#{roles}/*"), tmproles_dir)
229
199
  end
230
200
 
201
+ def prepare_clients
202
+ return unless clients
203
+
204
+ info("Preparing clients")
205
+ debug("Using roles from #{clients}")
206
+
207
+ tmpclients_dir = File.join(sandbox_path, "clients")
208
+ FileUtils.mkdir_p(tmpclients_dir)
209
+ FileUtils.cp_r(Dir.glob("#{clients}/*"), tmpclients_dir)
210
+ end
211
+
231
212
  def prepare_nodes
232
213
  return unless nodes
233
214
 
234
215
  info("Preparing nodes")
235
216
  debug("Using nodes from #{nodes}")
236
217
 
237
- tmpnodes_dir = File.join(tmpdir, "nodes")
218
+ tmpnodes_dir = File.join(sandbox_path, "nodes")
238
219
  FileUtils.mkdir_p(tmpnodes_dir)
239
220
  FileUtils.cp_r(Dir.glob("#{nodes}/*"), tmpnodes_dir)
240
221
  end
@@ -245,7 +226,7 @@ module Kitchen
245
226
  info("Preparing environments")
246
227
  debug("Using environments from #{environments}")
247
228
 
248
- tmpenvs_dir = File.join(tmpdir, "environments")
229
+ tmpenvs_dir = File.join(sandbox_path, "environments")
249
230
  FileUtils.mkdir_p(tmpenvs_dir)
250
231
  FileUtils.cp_r(Dir.glob("#{environments}/*"), tmpenvs_dir)
251
232
  end
@@ -256,11 +237,11 @@ module Kitchen
256
237
  info("Preparing encrypted data bag secret")
257
238
  debug("Using secret from #{secret}")
258
239
 
259
- FileUtils.cp_r(secret, File.join(tmpdir, "encrypted_data_bag_secret"))
240
+ FileUtils.cp_r(secret, File.join(sandbox_path, "encrypted_data_bag_secret"))
260
241
  end
261
242
 
262
243
  def prepare_cache
263
- FileUtils.mkdir_p(File.join(tmpdir, "cache"))
244
+ FileUtils.mkdir_p(File.join(sandbox_path, "cache"))
264
245
  end
265
246
 
266
247
  def prepare_cookbooks
@@ -324,6 +305,10 @@ module Kitchen
324
305
  config[:roles_path]
325
306
  end
326
307
 
308
+ def clients
309
+ config[:clients_path]
310
+ end
311
+
327
312
  def nodes
328
313
  config[:nodes_path]
329
314
  end
@@ -341,11 +326,11 @@ module Kitchen
341
326
  end
342
327
 
343
328
  def tmpbooks_dir
344
- File.join(tmpdir, "cookbooks")
329
+ File.join(sandbox_path, "cookbooks")
345
330
  end
346
331
 
347
332
  def tmpsitebooks_dir
348
- File.join(tmpdir, "cookbooks")
333
+ File.join(sandbox_path, "cookbooks")
349
334
  end
350
335
 
351
336
  def cp_cookbooks