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
@@ -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 "kitchen/errors"
|
20
|
+
require "kitchen/logging"
|
21
21
|
|
22
22
|
module Kitchen
|
23
23
|
|
@@ -26,24 +26,36 @@ module Kitchen
|
|
26
26
|
module Chef
|
27
27
|
|
28
28
|
# Chef cookbook resolver that uses Librarian-Chef and a Cheffile to
|
29
|
-
# calculate
|
29
|
+
# calculate dependencies.
|
30
30
|
#
|
31
31
|
# @author Fletcher Nichol <fnichol@nichol.ca>
|
32
32
|
class Librarian
|
33
33
|
|
34
34
|
include Logging
|
35
35
|
|
36
|
-
|
36
|
+
# Creates a new cookbook resolver.
|
37
|
+
#
|
38
|
+
# @param cheffile [String] path to a Cheffile
|
39
|
+
# @param path [String] path in which to vendor the resulting
|
40
|
+
# cookbooks
|
41
|
+
# @param logger [Kitchen::Logger] a logger to use for output, defaults
|
42
|
+
# to `Kitchen.logger`
|
37
43
|
def initialize(cheffile, path, logger = Kitchen.logger)
|
38
44
|
@cheffile = cheffile
|
39
45
|
@path = path
|
40
46
|
@logger = logger
|
41
47
|
end
|
42
48
|
|
49
|
+
# Loads the library code required to use the resolver.
|
50
|
+
#
|
51
|
+
# @param logger [Kitchen::Logger] a logger to use for output, defaults
|
52
|
+
# to `Kitchen.logger`
|
43
53
|
def self.load!(logger = Kitchen.logger)
|
44
54
|
load_librarian!(logger)
|
45
55
|
end
|
46
56
|
|
57
|
+
# Performs the cookbook resolution and vendors the resulting cookbooks
|
58
|
+
# in the desired path.
|
47
59
|
def resolve
|
48
60
|
version = ::Librarian::Chef::VERSION
|
49
61
|
info("Resolving cookbook dependencies with Librarian-Chef #{version}...")
|
@@ -56,12 +68,29 @@ module Kitchen
|
|
56
68
|
::Librarian::Action::Install.new(env).run
|
57
69
|
end
|
58
70
|
|
59
|
-
|
71
|
+
private
|
72
|
+
|
73
|
+
# @return [String] path to a Cheffile
|
74
|
+
# @api private
|
75
|
+
attr_reader :cheffile
|
76
|
+
|
77
|
+
# @return [String] path in which to vendor the resulting cookbooks
|
78
|
+
# @api private
|
79
|
+
attr_reader :path
|
80
|
+
|
81
|
+
# @return [Kitchen::Logger] a logger to use for output
|
82
|
+
# @api private
|
83
|
+
attr_reader :logger
|
60
84
|
|
85
|
+
# Load the Librarian-specific libary code.
|
86
|
+
#
|
87
|
+
# @param logger [Kitchen::Logger] the logger to use
|
88
|
+
# @raise [UserError] if the library couldn't be loaded
|
89
|
+
# @api private
|
61
90
|
def self.load_librarian!(logger)
|
62
|
-
first_load = require
|
63
|
-
require
|
64
|
-
require
|
91
|
+
first_load = require "librarian/chef/environment"
|
92
|
+
require "librarian/action/resolve"
|
93
|
+
require "librarian/action/install"
|
65
94
|
|
66
95
|
version = ::Librarian::Chef::VERSION
|
67
96
|
if first_load
|
@@ -70,9 +99,9 @@ module Kitchen
|
|
70
99
|
logger.debug("Librarian-Chef #{version} previously loaded")
|
71
100
|
end
|
72
101
|
rescue LoadError => e
|
73
|
-
logger.fatal("The `librarian-chef' gem is missing and must be installed"
|
74
|
-
" or cannot be properly activated. Run"
|
75
|
-
" `gem install librarian-chef` or add the following to your"
|
102
|
+
logger.fatal("The `librarian-chef' gem is missing and must be installed" \
|
103
|
+
" or cannot be properly activated. Run" \
|
104
|
+
" `gem install librarian-chef` or add the following to your" \
|
76
105
|
" Gemfile if you are using Bundler: `gem 'librarian-chef'`.")
|
77
106
|
raise UserError,
|
78
107
|
"Could not load or activate Librarian-Chef (#{e.message})"
|
@@ -16,13 +16,13 @@
|
|
16
16
|
# See the License for the specific language governing permissions and
|
17
17
|
# limitations under the License.
|
18
18
|
|
19
|
-
require
|
20
|
-
require
|
21
|
-
require
|
19
|
+
require "fileutils"
|
20
|
+
require "pathname"
|
21
|
+
require "json"
|
22
22
|
|
23
|
-
require
|
24
|
-
require
|
25
|
-
require
|
23
|
+
require "kitchen/provisioner/chef/berkshelf"
|
24
|
+
require "kitchen/provisioner/chef/librarian"
|
25
|
+
require "kitchen/util"
|
26
26
|
|
27
27
|
module Kitchen
|
28
28
|
|
@@ -34,12 +34,17 @@ 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.
|
37
|
+
default_config :chef_omnibus_url, "https://www.chef.io/chef/install.sh"
|
38
|
+
default_config :chef_omnibus_root, "/opt/chef"
|
39
|
+
default_config :chef_omnibus_install_options, nil
|
38
40
|
default_config :run_list, []
|
39
41
|
default_config :attributes, {}
|
40
|
-
default_config :
|
42
|
+
default_config :log_file, nil
|
43
|
+
default_config :cookbook_files_glob, %w[
|
44
|
+
README.* metadata.{json,rb}
|
41
45
|
attributes/**/* definitions/**/* files/**/* libraries/**/*
|
42
|
-
providers/**/* recipes/**/* resources/**/* templates/**/*
|
46
|
+
providers/**/* recipes/**/* resources/**/* templates/**/*
|
47
|
+
].join(",")
|
43
48
|
|
44
49
|
default_config :data_path do |provisioner|
|
45
50
|
provisioner.calculate_path("data")
|
@@ -72,79 +77,117 @@ module Kitchen
|
|
72
77
|
expand_path_for :clients_path
|
73
78
|
|
74
79
|
default_config :encrypted_data_bag_secret_key_path do |provisioner|
|
75
|
-
provisioner.calculate_path("encrypted_data_bag_secret_key", :file)
|
80
|
+
provisioner.calculate_path("encrypted_data_bag_secret_key", :type => :file)
|
76
81
|
end
|
77
82
|
expand_path_for :encrypted_data_bag_secret_key_path
|
78
83
|
|
84
|
+
# (see Base#install_command)
|
79
85
|
def install_command
|
80
86
|
return unless config[:require_chef_omnibus]
|
81
87
|
|
82
|
-
|
83
|
-
|
84
|
-
version = if flag.is_a?(String) && flag != "latest"
|
85
|
-
"-v #{flag.downcase}"
|
86
|
-
else
|
87
|
-
""
|
88
|
-
end
|
89
|
-
|
90
|
-
# use Bourne (/bin/sh) as Bash does not exist on all Unix flavors
|
91
|
-
<<-INSTALL.gsub(/^ {10}/, '')
|
92
|
-
sh -c '
|
93
|
-
#{Util.shell_helpers}
|
94
|
-
|
95
|
-
should_update_chef() {
|
96
|
-
case "#{flag}" in
|
97
|
-
true|`chef-solo -v | cut -d " " -f 2`) return 1 ;;
|
98
|
-
latest|*) return 0 ;;
|
99
|
-
esac
|
100
|
-
}
|
101
|
-
|
102
|
-
if [ ! -d "/opt/chef" ] || should_update_chef ; then
|
103
|
-
echo "-----> Installing Chef Omnibus (#{flag})"
|
104
|
-
do_download #{url} /tmp/install.sh
|
105
|
-
#{sudo('sh')} /tmp/install.sh #{version}
|
106
|
-
fi'
|
107
|
-
INSTALL
|
88
|
+
lines = [Util.shell_helpers, chef_shell_helpers, chef_install_function]
|
89
|
+
Util.wrap_command(lines.join("\n"))
|
108
90
|
end
|
109
91
|
|
92
|
+
# (see Base#init_command)
|
110
93
|
def init_command
|
111
|
-
dirs = %w
|
94
|
+
dirs = %w[cookbooks data data_bags environments roles clients].
|
112
95
|
map { |dir| File.join(config[:root_path], dir) }.join(" ")
|
113
|
-
"#{sudo(
|
96
|
+
lines = ["#{sudo("rm")} -rf #{dirs}", "mkdir -p #{config[:root_path]}"]
|
97
|
+
|
98
|
+
Util.wrap_command(lines.join("\n"))
|
114
99
|
end
|
115
100
|
|
101
|
+
# (see Base#create_sandbox)
|
116
102
|
def create_sandbox
|
117
103
|
super
|
118
104
|
prepare_json
|
119
105
|
prepare_cache
|
120
106
|
prepare_cookbooks
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
107
|
+
prepare(:data)
|
108
|
+
prepare(:data_bags)
|
109
|
+
prepare(:environments)
|
110
|
+
prepare(:nodes)
|
111
|
+
prepare(:roles)
|
112
|
+
prepare(:clients)
|
113
|
+
prepare(
|
114
|
+
:secret,
|
115
|
+
:type => :file,
|
116
|
+
:dest_name => "encrypted_data_bag_secret",
|
117
|
+
:key_name => :encrypted_data_bag_secret_key_path
|
118
|
+
)
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
|
123
|
+
# Load cookbook dependency resolver code, if required.
|
124
|
+
#
|
125
|
+
# (see Base#load_needed_dependencies!)
|
132
126
|
def load_needed_dependencies!
|
133
|
-
if File.
|
127
|
+
if File.exist?(berksfile)
|
134
128
|
debug("Berksfile found at #{berksfile}, loading Berkshelf")
|
135
129
|
Chef::Berkshelf.load!(logger)
|
136
|
-
elsif File.
|
130
|
+
elsif File.exist?(cheffile)
|
137
131
|
debug("Cheffile found at #{cheffile}, loading Librarian-Chef")
|
138
132
|
Chef::Librarian.load!(logger)
|
139
133
|
end
|
140
134
|
end
|
141
135
|
|
136
|
+
# Returns shell code with chef-related functions.
|
137
|
+
#
|
138
|
+
# @return [String] shell code
|
139
|
+
# @api private
|
140
|
+
def chef_shell_helpers
|
141
|
+
IO.read(File.join(
|
142
|
+
File.dirname(__FILE__), %w[.. .. .. support chef_helpers.sh]
|
143
|
+
))
|
144
|
+
end
|
145
|
+
|
146
|
+
# Generates the shell code to conditionally install a Chef Omnibus
|
147
|
+
# package onto an instance.
|
148
|
+
#
|
149
|
+
# @return [String] shell code
|
150
|
+
# @api private
|
151
|
+
def chef_install_function
|
152
|
+
version = config[:require_chef_omnibus].to_s.downcase
|
153
|
+
pretty_version = case version
|
154
|
+
when "true" then "install only if missing"
|
155
|
+
when "latest" then "always install latest version"
|
156
|
+
else version
|
157
|
+
end
|
158
|
+
install_flags = %w[latest true].include?(version) ? "" : "-v #{version}"
|
159
|
+
if config[:chef_omnibus_install_options]
|
160
|
+
install_flags += config[:chef_omnibus_install_options]
|
161
|
+
end
|
162
|
+
|
163
|
+
<<-INSTALL.gsub(/^ {10}/, "")
|
164
|
+
if should_update_chef "#{config[:chef_omnibus_root]}" "#{version}" ; then
|
165
|
+
echo "-----> Installing Chef Omnibus (#{pretty_version})"
|
166
|
+
do_download #{config[:chef_omnibus_url]} /tmp/install.sh
|
167
|
+
#{sudo("sh")} /tmp/install.sh #{install_flags}
|
168
|
+
else
|
169
|
+
echo "-----> Chef Omnibus installation detected (#{pretty_version})"
|
170
|
+
fi
|
171
|
+
INSTALL
|
172
|
+
end
|
173
|
+
|
174
|
+
# Generates a rendered client.rb/solo.rb/knife.rb formatted file as a
|
175
|
+
# String.
|
176
|
+
#
|
177
|
+
# @param data [Hash] a key/value pair hash of configuration
|
178
|
+
# @return [String] a rendered Chef config file as a String
|
179
|
+
# @api private
|
142
180
|
def format_config_file(data)
|
143
181
|
data.each.map { |attr, value|
|
144
|
-
[attr,
|
182
|
+
[attr, value.inspect].join(" ")
|
145
183
|
}.join("\n")
|
146
184
|
end
|
147
185
|
|
186
|
+
# Generates a Hash with default values for a solo.rb or client.rb Chef
|
187
|
+
# configuration file.
|
188
|
+
#
|
189
|
+
# @return [Hash] a configuration hash
|
190
|
+
# @api private
|
148
191
|
def default_config_rb
|
149
192
|
root = config[:root_path]
|
150
193
|
|
@@ -163,105 +206,78 @@ module Kitchen
|
|
163
206
|
:validation_key => "#{root}/validation.pem",
|
164
207
|
:client_key => "#{root}/client.pem",
|
165
208
|
:chef_server_url => "http://127.0.0.1:8889",
|
166
|
-
:encrypted_data_bag_secret => "#{root}/encrypted_data_bag_secret"
|
209
|
+
:encrypted_data_bag_secret => "#{root}/encrypted_data_bag_secret"
|
167
210
|
}
|
168
211
|
end
|
169
212
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
213
|
+
# Prepares a generic Chef component source directory or file for
|
214
|
+
# inclusion in the sandbox path. These components might includes nodes,
|
215
|
+
# roles, etc.
|
216
|
+
#
|
217
|
+
# @param component [Symbol,String] a component name such as `:node`
|
218
|
+
# @param opts [Hash] optional configuration
|
219
|
+
# @option opts [Symbol] :type whether the component is a directory or
|
220
|
+
# file (default: `:directory`)
|
221
|
+
# @option opts [Symbol] :key_name the key name in the config hash from
|
222
|
+
# which to pull the source path (default: `"#{component}_path"`)
|
223
|
+
# @option opts [String] :dest_name the destination file or directory
|
224
|
+
# basename in the sandbox path (default: `component.to_s`)
|
225
|
+
# @api private
|
226
|
+
def prepare(component, opts = {})
|
227
|
+
opts = { :type => :directory }.merge(opts)
|
228
|
+
key_name = opts.fetch(:key_name, "#{component}_path")
|
229
|
+
src = config[key_name.to_sym]
|
230
|
+
return if src.nil?
|
231
|
+
|
232
|
+
info("Preparing #{component}")
|
233
|
+
debug("Using #{component} from #{src}")
|
234
|
+
|
235
|
+
dest = File.join(sandbox_path, opts.fetch(:dest_name, component.to_s))
|
236
|
+
|
237
|
+
case opts[:type]
|
238
|
+
when :directory
|
239
|
+
FileUtils.mkdir_p(dest)
|
240
|
+
FileUtils.cp_r(Dir.glob("#{src}/*"), dest)
|
241
|
+
when :file
|
242
|
+
FileUtils.mkdir_p(File.dirname(dest))
|
243
|
+
FileUtils.cp_r(src, dest)
|
175
244
|
end
|
176
245
|
end
|
177
246
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
tmpdata_dir = File.join(sandbox_path, "data")
|
185
|
-
FileUtils.mkdir_p(tmpdata_dir)
|
186
|
-
FileUtils.cp_r(Dir.glob("#{data}/*"), tmpdata_dir)
|
187
|
-
end
|
188
|
-
|
189
|
-
def prepare_data_bags
|
190
|
-
return unless data_bags
|
191
|
-
|
192
|
-
info("Preparing data bags")
|
193
|
-
debug("Using data bags from #{data_bags}")
|
194
|
-
|
195
|
-
tmpbags_dir = File.join(sandbox_path, "data_bags")
|
196
|
-
FileUtils.mkdir_p(tmpbags_dir)
|
197
|
-
FileUtils.cp_r(Dir.glob("#{data_bags}/*"), tmpbags_dir)
|
198
|
-
end
|
199
|
-
|
200
|
-
def prepare_roles
|
201
|
-
return unless roles
|
202
|
-
|
203
|
-
info("Preparing roles")
|
204
|
-
debug("Using roles from #{roles}")
|
205
|
-
|
206
|
-
tmproles_dir = File.join(sandbox_path, "roles")
|
207
|
-
FileUtils.mkdir_p(tmproles_dir)
|
208
|
-
FileUtils.cp_r(Dir.glob("#{roles}/*"), tmproles_dir)
|
209
|
-
end
|
210
|
-
|
211
|
-
def prepare_clients
|
212
|
-
return unless clients
|
213
|
-
|
214
|
-
info("Preparing clients")
|
215
|
-
debug("Using roles from #{clients}")
|
216
|
-
|
217
|
-
tmpclients_dir = File.join(sandbox_path, "clients")
|
218
|
-
FileUtils.mkdir_p(tmpclients_dir)
|
219
|
-
FileUtils.cp_r(Dir.glob("#{clients}/*"), tmpclients_dir)
|
220
|
-
end
|
221
|
-
|
222
|
-
def prepare_nodes
|
223
|
-
return unless nodes
|
224
|
-
|
225
|
-
info("Preparing nodes")
|
226
|
-
debug("Using nodes from #{nodes}")
|
227
|
-
|
228
|
-
tmpnodes_dir = File.join(sandbox_path, "nodes")
|
229
|
-
FileUtils.mkdir_p(tmpnodes_dir)
|
230
|
-
FileUtils.cp_r(Dir.glob("#{nodes}/*"), tmpnodes_dir)
|
231
|
-
end
|
232
|
-
|
233
|
-
def prepare_environments
|
234
|
-
return unless environments
|
235
|
-
|
236
|
-
info("Preparing environments")
|
237
|
-
debug("Using environments from #{environments}")
|
238
|
-
|
239
|
-
tmpenvs_dir = File.join(sandbox_path, "environments")
|
240
|
-
FileUtils.mkdir_p(tmpenvs_dir)
|
241
|
-
FileUtils.cp_r(Dir.glob("#{environments}/*"), tmpenvs_dir)
|
242
|
-
end
|
243
|
-
|
244
|
-
def prepare_secret
|
245
|
-
return unless secret
|
247
|
+
# Prepares a Chef JSON file, sometimes called a dna.json or
|
248
|
+
# first-boot.json, for inclusion in the sandbox path.
|
249
|
+
#
|
250
|
+
# @api private
|
251
|
+
def prepare_json
|
252
|
+
dna = config[:attributes].merge(:run_list => config[:run_list])
|
246
253
|
|
247
|
-
info("Preparing
|
248
|
-
debug("
|
254
|
+
info("Preparing dna.json")
|
255
|
+
debug("Creating dna.json from #{dna.inspect}")
|
249
256
|
|
250
|
-
|
257
|
+
File.open(File.join(sandbox_path, "dna.json"), "wb") do |file|
|
258
|
+
file.write(dna.to_json)
|
259
|
+
end
|
251
260
|
end
|
252
261
|
|
262
|
+
# Prepares a cache directory for inclusion in the sandbox path.
|
263
|
+
#
|
264
|
+
# @api private
|
253
265
|
def prepare_cache
|
254
266
|
FileUtils.mkdir_p(File.join(sandbox_path, "cache"))
|
255
267
|
end
|
256
268
|
|
269
|
+
# Prepares Chef cookbooks for inclusion in the sandbox path.
|
270
|
+
#
|
271
|
+
# @api private
|
257
272
|
def prepare_cookbooks
|
258
|
-
if File.
|
273
|
+
if File.exist?(berksfile)
|
259
274
|
resolve_with_berkshelf
|
260
|
-
elsif File.
|
275
|
+
elsif File.exist?(cheffile)
|
261
276
|
resolve_with_librarian
|
277
|
+
cp_site_cookbooks if File.directory?(site_cookbooks_dir)
|
262
278
|
elsif File.directory?(cookbooks_dir)
|
263
279
|
cp_cookbooks
|
264
|
-
elsif File.
|
280
|
+
elsif File.exist?(metadata_rb)
|
265
281
|
cp_this_cookbook
|
266
282
|
else
|
267
283
|
make_fake_cookbook
|
@@ -270,79 +286,86 @@ module Kitchen
|
|
270
286
|
filter_only_cookbook_files
|
271
287
|
end
|
272
288
|
|
289
|
+
# Removes all non-cookbook files in the sandbox path.
|
290
|
+
#
|
291
|
+
# @api private
|
273
292
|
def filter_only_cookbook_files
|
274
293
|
info("Removing non-cookbook files before transfer")
|
275
294
|
FileUtils.rm(all_files_in_cookbooks - only_cookbook_files)
|
276
295
|
end
|
277
296
|
|
297
|
+
# Generates a list of all files in the cookbooks directory in the
|
298
|
+
# sandbox path.
|
299
|
+
#
|
300
|
+
# @return [Array<String>] an array of absolute paths to files
|
301
|
+
# @api private
|
278
302
|
def all_files_in_cookbooks
|
279
303
|
Dir.glob(File.join(tmpbooks_dir, "**/*"), File::FNM_DOTMATCH).
|
280
|
-
select { |fn| File.file?(fn) && ! %w
|
304
|
+
select { |fn| File.file?(fn) && ! %w[. ..].include?(fn) }
|
281
305
|
end
|
282
306
|
|
307
|
+
# Generates a list of all typical cookbook files needed in a Chef run,
|
308
|
+
# located in the cookbooks directory in the sandbox path.
|
309
|
+
#
|
310
|
+
# @return [Array<String>] an array of absolute paths to files
|
311
|
+
# @api private
|
283
312
|
def only_cookbook_files
|
284
313
|
glob = File.join(tmpbooks_dir, "*", "{#{config[:cookbook_files_glob]}}")
|
285
314
|
|
286
315
|
Dir.glob(glob, File::FNM_DOTMATCH).
|
287
|
-
select { |fn| File.file?(fn) && ! %w
|
316
|
+
select { |fn| File.file?(fn) && ! %w[. ..].include?(fn) }
|
288
317
|
end
|
289
318
|
|
319
|
+
# @return [String] an absolute path to a Berksfile, relative to the
|
320
|
+
# kitchen root
|
321
|
+
# @api private
|
290
322
|
def berksfile
|
291
323
|
File.join(config[:kitchen_root], "Berksfile")
|
292
324
|
end
|
293
325
|
|
326
|
+
# @return [String] an absolute path to a Cheffile, relative to the
|
327
|
+
# kitchen root
|
328
|
+
# @api private
|
294
329
|
def cheffile
|
295
330
|
File.join(config[:kitchen_root], "Cheffile")
|
296
331
|
end
|
297
332
|
|
333
|
+
# @return [String] an absolute path to a metadata.rb, relative to the
|
334
|
+
# kitchen root
|
335
|
+
# @api private
|
298
336
|
def metadata_rb
|
299
337
|
File.join(config[:kitchen_root], "metadata.rb")
|
300
338
|
end
|
301
339
|
|
340
|
+
# @return [String] an absolute path to a cookbooks/ directory, relative
|
341
|
+
# to the kitchen root
|
342
|
+
# @api private
|
302
343
|
def cookbooks_dir
|
303
344
|
File.join(config[:kitchen_root], "cookbooks")
|
304
345
|
end
|
305
346
|
|
347
|
+
# @return [String] an absolute path to a site-cookbooks/ directory,
|
348
|
+
# relative to the kitchen root
|
349
|
+
# @api private
|
306
350
|
def site_cookbooks_dir
|
307
351
|
File.join(config[:kitchen_root], "site-cookbooks")
|
308
352
|
end
|
309
353
|
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
def roles
|
315
|
-
config[:roles_path]
|
316
|
-
end
|
317
|
-
|
318
|
-
def clients
|
319
|
-
config[:clients_path]
|
320
|
-
end
|
321
|
-
|
322
|
-
def nodes
|
323
|
-
config[:nodes_path]
|
324
|
-
end
|
325
|
-
|
326
|
-
def data
|
327
|
-
config[:data_path]
|
328
|
-
end
|
329
|
-
|
330
|
-
def environments
|
331
|
-
config[:environments_path]
|
332
|
-
end
|
333
|
-
|
334
|
-
def secret
|
335
|
-
config[:encrypted_data_bag_secret_key_path]
|
336
|
-
end
|
337
|
-
|
354
|
+
# @return [String] an absolute path to a cookbooks/ directory in the
|
355
|
+
# sandbox path
|
356
|
+
# @api private
|
338
357
|
def tmpbooks_dir
|
339
358
|
File.join(sandbox_path, "cookbooks")
|
340
359
|
end
|
341
360
|
|
361
|
+
# @return [String] an absolute path to a site cookbooks directory in the
|
362
|
+
# sandbox path
|
363
|
+
# @api private
|
342
364
|
def tmpsitebooks_dir
|
343
365
|
File.join(sandbox_path, "cookbooks")
|
344
366
|
end
|
345
367
|
|
368
|
+
# Copies a cookbooks/ directory into the sandbox path.
|
346
369
|
def cp_cookbooks
|
347
370
|
info("Preparing cookbooks from project directory")
|
348
371
|
debug("Using cookbooks from #{cookbooks_dir}")
|
@@ -351,9 +374,12 @@ module Kitchen
|
|
351
374
|
FileUtils.cp_r(File.join(cookbooks_dir, "."), tmpbooks_dir)
|
352
375
|
|
353
376
|
cp_site_cookbooks if File.directory?(site_cookbooks_dir)
|
354
|
-
cp_this_cookbook if File.
|
377
|
+
cp_this_cookbook if File.exist?(metadata_rb)
|
355
378
|
end
|
356
379
|
|
380
|
+
# Copies a site-cookbooks/ directory into the sandbox path.
|
381
|
+
#
|
382
|
+
# @api private
|
357
383
|
def cp_site_cookbooks
|
358
384
|
info("Preparing site-cookbooks from project directory")
|
359
385
|
debug("Using cookbooks from #{site_cookbooks_dir}")
|
@@ -362,12 +388,16 @@ module Kitchen
|
|
362
388
|
FileUtils.cp_r(File.join(site_cookbooks_dir, "."), tmpsitebooks_dir)
|
363
389
|
end
|
364
390
|
|
391
|
+
# Copies the current project, assumed to be a Chef cookbook into the
|
392
|
+
# sandbox path.
|
393
|
+
#
|
394
|
+
# @api private
|
365
395
|
def cp_this_cookbook
|
366
396
|
info("Preparing current project directory as a cookbook")
|
367
397
|
debug("Using metadata.rb from #{metadata_rb}")
|
368
398
|
|
369
|
-
cb_name = MetadataChopper.extract(metadata_rb).first
|
370
|
-
"The metadata.rb does not define the 'name' key."
|
399
|
+
cb_name = MetadataChopper.extract(metadata_rb).first || raise(UserError,
|
400
|
+
"The metadata.rb does not define the 'name' key." \
|
371
401
|
" Please add: `name '<cookbook_name>'` to metadata.rb and retry")
|
372
402
|
|
373
403
|
cb_path = File.join(tmpbooks_dir, cb_name)
|
@@ -378,23 +408,32 @@ module Kitchen
|
|
378
408
|
FileUtils.cp_r(glob, cb_path)
|
379
409
|
end
|
380
410
|
|
411
|
+
# Creates a minimal, no-op cookbook in the sandbox path.
|
412
|
+
#
|
413
|
+
# @api private
|
381
414
|
def make_fake_cookbook
|
382
|
-
info("Berksfile, Cheffile, cookbooks/, or metadata.rb not found "
|
415
|
+
info("Berksfile, Cheffile, cookbooks/, or metadata.rb not found " \
|
383
416
|
"so Chef will run with effectively no cookbooks. Is this intended?")
|
384
417
|
name = File.basename(config[:kitchen_root])
|
385
418
|
fake_cb = File.join(tmpbooks_dir, name)
|
386
419
|
FileUtils.mkdir_p(fake_cb)
|
387
420
|
File.open(File.join(fake_cb, "metadata.rb"), "wb") do |file|
|
388
|
-
file.write(%{name "#{name}\n
|
421
|
+
file.write(%{name "#{name}"\n})
|
389
422
|
end
|
390
423
|
end
|
391
424
|
|
425
|
+
# Performs a Berkshelf cookbook resolution inside a common mutex.
|
426
|
+
#
|
427
|
+
# @api private
|
392
428
|
def resolve_with_berkshelf
|
393
429
|
Kitchen.mutex.synchronize do
|
394
430
|
Chef::Berkshelf.new(berksfile, tmpbooks_dir, logger).resolve
|
395
431
|
end
|
396
432
|
end
|
397
433
|
|
434
|
+
# Performs a Librarin-Chef cookbook resolution inside a common mutex.
|
435
|
+
#
|
436
|
+
# @api private
|
398
437
|
def resolve_with_librarian
|
399
438
|
Kitchen.mutex.synchronize do
|
400
439
|
Chef::Librarian.new(cheffile, tmpbooks_dir, logger).resolve
|