kitchen-dsc 0.7.0 → 0.8.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 199fcd3f8420441f3a0333169a07c2c20b5e9706
4
- data.tar.gz: 02003c4a0ef9fdf5cfe9b671c607b046680efac6
3
+ metadata.gz: d72d58d0319e21aff93818d1ef18619e54ed76ec
4
+ data.tar.gz: 732c13ad0c646ccd2f446e4f953f1821a7690774
5
5
  SHA512:
6
- metadata.gz: 25eabedf3875e2d2d7f8e47a399bfdfe9ad1dc6b64f62a2ae815d3c33e04d622611deef50e0472550a498500e689362032649b98643944149ba898a3e5f515dd
7
- data.tar.gz: e88664f829e881937b0172d0dcaba5cb4f3d075df4da140325214dbfa073e89a7bc2548aecf3a1f656bb5652609ebf14f660d279ba8d4dd3255612d479fbfe7d
6
+ metadata.gz: 3d5f65251c7829f715229a83328ee07ffe7bad6eaec69021cd293c6cbfe16ed73522ff76185e2cf52cc7e576785c2cc4363b4b24e0c9ca7f639d9ac949cce213
7
+ data.tar.gz: 7b7a308187d63be5a59e838d2592b7cb4371c338c398188d84ee6eda9a30c80b0eccc26265ff4c99303573c76d323bc142bb748f6d7581702ab90de095112717
data/README.md CHANGED
@@ -1,64 +1,93 @@
1
- [![Gem Version](https://badge.fury.io/rb/kitchen-dsc.svg)](http://badge.fury.io/rb/kitchen-dsc)
2
-
3
- # kitchen-dsc
4
- A Test Kitchen Provisioner for PowerShell DSC
5
-
6
-
7
- ## Requirements
8
- You'll need a driver box with WMF4 or greater (ONLY WINDOWS SYSTEMS)
9
-
10
- ## Installation & Setup
11
- You'll need the test-kitchen & kitchen-dsc gems installed in your system, along with kitchen-vagrant or some ther suitable driver for test-kitchen.
12
-
13
- ### Note:
14
- You will see a delay in the return of the run details due to an difference in how the verbose stream is returned for DSC runs between WMF versions, so I return the verbose stream after the job completes. I'd love to live stream the results, but that'll take a bit more experimentation. (PR's welcome!)
15
-
16
- ## Example Configurations
17
- * [Repository Style Testing](https://github.com/smurawski/dsc-kitchen-project)
18
- * [Module Style Testing](https://github.com/powershellorg/cwebadministration/tree/smurawski/adding_tests)
19
-
20
- ## Configuration Settings
21
- * configuration_script_folder
22
- * Defaults to 'examples'.
23
- * The location of a PowerShell script(s) containing the DSC configuration command(s).
24
-
25
- * configuration_script
26
- * Defaults to 'dsc_configuration.ps1'
27
- * The name of the PowerShell script containing the DSC configuration command(s) (and possibly configuration data)
28
-
29
- * configuration_name
30
- * Name of the configuration to run, defaults to the suite name.
31
-
32
- * configuration_data
33
- * A YAML representation of what should be passed to the configuration.
34
- * Overrides any configurationdata variable assigned in the configuration script.
35
-
36
- * configuration_data_variable
37
- * Defaults to 'ConfigurationData'
38
- * Name of the variable that contains the ConfigurationData hashtable
39
- * Can be defined in the configuration script or via the `configuration_data` configuration setting.
40
-
41
- * dsc_local_configuration_manager_version
42
- * Defaults to 'wmf4'
43
- * Identifies what version of the LCM is in place
44
- * Other valid values are 'wmf4_with_update' and 'wmf5'
45
- * Currently the only difference between wmf4 and wmf4_with_update/wmf5 is the action_after_reboot and the debug_mode settings. Eventually, I'd like to add support for partial configurations, pull servers, etc..
46
- * In this context, wmf4_with_update refers to wmf4 with KB3000850 applied (to add support for WMF 5 generated configurations, plus some fixes).
47
-
48
- * dsc_local_configuration_manager
49
- * Settings for the LCM
50
- * Defaults are:
51
- * action_after_reboot = 'StopConfiguration' # wmf4_with_update or wmf5
52
- * allow_module_overwrite = false
53
- * certificate_id = nil
54
- * configuration_mode = 'ApplyAndAutoCorrect'
55
- * configuration_mode_frequency_mins = 30 # 15 on wmf5
56
- * debug_mode = 'All' # wmf4_with_update
57
- * refresh_frequency_mins = 15 # 30 on wmf5
58
- * refresh_mode = 'PUSH'
59
-
60
- ### Specific to repository style testing
61
- * modules_path
62
- * Defaults to 'modules'.
63
- * Points to the location of modules containing DSC resources to upload
64
- * This path is relative to the root of the repository (the location of the .kitchen.yml).
1
+ [![Gem Version](https://badge.fury.io/rb/kitchen-dsc.svg)](http://badge.fury.io/rb/kitchen-dsc)
2
+
3
+ # kitchen-dsc
4
+ A Test Kitchen Provisioner for PowerShell DSC
5
+
6
+
7
+ ## Requirements
8
+ You'll need a driver box with WMF4 or greater (ONLY WINDOWS SYSTEMS)
9
+
10
+ ## Installation & Setup
11
+ You'll need the test-kitchen & kitchen-dsc gems installed in your system, along with kitchen-vagrant or some ther suitable driver for test-kitchen.
12
+
13
+ ### Note:
14
+ You will see a delay in the return of the run details due to an difference in how the verbose stream is returned for DSC runs between WMF versions, so I return the verbose stream after the job completes. I'd love to live stream the results, but that'll take a bit more experimentation. (PR's welcome!)
15
+
16
+ ## Example Configurations
17
+ * [Repository Style Testing](https://github.com/smurawski/dsc-kitchen-project)
18
+ * [Module Style Testing](https://github.com/powershellorg/cwebadministration/tree/smurawski/adding_tests)
19
+
20
+ ## Configuration Settings
21
+ * configuration_script_folder
22
+ * Defaults to 'examples'.
23
+ * The location of a PowerShell script(s) containing the DSC configuration command(s).
24
+
25
+ * configuration_script
26
+ * Defaults to 'dsc_configuration.ps1'
27
+ * The name of the PowerShell script containing the DSC configuration command(s) (and possibly configuration data)
28
+
29
+ * configuration_name
30
+ * Name of the configuration to run, defaults to the suite name.
31
+
32
+ * configuration_data
33
+ * A YAML representation of what should be passed to the configuration.
34
+ * Overrides any configurationdata variable assigned in the configuration script.
35
+
36
+ * configuration_data_variable
37
+ * Defaults to 'ConfigurationData'
38
+ * Name of the variable that contains the ConfigurationData hashtable
39
+ * Can be defined in the configuration script or via the `configuration_data` configuration setting.
40
+
41
+ * dsc_local_configuration_manager_version
42
+ * Defaults to 'wmf4'
43
+ * Identifies what version of the LCM is in place
44
+ * Other valid values are 'wmf4_with_update' and 'wmf5'
45
+ * Currently the only difference between wmf4 and wmf4_with_update/wmf5 is the action_after_reboot and the debug_mode settings. Eventually, I'd like to add support for partial configurations, pull servers, etc..
46
+ * In this context, wmf4_with_update refers to wmf4 with KB3000850 applied (to add support for WMF 5 generated configurations, plus some fixes).
47
+
48
+ * dsc_local_configuration_manager
49
+ * Settings for the LCM
50
+ * Defaults are:
51
+ * action_after_reboot = 'StopConfiguration' # wmf4_with_update or wmf5
52
+ * reboot_if_needed = false
53
+ * allow_module_overwrite = false
54
+ * certificate_id = nil
55
+ * configuration_mode = 'ApplyAndAutoCorrect'
56
+ * configuration_mode_frequency_mins = 30 # 15 on wmf5
57
+ * debug_mode = 'All' # wmf4_with_update
58
+ * refresh_frequency_mins = 15 # 30 on wmf5
59
+ * refresh_mode = 'PUSH'
60
+
61
+ * modules_from_gallery
62
+ * Requires WMF 5
63
+ * Takes a string (for one module) or an array (for multiple) to install from the gallery
64
+
65
+ ### Specific to repository style testing
66
+ * modules_path
67
+ * Defaults to 'modules'.
68
+ * Points to the location of modules containing DSC resources to upload
69
+ * This path is relative to the root of the repository (the location of the .kitchen.yml).
70
+
71
+ ## Example
72
+
73
+ ```yaml
74
+ provisioner:
75
+ - name: dsc
76
+ dsc_local_configuration_manager_version: wmf5
77
+ dsc_local_configuration_manager:
78
+ reboot_if_needed: true
79
+ debug_mode: none
80
+ configuration_script_folder: .
81
+ configuration_script: SampleConfig.ps1
82
+ modules_from_gallery:
83
+ - xWebAdministration
84
+ - xComputerManagement
85
+
86
+ suite:
87
+ - name: test
88
+ provisioner:
89
+ configuration_data:
90
+ AllNodes:
91
+ - nodename: localhost
92
+ role: webserver
93
+ ```
data/kitchen-dsc.gemspec CHANGED
@@ -1,41 +1,41 @@
1
- # encoding: utf-8
2
-
3
- $LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
4
- require 'kitchen-dsc/version'
5
-
6
- Gem::Specification.new do |s|
7
- s.name = 'kitchen-dsc'
8
- s.version = Kitchen::Dsc::VERSION
9
- s.authors = ['Steven Murawski']
10
- s.email = ['steven.murawski@gmail.com']
11
- s.homepage = 'https://github.com/test-kitchen/kitchen-dsc'
12
- s.summary = 'PowerShell DSC provisioner for test-kitchen'
13
- candidates = Dir.glob('lib/**/*') + ['README.md', 'kitchen-dsc.gemspec']
14
- s.files = candidates.sort
15
- s.platform = Gem::Platform::RUBY
16
- s.require_paths = ['lib']
17
- s.rubyforge_project = '[none]'
18
- s.license = 'Apache 2'
19
- s.description = <<-EOF
20
- == DESCRIPTION:
21
-
22
- DSC Provisioner for Test Kitchen
23
-
24
- == FEATURES:
25
-
26
- TBD
27
-
28
- EOF
29
- s.add_dependency "test-kitchen", "~> 1.4"
30
-
31
- s.add_development_dependency "countloc", "~> 0.4"
32
- s.add_development_dependency "rake"
33
- s.add_development_dependency "rspec", "~> 3.2"
34
- s.add_development_dependency "simplecov", "~> 0.9"
35
-
36
- # style and complexity libraries are tightly version pinned as newer releases
37
- # may introduce new and undesireable style choices which would be immediately
38
- # enforced in CI
39
- s.add_development_dependency "finstyle", "1.4.0"
40
- s.add_development_dependency "cane", "2.6.2"
41
- end
1
+ # encoding: utf-8
2
+
3
+ $LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
4
+ require 'kitchen-dsc/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = 'kitchen-dsc'
8
+ s.version = Kitchen::Dsc::VERSION
9
+ s.authors = ['Steven Murawski']
10
+ s.email = ['steven.murawski@gmail.com']
11
+ s.homepage = 'https://github.com/test-kitchen/kitchen-dsc'
12
+ s.summary = 'PowerShell DSC provisioner for test-kitchen'
13
+ candidates = Dir.glob('lib/**/*') + ['README.md', 'kitchen-dsc.gemspec']
14
+ s.files = candidates.sort
15
+ s.platform = Gem::Platform::RUBY
16
+ s.require_paths = ['lib']
17
+ s.rubyforge_project = '[none]'
18
+ s.license = 'Apache 2'
19
+ s.description = <<-EOF
20
+ == DESCRIPTION:
21
+
22
+ DSC Provisioner for Test Kitchen
23
+
24
+ == FEATURES:
25
+
26
+ TBD
27
+
28
+ EOF
29
+ s.add_dependency "test-kitchen", "~> 1.4"
30
+
31
+ s.add_development_dependency "countloc", "~> 0.4"
32
+ s.add_development_dependency "rake"
33
+ s.add_development_dependency "rspec", "~> 3.2"
34
+ s.add_development_dependency "simplecov", "~> 0.9"
35
+
36
+ # style and complexity libraries are tightly version pinned as newer releases
37
+ # may introduce new and undesireable style choices which would be immediately
38
+ # enforced in CI
39
+ s.add_development_dependency "finstyle", "1.4.0"
40
+ s.add_development_dependency "cane", "2.6.2"
41
+ end
@@ -1,7 +1,7 @@
1
- # encoding: utf-8
2
-
3
- module Kitchen
4
- module Dsc
5
- VERSION = '0.7.0'
6
- end
7
- end
1
+ # encoding: utf-8
2
+
3
+ module Kitchen
4
+ module Dsc
5
+ VERSION = '0.8.0'
6
+ end
7
+ end
@@ -1,279 +1,331 @@
1
- # -*- encoding: utf-8 -*-
2
- #
3
- # Author:: Steven Murawski (<steven.murawski@gmail.com>)
4
- #
5
- # Copyright (C) 2014 Steven Murawski
6
- #
7
- # Licensed under the MIT License.
8
- # See LICENSE for more details
9
-
10
- require 'fileutils'
11
- require 'pathname'
12
- require 'kitchen/provisioner/base'
13
- require 'kitchen/util'
14
-
15
- module Kitchen
16
-
17
- module Provisioner
18
- class Dsc < Base
19
- kitchen_provisioner_api_version 2
20
-
21
- attr_accessor :tmp_dir
22
-
23
- default_config :modules_path, 'modules'
24
-
25
- default_config :configuration_script_folder, 'examples'
26
- default_config :configuration_script, 'dsc_configuration.ps1'
27
- default_config :configuration_name do |provisioner|
28
- provisioner.instance.suite.name
29
- end
30
-
31
- default_config :configuration_data_variable, 'ConfigurationData'
32
- default_config :configuration_data
33
-
34
- default_config :dsc_local_configuration_manager_version, 'wmf4'
35
- default_config :dsc_local_configuration_manager, {
36
- action_after_reboot: 'StopConfiguration',
37
- allow_module_overwrite: false,
38
- certificate_id: nil,
39
- configuration_mode: 'ApplyAndAutoCorrect',
40
- debug_mode: 'All',
41
- reboot_if_needed: false,
42
- refresh_mode: 'PUSH'
43
- }
44
-
45
- # Disable line length check, it is all embedded script.
46
- # rubocop:disable Metrics/LineLength
47
- def install_command
48
- lcm_config = config[:dsc_local_configuration_manager]
49
- case config[:dsc_local_configuration_manager_version]
50
- when 'wmf4_legacy', 'wmf4'
51
- lcm_configuration_script = <<-LCMSETUP
52
- configuration SetupLCM
53
- {
54
- LocalConfigurationManager
55
- {
56
- AllowModuleOverwrite = [bool]::Parse('#{lcm_config[:allow_module_overwrite]}')
57
- CertificateID = '#{lcm_config[:certificate_id].nil? ? '$null' : lcm_config[:certificate_id]}'
58
- ConfigurationMode = '#{lcm_config[:configuration_mode]}'
59
- ConfigurationModeFrequencyMins = #{lcm_config[:configuration_mode_frequency_mins].nil? ? '30' : lcm_config[:configuration_mode_frequency_mins]}
60
- RebootNodeIfNeeded = [bool]::Parse('#{lcm_config[:reboot_if_needed]}')
61
- RefreshFrequencyMins = #{lcm_config[:refresh_frequency_mins].nil? ? '15' : lcm_config[:refresh_frequency_mins]}
62
- RefreshMode = '#{lcm_config[:refresh_mode]}'
63
- }
64
- }
65
- LCMSETUP
66
- when 'wmf4_with_update'
67
- lcm_configuration_script = <<-LCMSETUP
68
- configuration SetupLCM
69
- {
70
- LocalConfigurationManager
71
- {
72
- ActionAfterReboot = '#{lcm_config[:action_after_reboot]}'
73
- AllowModuleOverwrite = [bool]::Parse('#{lcm_config[:allow_module_overwrite]}')
74
- CertificateID = '#{lcm_config[:certificate_id].nil? ? '$null' : lcm_config[:certificate_id]}'
75
- ConfigurationMode = '#{lcm_config[:configuration_mode]}'
76
- ConfigurationModeFrequencyMins = #{lcm_config[:configuration_mode_frequency_mins].nil? ? '30' : lcm_config[:configuration_mode_frequency_mins]}
77
- DebugMode = '#{lcm_config[:debug_mode]}'
78
- RebootNodeIfNeeded = [bool]::Parse('#{lcm_config[:reboot_if_needed]}')
79
- RefreshFrequencyMins = #{lcm_config[:refresh_frequency_mins].nil? ? '15' : lcm_config[:refresh_frequency_mins]}
80
- RefreshMode = '#{lcm_config[:refresh_mode]}'
81
- }
82
- }
83
- LCMSETUP
84
- when 'wmf5'
85
- lcm_configuration_script = <<-LCMSETUP
86
- configuration SetupLCM
87
- {
88
- LocalConfigurationManager
89
- {
90
- ActionAfterReboot = '#{lcm_config[:action_after_reboot]}'
91
- AllowModuleOverwrite = [bool]::Parse('#{lcm_config[:allow_module_overwrite]}')
92
- CertificateID = '#{lcm_config[:certificate_id].nil? ? '$null' : lcm_config[:certificate_id]}'
93
- ConfigurationMode = '#{lcm_config[:configuration_mode]}'
94
- ConfigurationModeFrequencyMins = #{lcm_config[:configuration_mode_frequency_mins].nil? ? '15' : lcm_config[:configuration_mode_frequency_mins]}
95
- DebugMode = '#{lcm_config[:debug_mode]}'
96
- RebootNodeIfNeeded = [bool]::Parse('#{lcm_config[:reboot_if_needed]}')
97
- RefreshFrequencyMins = #{lcm_config[:refresh_frequency_mins].nil? ? '30' : lcm_config[:refresh_frequency_mins]}
98
- RefreshMode = '#{lcm_config[:refresh_mode]}'
99
- }
100
- }
101
- LCMSETUP
102
- end
103
- full_lcm_configuration_script = <<-EOH
104
- #{lcm_configuration_script}
105
-
106
- $null = SetupLCM
107
- Set-DscLocalConfigurationManager -Path ./SetupLCM
108
- EOH
109
-
110
- wrap_shell_code(full_lcm_configuration_script)
111
- end
112
- # rubocop:enable Metrics/LineLength
113
-
114
- def init_command
115
- wrap_shell_code("mkdir (split-path (join-path #{config[:root_path]} #{sandboxed_configuration_script})) -force | out-null")
116
- end
117
-
118
- def create_sandbox
119
- super
120
- info('Staging DSC Resource Modules for copy to the SUT')
121
- if resource_module? || class_resource_module?
122
- prepare_resource_style_directory
123
- else
124
- prepare_repo_style_directory
125
- end
126
- info('Staging DSC configuration script for copy to the SUT')
127
- prepare_configuration_script
128
- end
129
-
130
- # Disable line length check, it is all logging and embedded script.
131
- # rubocop:disable Metrics/LineLength
132
- def prepare_command
133
- info('Moving DSC Resources onto PSModulePath')
134
- info("Generating the MOF script for the configuration #{config[:configuration_name]}")
135
- stage_resources_and_generate_mof_script = <<-EOH
136
- if (Test-Path (join-path #{config[:root_path]} 'modules'))
137
- {
138
- dir ( join-path #{config[:root_path]} 'modules/*') -directory |
139
- copy-item -destination $env:programfiles/windowspowershell/modules/ -recurse -force
140
- }
141
- if (-not (test-path 'c:/configurations'))
142
- {
143
- mkdir 'c:/configurations' | out-null
144
- }
145
- $ConfigurationScriptPath = Join-path #{config[:root_path]} #{sandboxed_configuration_script}
146
- if (-not (test-path $ConfigurationScriptPath))
147
- {
148
- throw "Failed to find $ConfigurationScriptPath"
149
- }
150
- invoke-expression (get-content $ConfigurationScriptPath -raw)
151
- if (-not (get-command #{config[:configuration_name]}))
152
- {
153
- throw "Failed to create a configuration command #{config[:configuration_name]}"
154
- }
155
-
156
- #{configuration_data_assignment unless config[:configuration_data].nil?}
157
-
158
- $null = #{config[:configuration_name]} -outputpath c:/configurations #{'-configurationdata $' + configuration_data_variable}
159
- EOH
160
- debug("Shelling out: #{stage_resources_and_generate_mof_script}")
161
- wrap_shell_code(stage_resources_and_generate_mof_script)
162
- end
163
- # rubocop:enable Metrics/LineLength
164
-
165
- def configuration_data_variable
166
- config[:configuration_data_variable].nil? ? 'ConfigurationData' : config[:configuration_data_variable]
167
- end
168
-
169
- def configuration_data_assignment
170
- '$' + configuration_data_variable + ' = ' + ps_hash(config[:configuration_data])
171
- end
172
-
173
- def run_command
174
- info("Running the configuration #{config[:configuration_name]}")
175
- run_configuration_script = <<-EOH
176
- $ProgressPreference = 'SilentlyContinue'
177
- $job = start-dscconfiguration -Path c:/configurations/ -force
178
- $job | wait-job
179
- $job.childjobs[0].verbose
180
- $dsc_errors = $job.childjobs[0].Error
181
- if ($dsc_errors -ne $null) {
182
- $dsc_errors
183
- exit 1
184
- }
185
- EOH
186
-
187
- debug("Shelling out: #{run_configuration_script}")
188
- wrap_shell_code(run_configuration_script)
189
- end
190
-
191
- private
192
-
193
- def resource_module?
194
- module_metadata_file = File.join(config[:kitchen_root], "#{module_name}.psd1")
195
- module_dsc_resource_folder = File.join(config[:kitchen_root], 'DSCResources')
196
- File.exist?(module_metadata_file) &&
197
- File.exist?(module_dsc_resource_folder)
198
- end
199
-
200
- def class_resource_module?
201
- module_metadata_file = File.join(config[:kitchen_root], "#{module_name}.psd1")
202
- module_dsc_resource_folder = File.join(config[:kitchen_root], 'DSCResources')
203
- File.exist?(module_metadata_file) &&
204
- !File.exist?(module_dsc_resource_folder)
205
- end
206
-
207
- def list_files(path)
208
- base_directory_content = Dir.glob(File.join(path, '*'))
209
- nested_directory_content = Dir.glob(File.join(path, '*/**/*'))
210
- all_directory_content =([base_directory_content, nested_directory_content]).flatten
211
-
212
- ignore_files = ['Gemfile', 'Gemfile.lock', 'README.md', 'LICENSE.txt']
213
- all_directory_content.reject do |f|
214
- debug("Enumerating #{f}")
215
- ignore_files.include?(File.basename(f)) || File.directory?(f)
216
- end
217
- end
218
-
219
- def module_name
220
- File.basename(config[:kitchen_root])
221
- end
222
-
223
- def prepare_resource_style_directory
224
- sandbox_base_module_path = File.join(sandbox_path, "modules/#{module_name}")
225
-
226
- base = config[:kitchen_root]
227
- list_files(base).each do |src|
228
- dest = File.join(sandbox_base_module_path, src.sub("#{base}/", ''))
229
- FileUtils.mkdir_p(File.dirname(dest))
230
- debug("Staging #{src} ")
231
- debug(" at #{dest}")
232
- FileUtils.cp(src, dest, preserve: true)
233
- end
234
- end
235
-
236
- def prepare_repo_style_directory
237
- module_path = File.join(config[:kitchen_root], config[:modules_path])
238
- sandbox_module_path = File.join(sandbox_path, 'modules')
239
-
240
- if Dir.exist?(module_path)
241
- debug("Moving #{module_path} to #{sandbox_module_path}")
242
- FileUtils.cp_r(module_path, sandbox_module_path)
243
- else
244
- debug("The modules path #{module_path} was not found. Not moving to #{sandbox_module_path}.")
245
- end
246
- end
247
-
248
- def sandboxed_configuration_script
249
- File.join('configuration', config[:configuration_script])
250
- end
251
-
252
- def pad(depth = 0)
253
- " " * depth
254
- end
255
-
256
- def ps_hash(obj, depth = 0)
257
- if obj.is_a?(Hash)
258
- obj.map { |k, v|
259
- %{#{pad(depth + 2)}#{ps_hash(k)} = #{ps_hash(v, depth + 2)}}
260
- }.join(";\n").insert(0, "@{\n").insert(-1, "\n#{pad(depth)}}")
261
- elsif obj.is_a?(Array)
262
- array_string = obj.map { |v| ps_hash(v, depth+4)}.join(",")
263
- "#{pad(depth)}@(\n#{array_string}\n)"
264
- else
265
- %{"#{obj}"}
266
- end
267
- end
268
-
269
- def prepare_configuration_script
270
- configuration_script_file = File.join(config[:configuration_script_folder], config[:configuration_script])
271
- configuration_script_path = File.join(config[:kitchen_root], configuration_script_file)
272
- sandbox_configuration_script_path = File.join(sandbox_path, sandboxed_configuration_script)
273
- FileUtils.mkdir_p(File.dirname(sandbox_configuration_script_path))
274
- debug("Moving #{configuration_script_path} to #{sandbox_configuration_script_path}")
275
- FileUtils.cp(configuration_script_path, sandbox_configuration_script_path)
276
- end
277
- end
278
- end
279
- end
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Author:: Steven Murawski (<steven.murawski@gmail.com>)
4
+ #
5
+ # Copyright (C) 2014 Steven Murawski
6
+ #
7
+ # Licensed under the MIT License.
8
+ # See LICENSE for more details
9
+
10
+ require 'fileutils'
11
+ require 'pathname'
12
+ require 'kitchen/provisioner/base'
13
+ require 'kitchen/util'
14
+
15
+ module Kitchen
16
+
17
+ module Provisioner
18
+ class Dsc < Base
19
+ kitchen_provisioner_api_version 2
20
+
21
+ attr_accessor :tmp_dir
22
+
23
+ default_config :modules_path, 'modules'
24
+
25
+ default_config :configuration_script_folder, 'examples'
26
+ default_config :configuration_script, 'dsc_configuration.ps1'
27
+ default_config :configuration_name do |provisioner|
28
+ provisioner.instance.suite.name
29
+ end
30
+
31
+ default_config :configuration_data_variable, 'ConfigurationData'
32
+ default_config :configuration_data
33
+
34
+ default_config :modules_from_gallery
35
+
36
+ default_config :dsc_local_configuration_manager_version, 'wmf4'
37
+ default_config :dsc_local_configuration_manager
38
+
39
+ def override_lcm_setting?(name)
40
+ if config[:dsc_local_configuration_manager].nil? ||
41
+ config[:dsc_local_configuration_manager][name.to_sym].nil?
42
+ false
43
+ else
44
+ true
45
+ end
46
+ end
47
+
48
+ def resolve_lcm_setting(name, default_value)
49
+ if override_lcm_setting? name
50
+ config[:dsc_local_configuration_manager][name.to_sym]
51
+ else
52
+ default_value
53
+ end
54
+ end
55
+
56
+ def lcm_settings
57
+ {
58
+ action_after_reboot: (resolve_lcm_setting 'action_after_reboot', 'StopConfiguration'),
59
+ allow_module_overwrite: (resolve_lcm_setting 'allow_module_overwrite', false),
60
+ certificate_id: (resolve_lcm_setting 'certificate_id', nil),
61
+ configuration_mode: (resolve_lcm_setting 'configuration_mode', 'ApplyAndAutoCorrect'),
62
+ debug_mode: (resolve_lcm_setting 'debug_mode', 'All'),
63
+ reboot_if_needed: (resolve_lcm_setting 'reboot_if_needed', false),
64
+ refresh_mode: (resolve_lcm_setting 'refresh_mode' , 'PUSH')
65
+ }
66
+ end
67
+
68
+
69
+ # Disable line length check, it is all embedded script.
70
+ # rubocop:disable Metrics/LineLength
71
+ def install_command
72
+ lcm_config = lcm_settings
73
+ case config[:dsc_local_configuration_manager_version]
74
+ when 'wmf4_legacy', 'wmf4'
75
+ lcm_configuration_script = <<-LCMSETUP
76
+ configuration SetupLCM
77
+ {
78
+ LocalConfigurationManager
79
+ {
80
+ AllowModuleOverwrite = [bool]::Parse('#{lcm_config[:allow_module_overwrite]}')
81
+ CertificateID = '#{lcm_config[:certificate_id].nil? ? '$null' : lcm_config[:certificate_id]}'
82
+ ConfigurationMode = '#{lcm_config[:configuration_mode]}'
83
+ ConfigurationModeFrequencyMins = #{lcm_config[:configuration_mode_frequency_mins].nil? ? '30' : lcm_config[:configuration_mode_frequency_mins]}
84
+ RebootNodeIfNeeded = [bool]::Parse('#{lcm_config[:reboot_if_needed]}')
85
+ RefreshFrequencyMins = #{lcm_config[:refresh_frequency_mins].nil? ? '15' : lcm_config[:refresh_frequency_mins]}
86
+ RefreshMode = '#{lcm_config[:refresh_mode]}'
87
+ }
88
+ }
89
+ LCMSETUP
90
+ when 'wmf4_with_update'
91
+ lcm_configuration_script = <<-LCMSETUP
92
+ configuration SetupLCM
93
+ {
94
+ LocalConfigurationManager
95
+ {
96
+ ActionAfterReboot = '#{lcm_config[:action_after_reboot]}'
97
+ AllowModuleOverwrite = [bool]::Parse('#{lcm_config[:allow_module_overwrite]}')
98
+ CertificateID = '#{lcm_config[:certificate_id].nil? ? '$null' : lcm_config[:certificate_id]}'
99
+ ConfigurationMode = '#{lcm_config[:configuration_mode]}'
100
+ ConfigurationModeFrequencyMins = #{lcm_config[:configuration_mode_frequency_mins].nil? ? '30' : lcm_config[:configuration_mode_frequency_mins]}
101
+ DebugMode = '#{lcm_config[:debug_mode]}'
102
+ RebootNodeIfNeeded = [bool]::Parse('#{lcm_config[:reboot_if_needed]}')
103
+ RefreshFrequencyMins = #{lcm_config[:refresh_frequency_mins].nil? ? '15' : lcm_config[:refresh_frequency_mins]}
104
+ RefreshMode = '#{lcm_config[:refresh_mode]}'
105
+ }
106
+ }
107
+ LCMSETUP
108
+ when 'wmf5'
109
+ lcm_configuration_script = <<-LCMSETUP
110
+ [DSCLocalConfigurationManager()]
111
+ configuration SetupLCM
112
+ {
113
+ Settings
114
+ {
115
+ ActionAfterReboot = '#{lcm_config[:action_after_reboot]}'
116
+ AllowModuleOverwrite = [bool]::Parse('#{lcm_config[:allow_module_overwrite]}')
117
+ CertificateID = '#{lcm_config[:certificate_id].nil? ? '$null' : lcm_config[:certificate_id]}'
118
+ ConfigurationMode = '#{lcm_config[:configuration_mode]}'
119
+ ConfigurationModeFrequencyMins = #{lcm_config[:configuration_mode_frequency_mins].nil? ? '15' : lcm_config[:configuration_mode_frequency_mins]}
120
+ DebugMode = '#{lcm_config[:debug_mode]}'
121
+ RebootNodeIfNeeded = [bool]::Parse('#{lcm_config[:reboot_if_needed]}')
122
+ RefreshFrequencyMins = #{lcm_config[:refresh_frequency_mins].nil? ? '30' : lcm_config[:refresh_frequency_mins]}
123
+ RefreshMode = '#{lcm_config[:refresh_mode]}'
124
+ }
125
+ }
126
+ LCMSETUP
127
+ end
128
+ full_lcm_configuration_script = <<-EOH
129
+ #{lcm_configuration_script}
130
+
131
+ $null = SetupLCM
132
+ Set-DscLocalConfigurationManager -Path ./SetupLCM | out-null
133
+ EOH
134
+
135
+ wrap_shell_code(full_lcm_configuration_script)
136
+ end
137
+ # rubocop:enable Metrics/LineLength
138
+
139
+ def setup_config_directory_script
140
+ "mkdir (split-path (join-path #{config[:root_path]} #{sandboxed_configuration_script})) -force | out-null"
141
+ end
142
+
143
+ def powershell_modules
144
+ Array(config[:modules_from_gallery]).map do |powershell_module|
145
+ "install-module '#{powershell_module}' -force | out-null"
146
+ end
147
+ end
148
+
149
+ def install_module_script
150
+ return if config[:modules_from_gallery].nil?
151
+ script = <<-EOH
152
+ install-packageprovider nuget -force -forcebootstrap | out-null
153
+ #{powershell_modules.join("\n")}
154
+ EOH
155
+ end
156
+
157
+ def install_modules?
158
+ config[:dsc_local_configuration_manager_version] == 'wmf5' &&
159
+ !config[:modules_from_gallery].nil?
160
+ end
161
+
162
+ def init_command
163
+ script = <<-EOH
164
+ #{setup_config_directory_script}
165
+ #{install_module_script if install_modules?}
166
+ EOH
167
+ wrap_shell_code(script)
168
+ end
169
+
170
+ def create_sandbox
171
+ super
172
+ info('Staging DSC Resource Modules for copy to the SUT')
173
+ if resource_module? || class_resource_module?
174
+ prepare_resource_style_directory
175
+ else
176
+ prepare_repo_style_directory
177
+ end
178
+ info('Staging DSC configuration script for copy to the SUT')
179
+ prepare_configuration_script
180
+ end
181
+
182
+ # Disable line length check, it is all logging and embedded script.
183
+ # rubocop:disable Metrics/LineLength
184
+ def prepare_command
185
+ info('Moving DSC Resources onto PSModulePath')
186
+ info("Generating the MOF script for the configuration #{config[:configuration_name]}")
187
+ stage_resources_and_generate_mof_script = <<-EOH
188
+ if (Test-Path (join-path #{config[:root_path]} 'modules'))
189
+ {
190
+ dir ( join-path #{config[:root_path]} 'modules/*') -directory |
191
+ copy-item -destination $env:programfiles/windowspowershell/modules/ -recurse -force
192
+ }
193
+ if (-not (test-path 'c:/configurations'))
194
+ {
195
+ mkdir 'c:/configurations' | out-null
196
+ }
197
+ $ConfigurationScriptPath = Join-path #{config[:root_path]} #{sandboxed_configuration_script}
198
+ if (-not (test-path $ConfigurationScriptPath))
199
+ {
200
+ throw "Failed to find $ConfigurationScriptPath"
201
+ }
202
+ invoke-expression (get-content $ConfigurationScriptPath -raw)
203
+ if (-not (get-command #{config[:configuration_name]}))
204
+ {
205
+ throw "Failed to create a configuration command #{config[:configuration_name]}"
206
+ }
207
+
208
+ #{configuration_data_assignment unless config[:configuration_data].nil?}
209
+
210
+ $null = #{config[:configuration_name]} -outputpath c:/configurations #{'-configurationdata $' + configuration_data_variable}
211
+ EOH
212
+ debug("Shelling out: #{stage_resources_and_generate_mof_script}")
213
+ wrap_shell_code(stage_resources_and_generate_mof_script)
214
+ end
215
+ # rubocop:enable Metrics/LineLength
216
+
217
+ def configuration_data_variable
218
+ config[:configuration_data_variable].nil? ? 'ConfigurationData' : config[:configuration_data_variable]
219
+ end
220
+
221
+ def configuration_data_assignment
222
+ '$' + configuration_data_variable + ' = ' + ps_hash(config[:configuration_data])
223
+ end
224
+
225
+ def run_command
226
+ info("Running the configuration #{config[:configuration_name]}")
227
+ run_configuration_script = <<-EOH
228
+ $ProgressPreference = 'SilentlyContinue'
229
+ $job = start-dscconfiguration -Path c:/configurations/ -force
230
+ $job | wait-job
231
+ $job.childjobs[0].verbose
232
+ $dsc_errors = $job.childjobs[0].Error
233
+ if ($dsc_errors -ne $null) {
234
+ $dsc_errors
235
+ exit 1
236
+ }
237
+ EOH
238
+
239
+ debug("Shelling out: #{run_configuration_script}")
240
+ wrap_shell_code(run_configuration_script)
241
+ end
242
+
243
+ private
244
+
245
+ def resource_module?
246
+ module_metadata_file = File.join(config[:kitchen_root], "#{module_name}.psd1")
247
+ module_dsc_resource_folder = File.join(config[:kitchen_root], 'DSCResources')
248
+ File.exist?(module_metadata_file) &&
249
+ File.exist?(module_dsc_resource_folder)
250
+ end
251
+
252
+ def class_resource_module?
253
+ module_metadata_file = File.join(config[:kitchen_root], "#{module_name}.psd1")
254
+ module_dsc_resource_folder = File.join(config[:kitchen_root], 'DSCResources')
255
+ File.exist?(module_metadata_file) &&
256
+ !File.exist?(module_dsc_resource_folder)
257
+ end
258
+
259
+ def list_files(path)
260
+ base_directory_content = Dir.glob(File.join(path, '*'))
261
+ nested_directory_content = Dir.glob(File.join(path, '*/**/*'))
262
+ all_directory_content =([base_directory_content, nested_directory_content]).flatten
263
+
264
+ ignore_files = ['Gemfile', 'Gemfile.lock', 'README.md', 'LICENSE.txt']
265
+ all_directory_content.reject do |f|
266
+ debug("Enumerating #{f}")
267
+ ignore_files.include?(File.basename(f)) || File.directory?(f)
268
+ end
269
+ end
270
+
271
+ def module_name
272
+ File.basename(config[:kitchen_root])
273
+ end
274
+
275
+ def prepare_resource_style_directory
276
+ sandbox_base_module_path = File.join(sandbox_path, "modules/#{module_name}")
277
+
278
+ base = config[:kitchen_root]
279
+ list_files(base).each do |src|
280
+ dest = File.join(sandbox_base_module_path, src.sub("#{base}/", ''))
281
+ FileUtils.mkdir_p(File.dirname(dest))
282
+ debug("Staging #{src} ")
283
+ debug(" at #{dest}")
284
+ FileUtils.cp(src, dest, preserve: true)
285
+ end
286
+ end
287
+
288
+ def prepare_repo_style_directory
289
+ module_path = File.join(config[:kitchen_root], config[:modules_path])
290
+ sandbox_module_path = File.join(sandbox_path, 'modules')
291
+
292
+ if Dir.exist?(module_path)
293
+ debug("Moving #{module_path} to #{sandbox_module_path}")
294
+ FileUtils.cp_r(module_path, sandbox_module_path)
295
+ else
296
+ debug("The modules path #{module_path} was not found. Not moving to #{sandbox_module_path}.")
297
+ end
298
+ end
299
+
300
+ def sandboxed_configuration_script
301
+ File.join('configuration', config[:configuration_script])
302
+ end
303
+
304
+ def pad(depth = 0)
305
+ " " * depth
306
+ end
307
+
308
+ def ps_hash(obj, depth = 0)
309
+ if obj.is_a?(Hash)
310
+ obj.map { |k, v|
311
+ %{#{pad(depth + 2)}#{ps_hash(k)} = #{ps_hash(v, depth + 2)}}
312
+ }.join(";\n").insert(0, "@{\n").insert(-1, "\n#{pad(depth)}}")
313
+ elsif obj.is_a?(Array)
314
+ array_string = obj.map { |v| ps_hash(v, depth+4)}.join(",")
315
+ "#{pad(depth)}@(\n#{array_string}\n)"
316
+ else
317
+ %{"#{obj}"}
318
+ end
319
+ end
320
+
321
+ def prepare_configuration_script
322
+ configuration_script_file = File.join(config[:configuration_script_folder], config[:configuration_script])
323
+ configuration_script_path = File.join(config[:kitchen_root], configuration_script_file)
324
+ sandbox_configuration_script_path = File.join(sandbox_path, sandboxed_configuration_script)
325
+ FileUtils.mkdir_p(File.dirname(sandbox_configuration_script_path))
326
+ debug("Moving #{configuration_script_path} to #{sandbox_configuration_script_path}")
327
+ FileUtils.cp(configuration_script_path, sandbox_configuration_script_path)
328
+ end
329
+ end
330
+ end
331
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kitchen-dsc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steven Murawski
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-03-28 00:00:00.000000000 Z
11
+ date: 2016-03-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: test-kitchen