kitchen-dsc 0.12.0 → 0.13.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.
- checksums.yaml +4 -4
- data/.github/CODEOWNERS +1 -0
- data/.github/workflows/lint.yml +3 -3
- data/.github/workflows/publish.yaml +2 -5
- data/.gitignore +3 -0
- data/.markdownlint.yaml +2 -5
- data/.release-please-manifest.json +3 -0
- data/.rspec +2 -0
- data/.rubocop.yml +1 -4
- data/.yamllint +6 -0
- data/.yardopts +11 -0
- data/CHANGELOG.md +14 -0
- data/CONTRIBUTING.md +121 -0
- data/Gemfile +7 -9
- data/README.md +230 -79
- data/Rakefile +40 -5
- data/kitchen-dsc.gemspec +3 -3
- data/lib/kitchen/provisioner/dsc.rb +241 -2
- data/lib/kitchen-dsc/version.rb +19 -1
- data/release-please-config.json +12 -0
- data/spec/kitchen/provisioner/dsc_spec.rb +590 -0
- data/spec/kitchen_dsc/version_spec.rb +21 -0
- data/spec/spec_helper.rb +70 -16
- data/spec/support/kitchen_helpers.rb +159 -0
- metadata +15 -9
- data/lib/kitchen/provisioner/dsc_lcm/lcm_base.rb +0 -86
- data/lib/kitchen/provisioner/dsc_lcm/lcm_v4.rb +0 -52
- data/lib/kitchen/provisioner/dsc_lcm/lcm_v5.rb +0 -53
data/Rakefile
CHANGED
|
@@ -1,21 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "bundler/gem_tasks"
|
|
2
|
-
|
|
4
|
+
|
|
5
|
+
require "cookstyle/chefstyle"
|
|
3
6
|
require "rubocop/rake_task"
|
|
4
7
|
|
|
5
8
|
RuboCop::RakeTask.new(:style) do |task|
|
|
6
9
|
task.options += ["--display-cop-names", "--no-color"]
|
|
7
10
|
end
|
|
8
11
|
|
|
9
|
-
# Create the spec task.
|
|
10
12
|
require "rspec/core/rake_task"
|
|
13
|
+
|
|
14
|
+
# Named `test` because the shared test-kitchen CI workflow invokes
|
|
15
|
+
# `bundle exec rake test`. Renaming it would break CI.
|
|
11
16
|
RSpec::Core::RakeTask.new(:test, :tag) do |t, args|
|
|
12
17
|
t.rspec_opts = [].tap do |a|
|
|
13
|
-
a << "--color"
|
|
14
18
|
a << "--format #{ENV["CI"] ? "documentation" : "progress"}"
|
|
15
19
|
a << "--backtrace" if ENV["VERBOSE"] || ENV["DEBUG"]
|
|
16
20
|
a << "--seed #{ENV["SEED"]}" if ENV["SEED"]
|
|
17
21
|
a << "--tag #{args[:tag]}" if args[:tag]
|
|
18
|
-
a << "--
|
|
19
|
-
a << "-I test/spec"
|
|
22
|
+
a << "--only-failures" if ENV["ONLY_FAILURES"]
|
|
20
23
|
end.join(" ")
|
|
21
24
|
end
|
|
25
|
+
|
|
26
|
+
# YARD lives in the :development bundle group, which a test-only install may
|
|
27
|
+
# skip. Documentation tasks are optional, so degrade rather than break `rake`.
|
|
28
|
+
begin
|
|
29
|
+
require "yard"
|
|
30
|
+
|
|
31
|
+
YARD::Rake::YardocTask.new(:yard) do |t|
|
|
32
|
+
t.stats_options = ["--list-undoc"]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
namespace :yard do
|
|
36
|
+
desc "Report documentation coverage, listing undocumented objects"
|
|
37
|
+
task :stats do
|
|
38
|
+
sh "yard stats --list-undoc"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
desc "Serve the docs at http://localhost:8808, reloading on change"
|
|
42
|
+
task :server do
|
|
43
|
+
sh "yard server --reload"
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
rescue LoadError
|
|
47
|
+
%w{yard yard:stats yard:server}.each do |name|
|
|
48
|
+
desc "(unavailable: install the :development bundle group)" if name == "yard"
|
|
49
|
+
task name do
|
|
50
|
+
abort "YARD is not available. Run `bundle install --with development`."
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
desc "Run the linter and the unit tests"
|
|
56
|
+
task default: %i{style test}
|
data/kitchen-dsc.gemspec
CHANGED
|
@@ -3,15 +3,15 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
|
3
3
|
require "kitchen-dsc/version"
|
|
4
4
|
|
|
5
5
|
Gem::Specification.new do |gem|
|
|
6
|
-
gem.name
|
|
6
|
+
gem.name = "kitchen-dsc"
|
|
7
|
+
gem.required_ruby_version = ">= 3.1"
|
|
7
8
|
gem.version = Kitchen::Dsc::VERSION
|
|
8
9
|
gem.authors = ["Test Kitchen Team"]
|
|
9
10
|
gem.email = ["help@sous-chefs.org"]
|
|
10
11
|
gem.homepage = "https://github.com/test-kitchen/kitchen-dsc"
|
|
11
12
|
gem.summary = "PowerShell DSC provisioner for test-kitchen"
|
|
12
13
|
gem.description = "PowerShell DSC provisioner for test-kitchen"
|
|
13
|
-
gem.files
|
|
14
|
-
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
|
14
|
+
gem.files = `git ls-files`.split($/)
|
|
15
15
|
gem.platform = Gem::Platform::RUBY
|
|
16
16
|
gem.require_paths = ["lib"]
|
|
17
17
|
gem.license = "Apache-2.0"
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
#
|
|
2
4
|
# Author:: Steven Murawski (<steven.murawski@gmail.com>)
|
|
3
5
|
#
|
|
@@ -13,10 +15,41 @@ require "kitchen/util"
|
|
|
13
15
|
require "dsc_lcm_configuration"
|
|
14
16
|
|
|
15
17
|
module Kitchen
|
|
18
|
+
# Test Kitchen's provisioner namespace, reopened to register the DSC
|
|
19
|
+
# provisioner alongside the ones that ship with Test Kitchen itself.
|
|
16
20
|
module Provisioner
|
|
21
|
+
# Applies PowerShell Desired State Configuration to a Test Kitchen instance.
|
|
22
|
+
#
|
|
23
|
+
# The provisioner runs across the four Test Kitchen phases:
|
|
24
|
+
#
|
|
25
|
+
# 1. {#install_command} configures the Local Configuration Manager on the
|
|
26
|
+
# system under test.
|
|
27
|
+
# 2. {#init_command} creates the remote configuration directory and, on
|
|
28
|
+
# WMF 5, installs any modules requested from a PowerShell gallery.
|
|
29
|
+
# 3. {#create_sandbox} stages DSC resources and the configuration script on
|
|
30
|
+
# the workstation, ready for upload.
|
|
31
|
+
# 4. {#prepare_command} compiles the configuration into a MOF on the system
|
|
32
|
+
# under test and {#run_command} applies it.
|
|
33
|
+
#
|
|
34
|
+
# Two project layouts are supported, chosen automatically by
|
|
35
|
+
# {#powershell_module?}: *module style*, where the kitchen root is itself a
|
|
36
|
+
# PowerShell module (identified by a `<module name>.psd1` manifest), and
|
|
37
|
+
# *repository style*, where DSC resources live in a `modules` directory.
|
|
38
|
+
#
|
|
39
|
+
# @example Minimal kitchen.yml
|
|
40
|
+
# provisioner:
|
|
41
|
+
# name: dsc
|
|
42
|
+
# dsc_local_configuration_manager_version: wmf5
|
|
43
|
+
# configuration_script: web.ps1
|
|
44
|
+
#
|
|
45
|
+
# @see https://github.com/test-kitchen/kitchen-dsc kitchen-dsc README
|
|
17
46
|
class Dsc < Base
|
|
18
47
|
kitchen_provisioner_api_version 2
|
|
19
48
|
|
|
49
|
+
# @!attribute [rw] tmp_dir
|
|
50
|
+
# @return [String, nil] path to a scratch directory on the system under
|
|
51
|
+
# test. Set by consumers that need somewhere to write intermediate
|
|
52
|
+
# files; unused by the provisioner itself.
|
|
20
53
|
attr_accessor :tmp_dir
|
|
21
54
|
|
|
22
55
|
default_config :modules_path, "modules"
|
|
@@ -37,11 +70,29 @@ module Kitchen
|
|
|
37
70
|
default_config :dsc_local_configuration_manager_version, "wmf4"
|
|
38
71
|
default_config :dsc_local_configuration_manager, {}
|
|
39
72
|
|
|
73
|
+
# Resolves the Local Configuration Manager settings before the instance is
|
|
74
|
+
# used.
|
|
75
|
+
#
|
|
76
|
+
# Replaces the caller's partial `:dsc_local_configuration_manager` hash
|
|
77
|
+
# with the fully defaulted settings for the configured WMF version, so
|
|
78
|
+
# later phases and `kitchen diagnose` see the values that will actually be
|
|
79
|
+
# applied.
|
|
80
|
+
#
|
|
81
|
+
# @param instance [Kitchen::Instance] the instance this provisioner serves
|
|
82
|
+
# @return [self]
|
|
40
83
|
def finalize_config!(instance)
|
|
41
84
|
config[:dsc_local_configuration_manager] = lcm.lcm_config
|
|
42
85
|
super(instance)
|
|
43
86
|
end
|
|
44
87
|
|
|
88
|
+
# Builds the command that configures the Local Configuration Manager.
|
|
89
|
+
#
|
|
90
|
+
# Runs during Test Kitchen's `install` phase, before any configuration is
|
|
91
|
+
# compiled, since the LCM controls how DSC behaves for the rest of the
|
|
92
|
+
# run.
|
|
93
|
+
#
|
|
94
|
+
# @return [String] PowerShell that declares and applies the `SetupLCM`
|
|
95
|
+
# meta-configuration
|
|
45
96
|
def install_command
|
|
46
97
|
full_lcm_configuration_script = <<-EOH
|
|
47
98
|
#{lcm.lcm_configuration_script}
|
|
@@ -53,6 +104,14 @@ module Kitchen
|
|
|
53
104
|
wrap_powershell_code(full_lcm_configuration_script)
|
|
54
105
|
end
|
|
55
106
|
|
|
107
|
+
# Builds the command that prepares the system under test for upload.
|
|
108
|
+
#
|
|
109
|
+
# Always creates the directory the configuration script will be copied
|
|
110
|
+
# into. On WMF 5 with `:modules_from_gallery` set, it also bootstraps
|
|
111
|
+
# PackageManagement and installs those modules.
|
|
112
|
+
#
|
|
113
|
+
# @return [String] PowerShell run during the `converge` phase, before
|
|
114
|
+
# files are transferred
|
|
56
115
|
def init_command
|
|
57
116
|
script = <<~EOH
|
|
58
117
|
#{setup_config_directory_script}
|
|
@@ -61,6 +120,17 @@ module Kitchen
|
|
|
61
120
|
wrap_powershell_code(script)
|
|
62
121
|
end
|
|
63
122
|
|
|
123
|
+
# Stages DSC resources and the configuration script into the sandbox.
|
|
124
|
+
#
|
|
125
|
+
# The sandbox is the local directory Test Kitchen uploads to the system
|
|
126
|
+
# under test. Which staging strategy runs depends on whether the project
|
|
127
|
+
# is laid out as a PowerShell module or as a repository of modules.
|
|
128
|
+
#
|
|
129
|
+
# @return [void]
|
|
130
|
+
# @raise [Errno::ENOENT] if the configuration script named by
|
|
131
|
+
# `:configuration_script_folder` and `:configuration_script` is missing
|
|
132
|
+
# @see #prepare_resource_style_directory
|
|
133
|
+
# @see #prepare_repo_style_directory
|
|
64
134
|
def create_sandbox
|
|
65
135
|
super
|
|
66
136
|
info("Staging DSC Resource Modules for copy to the SUT")
|
|
@@ -73,9 +143,19 @@ module Kitchen
|
|
|
73
143
|
prepare_configuration_script
|
|
74
144
|
end
|
|
75
145
|
|
|
146
|
+
# Builds the command that compiles configurations into MOF documents.
|
|
147
|
+
#
|
|
148
|
+
# Copies the uploaded modules onto the `PSModulePath`, loads the
|
|
149
|
+
# configuration script, then compiles each name in `:configuration_name`
|
|
150
|
+
# into `c:/configurations/<name>`. Any leftover MOF from a previous
|
|
151
|
+
# converge is removed first so a failed compile cannot be silently applied.
|
|
152
|
+
#
|
|
153
|
+
# @return [String] PowerShell run after files are transferred and before
|
|
154
|
+
# {#run_command}
|
|
76
155
|
def prepare_command
|
|
77
156
|
info("Moving DSC Resources onto PSModulePath")
|
|
78
|
-
|
|
157
|
+
# +@ makes an explicitly mutable buffer under frozen_string_literal.
|
|
158
|
+
scripts = +<<-EOH
|
|
79
159
|
|
|
80
160
|
if (Test-Path (join-path #{config[:root_path]} 'modules'))
|
|
81
161
|
{
|
|
@@ -133,10 +213,20 @@ module Kitchen
|
|
|
133
213
|
wrap_powershell_code(scripts)
|
|
134
214
|
end
|
|
135
215
|
|
|
216
|
+
# Builds the command that applies the compiled MOF documents.
|
|
217
|
+
#
|
|
218
|
+
# A DSC resource may require a reboot to finish. Rather than failing, the
|
|
219
|
+
# generated script reboots the node and exits 35, and this method opts the
|
|
220
|
+
# instance into retrying that exit code so the converge resumes once the
|
|
221
|
+
# node is back. Explicit `:retry_on_exit_code` and `:max_retries` settings
|
|
222
|
+
# are left untouched.
|
|
223
|
+
#
|
|
224
|
+
# @return [String] PowerShell that starts a DSC configuration job per
|
|
225
|
+
# configuration name and reports its errors
|
|
136
226
|
def run_command
|
|
137
227
|
config[:retry_on_exit_code] = [35] if config[:retry_on_exit_code].empty?
|
|
138
228
|
config[:max_retries] = 3 if config[:max_retries] == 1
|
|
139
|
-
scripts = ""
|
|
229
|
+
scripts = +""
|
|
140
230
|
ensure_array(config[:configuration_name]).each do |configuration|
|
|
141
231
|
info("Running the configuration #{configuration}")
|
|
142
232
|
run_configuration_script = <<-EOH
|
|
@@ -163,6 +253,15 @@ module Kitchen
|
|
|
163
253
|
|
|
164
254
|
private
|
|
165
255
|
|
|
256
|
+
# The Local Configuration Manager configuration for the target WMF version.
|
|
257
|
+
#
|
|
258
|
+
# Note that `DscLcmConfiguration::Factory` only recognizes `"4"`,
|
|
259
|
+
# `"wmf4_with_update"`, `"5"` and `"wmf5"`; every other value, including
|
|
260
|
+
# this provisioner's own `"wmf4"` default, yields the base LCM
|
|
261
|
+
# configuration.
|
|
262
|
+
#
|
|
263
|
+
# @api private
|
|
264
|
+
# @return [DscLcmConfiguration::LcmBase] a memoized LCM configuration
|
|
166
265
|
def lcm
|
|
167
266
|
@lcm ||= begin
|
|
168
267
|
lcm_version = config[:dsc_local_configuration_manager_version]
|
|
@@ -171,10 +270,26 @@ module Kitchen
|
|
|
171
270
|
end
|
|
172
271
|
end
|
|
173
272
|
|
|
273
|
+
# PowerShell that creates the remote directory holding the configuration
|
|
274
|
+
# script.
|
|
275
|
+
#
|
|
276
|
+
# @api private
|
|
277
|
+
# @return [String] a `mkdir` invocation
|
|
174
278
|
def setup_config_directory_script
|
|
175
279
|
"mkdir (split-path (join-path #{config[:root_path]} #{sandboxed_configuration_script})) -force | out-null"
|
|
176
280
|
end
|
|
177
281
|
|
|
282
|
+
# Renders a module specification hash as `install-module` parameters.
|
|
283
|
+
#
|
|
284
|
+
# A `Force` key is dropped because `-force` is already appended to every
|
|
285
|
+
# `install-module` call, and PowerShell rejects a duplicated parameter. A
|
|
286
|
+
# `Repository` key is added from the configured gallery unless the caller
|
|
287
|
+
# supplied one.
|
|
288
|
+
#
|
|
289
|
+
# @api private
|
|
290
|
+
# @param module_specification_hash [Hash] `install-module` parameters, as
|
|
291
|
+
# given in kitchen.yml
|
|
292
|
+
# @return [String] space-separated `-Key Value` pairs
|
|
178
293
|
def powershell_module_params(module_specification_hash)
|
|
179
294
|
keys = module_specification_hash.keys.reject { |k| k.to_s.casecmp("force") == 0 }
|
|
180
295
|
unless keys.any? { |k| k.to_s.downcase == "repository" }
|
|
@@ -184,6 +299,13 @@ module Kitchen
|
|
|
184
299
|
keys.map { |key| "-#{key} #{module_specification_hash[key]}" }.join(" ")
|
|
185
300
|
end
|
|
186
301
|
|
|
302
|
+
# Builds one `install-module` line per entry in `:modules_from_gallery`.
|
|
303
|
+
#
|
|
304
|
+
# Entries may be plain module names or hashes of `install-module`
|
|
305
|
+
# parameters.
|
|
306
|
+
#
|
|
307
|
+
# @api private
|
|
308
|
+
# @return [Array<String>] PowerShell `install-module` invocations
|
|
187
309
|
def powershell_modules
|
|
188
310
|
Array(config[:modules_from_gallery]).map do |powershell_module|
|
|
189
311
|
params = if powershell_module.is_a? Hash
|
|
@@ -195,6 +317,14 @@ module Kitchen
|
|
|
195
317
|
end
|
|
196
318
|
end
|
|
197
319
|
|
|
320
|
+
# PowerShell that bootstraps the NuGet package provider.
|
|
321
|
+
#
|
|
322
|
+
# PackageManagement cannot install from a gallery until the NuGet provider
|
|
323
|
+
# is present, and its interactive bootstrap prompt would hang a converge.
|
|
324
|
+
#
|
|
325
|
+
# @api private
|
|
326
|
+
# @return [String, nil] the bootstrap command, or nil when
|
|
327
|
+
# `:nuget_force_bootstrap` is disabled
|
|
198
328
|
def nuget_force_bootstrap
|
|
199
329
|
return unless config[:nuget_force_bootstrap]
|
|
200
330
|
|
|
@@ -202,6 +332,12 @@ module Kitchen
|
|
|
202
332
|
"install-packageprovider nuget -force -forcebootstrap | out-null"
|
|
203
333
|
end
|
|
204
334
|
|
|
335
|
+
# The PowerShellGet repository name to install modules from.
|
|
336
|
+
#
|
|
337
|
+
# @api private
|
|
338
|
+
# @return [String] `:gallery_name` when set, the public `PSGallery` when
|
|
339
|
+
# neither gallery setting is given, and `testing` for an unnamed private
|
|
340
|
+
# `:gallery_uri`
|
|
205
341
|
def psmodule_repository_name
|
|
206
342
|
return "PSGallery" if config[:gallery_name].nil? && config[:gallery_uri].nil?
|
|
207
343
|
return "testing" if config[:gallery_name].nil?
|
|
@@ -209,6 +345,11 @@ module Kitchen
|
|
|
209
345
|
config[:gallery_name]
|
|
210
346
|
end
|
|
211
347
|
|
|
348
|
+
# PowerShell that registers a private gallery as a package source.
|
|
349
|
+
#
|
|
350
|
+
# @api private
|
|
351
|
+
# @return [String, nil] the `register-packagesource` command, or nil when
|
|
352
|
+
# no `:gallery_uri` is configured
|
|
212
353
|
def register_psmodule_repository
|
|
213
354
|
return if config[:gallery_uri].nil?
|
|
214
355
|
|
|
@@ -216,6 +357,11 @@ module Kitchen
|
|
|
216
357
|
"register-packagesource -providername PowerShellGet -name '#{psmodule_repository_name}' -location '#{config[:gallery_uri]}' -force -trusted"
|
|
217
358
|
end
|
|
218
359
|
|
|
360
|
+
# PowerShell that installs every requested gallery module.
|
|
361
|
+
#
|
|
362
|
+
# @api private
|
|
363
|
+
# @return [String, nil] the bootstrap, registration and install commands,
|
|
364
|
+
# or nil when no gallery modules are configured
|
|
219
365
|
def install_module_script
|
|
220
366
|
return if config[:modules_from_gallery].nil?
|
|
221
367
|
|
|
@@ -226,28 +372,67 @@ module Kitchen
|
|
|
226
372
|
EOH
|
|
227
373
|
end
|
|
228
374
|
|
|
375
|
+
# Whether gallery modules should be installed during {#init_command}.
|
|
376
|
+
#
|
|
377
|
+
# Gallery installation depends on PowerShellGet, which ships with WMF 5.
|
|
378
|
+
#
|
|
379
|
+
# @api private
|
|
380
|
+
# @return [Boolean] true only when targeting WMF 5 with modules requested
|
|
229
381
|
def install_modules?
|
|
230
382
|
config[:dsc_local_configuration_manager_version] == "wmf5" &&
|
|
231
383
|
!config[:modules_from_gallery].nil?
|
|
232
384
|
end
|
|
233
385
|
|
|
386
|
+
# Name of the PowerShell variable holding configuration data.
|
|
387
|
+
#
|
|
388
|
+
# @api private
|
|
389
|
+
# @return [String] `:configuration_data_variable`, or `ConfigurationData`
|
|
390
|
+
# when it was explicitly blanked out
|
|
234
391
|
def configuration_data_variable
|
|
235
392
|
config[:configuration_data_variable].nil? ? "ConfigurationData" : config[:configuration_data_variable]
|
|
236
393
|
end
|
|
237
394
|
|
|
395
|
+
# PowerShell that assigns `:configuration_data` to its variable.
|
|
396
|
+
#
|
|
397
|
+
# @api private
|
|
398
|
+
# @return [String] a hashtable assignment
|
|
238
399
|
def configuration_data_assignment
|
|
239
400
|
"$" + configuration_data_variable + " = " + ps_hash(config[:configuration_data])
|
|
240
401
|
end
|
|
241
402
|
|
|
403
|
+
# Wraps generated PowerShell for execution by the transport.
|
|
404
|
+
#
|
|
405
|
+
# Progress streams are silenced first: WinRM relays them as output, which
|
|
406
|
+
# makes converge logs unreadable and can slow long-running resources.
|
|
407
|
+
#
|
|
408
|
+
# @api private
|
|
409
|
+
# @param code [String] the PowerShell to wrap
|
|
410
|
+
# @return [String] the wrapped command
|
|
242
411
|
def wrap_powershell_code(code)
|
|
243
412
|
wrap_shell_code(["$ProgressPreference = 'SilentlyContinue';", code].join("\n"))
|
|
244
413
|
end
|
|
245
414
|
|
|
415
|
+
# Whether the kitchen root is itself a PowerShell module.
|
|
416
|
+
#
|
|
417
|
+
# Detected by a `<module name>.psd1` manifest sitting beside the project,
|
|
418
|
+
# which selects module-style staging over repository-style staging.
|
|
419
|
+
#
|
|
420
|
+
# @api private
|
|
421
|
+
# @return [Boolean] true when a matching module manifest exists
|
|
246
422
|
def powershell_module?
|
|
247
423
|
module_metadata_file = File.join(config[:kitchen_root], "#{module_name}.psd1")
|
|
248
424
|
File.exist?(module_metadata_file)
|
|
249
425
|
end
|
|
250
426
|
|
|
427
|
+
# Lists the files to stage from a module-style project.
|
|
428
|
+
#
|
|
429
|
+
# Directories are excluded because the copy recreates them as needed, and
|
|
430
|
+
# repository housekeeping files are excluded because they are not part of
|
|
431
|
+
# the module.
|
|
432
|
+
#
|
|
433
|
+
# @api private
|
|
434
|
+
# @param path [String] directory to enumerate
|
|
435
|
+
# @return [Array<String>] absolute paths of files to stage
|
|
251
436
|
def list_files(path)
|
|
252
437
|
base_directory_content = Dir.glob(File.join(path, "*"))
|
|
253
438
|
nested_directory_content = Dir.glob(File.join(path, "*/**/*"))
|
|
@@ -260,10 +445,22 @@ module Kitchen
|
|
|
260
445
|
end
|
|
261
446
|
end
|
|
262
447
|
|
|
448
|
+
# The PowerShell module name implied by the project directory.
|
|
449
|
+
#
|
|
450
|
+
# @api private
|
|
451
|
+
# @return [String] basename of `:kitchen_root`
|
|
263
452
|
def module_name
|
|
264
453
|
File.basename(config[:kitchen_root])
|
|
265
454
|
end
|
|
266
455
|
|
|
456
|
+
# Stages a module-style project into the sandbox.
|
|
457
|
+
#
|
|
458
|
+
# The whole kitchen root is copied to `modules/<module name>` so the
|
|
459
|
+
# module lands on the system under test's `PSModulePath` under the name
|
|
460
|
+
# DSC expects.
|
|
461
|
+
#
|
|
462
|
+
# @api private
|
|
463
|
+
# @return [void]
|
|
267
464
|
def prepare_resource_style_directory
|
|
268
465
|
sandbox_base_module_path = File.join(sandbox_path, "modules/#{module_name}")
|
|
269
466
|
|
|
@@ -277,6 +474,13 @@ module Kitchen
|
|
|
277
474
|
end
|
|
278
475
|
end
|
|
279
476
|
|
|
477
|
+
# Stages a repository-style project into the sandbox.
|
|
478
|
+
#
|
|
479
|
+
# A missing modules directory is not an error: a project may ship only a
|
|
480
|
+
# configuration script and rely on resources already present on the node.
|
|
481
|
+
#
|
|
482
|
+
# @api private
|
|
483
|
+
# @return [void]
|
|
280
484
|
def prepare_repo_style_directory
|
|
281
485
|
module_path = File.join(config[:kitchen_root], config[:modules_path])
|
|
282
486
|
sandbox_module_path = File.join(sandbox_path, "modules")
|
|
@@ -289,14 +493,38 @@ module Kitchen
|
|
|
289
493
|
end
|
|
290
494
|
end
|
|
291
495
|
|
|
496
|
+
# Path of the configuration script relative to the sandbox and to
|
|
497
|
+
# `:root_path` on the system under test.
|
|
498
|
+
#
|
|
499
|
+
# @api private
|
|
500
|
+
# @return [String] the sandboxed script path
|
|
292
501
|
def sandboxed_configuration_script
|
|
293
502
|
File.join("configuration", config[:configuration_script])
|
|
294
503
|
end
|
|
295
504
|
|
|
505
|
+
# Indentation used when rendering PowerShell hashtables.
|
|
506
|
+
#
|
|
507
|
+
# @api private
|
|
508
|
+
# @param depth [Integer] number of spaces
|
|
509
|
+
# @return [String] a run of spaces
|
|
296
510
|
def pad(depth = 0)
|
|
297
511
|
" " * depth
|
|
298
512
|
end
|
|
299
513
|
|
|
514
|
+
# Renders a Ruby object as a PowerShell literal.
|
|
515
|
+
#
|
|
516
|
+
# Hashes become hashtables and arrays become arrays; every other value is
|
|
517
|
+
# rendered as a double-quoted string, so Ruby booleans and integers reach
|
|
518
|
+
# DSC quoted.
|
|
519
|
+
#
|
|
520
|
+
# @api private
|
|
521
|
+
# @param obj [Hash, Array, Object] the value to render
|
|
522
|
+
# @param depth [Integer] current indentation depth
|
|
523
|
+
# @return [String] a PowerShell literal
|
|
524
|
+
#
|
|
525
|
+
# @example
|
|
526
|
+
# ps_hash("AllNodes" => [{ "NodeName" => "*" }])
|
|
527
|
+
# #=> %{@{\n "AllNodes" = @(\n@{\n "NodeName" = "*"\n }\n)\n}}
|
|
300
528
|
def ps_hash(obj, depth = 0)
|
|
301
529
|
if obj.is_a?(Hash)
|
|
302
530
|
obj.map do |k, v|
|
|
@@ -310,6 +538,11 @@ module Kitchen
|
|
|
310
538
|
end
|
|
311
539
|
end
|
|
312
540
|
|
|
541
|
+
# Copies the DSC configuration script into the sandbox.
|
|
542
|
+
#
|
|
543
|
+
# @api private
|
|
544
|
+
# @return [void]
|
|
545
|
+
# @raise [Errno::ENOENT] if the configured script does not exist
|
|
313
546
|
def prepare_configuration_script
|
|
314
547
|
configuration_script_file = File.join(config[:configuration_script_folder], config[:configuration_script])
|
|
315
548
|
configuration_script_path = File.join(config[:kitchen_root], configuration_script_file)
|
|
@@ -319,6 +552,12 @@ module Kitchen
|
|
|
319
552
|
FileUtils.cp(configuration_script_path, sandbox_configuration_script_path)
|
|
320
553
|
end
|
|
321
554
|
|
|
555
|
+
# Wraps a scalar in an array so `:configuration_name` may be given either
|
|
556
|
+
# as a single name or as a list.
|
|
557
|
+
#
|
|
558
|
+
# @api private
|
|
559
|
+
# @param thing [Object, Array] the value to normalize
|
|
560
|
+
# @return [Array] +thing+ if it is already an array, otherwise `[thing]`
|
|
322
561
|
def ensure_array(thing)
|
|
323
562
|
if thing.is_a?(Array)
|
|
324
563
|
thing
|
data/lib/kitchen-dsc/version.rb
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Author:: Steven Murawski (<steven.murawski@gmail.com>)
|
|
3
|
+
#
|
|
4
|
+
# Copyright (C) 2014 Steven Murawski
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache 2 License.
|
|
7
|
+
# See LICENSE for more details
|
|
8
|
+
|
|
1
9
|
module Kitchen
|
|
10
|
+
# Namespace for the kitchen-dsc plugin's own metadata.
|
|
11
|
+
#
|
|
12
|
+
# The provisioner itself lives in {Kitchen::Provisioner::Dsc}; this module
|
|
13
|
+
# exists so the gemspec can read the version without loading Test Kitchen.
|
|
2
14
|
module Dsc
|
|
3
|
-
|
|
15
|
+
# The released version of the kitchen-dsc gem.
|
|
16
|
+
#
|
|
17
|
+
# Kept in sync with the gemspec by release-please; changing it by hand is
|
|
18
|
+
# only appropriate as part of a release commit.
|
|
19
|
+
#
|
|
20
|
+
# @return [String] a frozen dotted version number
|
|
21
|
+
VERSION = "0.13.0".freeze
|
|
4
22
|
end
|
|
5
23
|
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"packages": {
|
|
3
|
+
".": {
|
|
4
|
+
"package-name": "kitchen-dsc",
|
|
5
|
+
"changelog-path": "CHANGELOG.md",
|
|
6
|
+
"release-type": "ruby",
|
|
7
|
+
"include-component-in-tag": false,
|
|
8
|
+
"version-file": "lib/kitchen-dsc/version.rb"
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json"
|
|
12
|
+
}
|