kitchen-dsc 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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