kitchen-pester 0.12.2 → 1.0.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/Rakefile +0 -2
- data/kitchen-pester.gemspec +0 -1
- data/lib/kitchen/verifier/pester.rb +335 -172
- data/lib/kitchen/verifier/pester_version.rb +1 -1
- data/lib/support/modules/PesterUtil/PesterUtil.psm1 +157 -0
- metadata +3 -3
- data/lib/support/powershell/PesterUtil/PesterUtil.psm1 +0 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3d308bdccbb4f9795482dcdac1abbe373935c4cb164e0bb1bca3d16c8d7185fe
|
4
|
+
data.tar.gz: 798da4d69f83306722efd7311104e8797611cec975af00d88168c535a2056cc7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5bebf6a71183b7c02b3dcce3c8f1769b039dd30266a4da75da95f5c9fefd7da00f42421950e543fa529cfdf3cdbf2eb41756569d13e0a91bce68af881aed3cc3
|
7
|
+
data.tar.gz: 408ac94d07a5b4a544892f325b7c70b85dbf4e9dcc9193d4505ca3307fcbda5b5e0f5a49c1a3f80cda8b8f084490cf52d7ac5169b372419c6a2075abbb21467a
|
data/Rakefile
CHANGED
data/kitchen-pester.gemspec
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
|
-
#
|
3
1
|
# Author:: Steven Murawski (<steven.murawski@gmail.com>)
|
4
2
|
#
|
5
3
|
# Copyright (C) 2015, Steven Murawski
|
@@ -16,9 +14,12 @@
|
|
16
14
|
# See the License for the specific language governing permissions and
|
17
15
|
# limitations under the License.
|
18
16
|
|
17
|
+
require "fileutils"
|
19
18
|
require "pathname"
|
19
|
+
require "kitchen/util"
|
20
20
|
require "kitchen/verifier/base"
|
21
21
|
require "kitchen/version"
|
22
|
+
require "base64"
|
22
23
|
require_relative "pester_version"
|
23
24
|
|
24
25
|
module Kitchen
|
@@ -32,9 +33,24 @@ module Kitchen
|
|
32
33
|
plugin_version Kitchen::Verifier::PESTER_VERSION
|
33
34
|
|
34
35
|
default_config :restart_winrm, false
|
35
|
-
default_config :test_folder
|
36
|
-
default_config :
|
36
|
+
default_config :test_folder, "tests"
|
37
|
+
default_config :remove_builtin_powershellget, true
|
38
|
+
default_config :remove_builtin_pester, true
|
39
|
+
default_config :skip_pester_install, false
|
40
|
+
default_config :bootstrap, {
|
41
|
+
repository_url: "https://www.powershellgallery.com/api/v2",
|
42
|
+
modules: [],
|
43
|
+
}
|
44
|
+
default_config :register_repository, []
|
45
|
+
default_config :pester_install, {
|
46
|
+
SkipPublisherCheck: true,
|
47
|
+
Force: true,
|
48
|
+
ErrorAction: "Stop",
|
49
|
+
}
|
50
|
+
default_config :install_modules, []
|
37
51
|
default_config :downloads, ["./PesterTestResults.xml"] => "./testresults"
|
52
|
+
default_config :copy_folders, []
|
53
|
+
default_config :sudo, false
|
38
54
|
|
39
55
|
# Creates a new Verifier object using the provided configuration data
|
40
56
|
# which will be merged with any default configuration.
|
@@ -64,21 +80,56 @@ module Kitchen
|
|
64
80
|
# end
|
65
81
|
def create_sandbox
|
66
82
|
super
|
67
|
-
|
83
|
+
prepare_supporting_psmodules
|
84
|
+
prepare_copy_folders
|
68
85
|
prepare_pester_tests
|
69
86
|
prepare_helpers
|
87
|
+
|
88
|
+
debug("\n\n")
|
89
|
+
debug("Sandbox content:\n")
|
90
|
+
list_files(sandbox_path).each do |f|
|
91
|
+
debug(" #{f}")
|
92
|
+
end
|
70
93
|
end
|
71
94
|
|
72
95
|
# Generates a command string which will install and configure the
|
73
96
|
# verifier software on an instance. If no work is required, then `nil`
|
74
97
|
# will be returned.
|
98
|
+
# PowerShellGet & Pester Bootstrap are done in prepare_command (after sandbox is transferred)
|
99
|
+
# so that we can use the PesterUtil.psm1
|
75
100
|
#
|
76
101
|
# @return [String] a command string
|
77
102
|
def install_command
|
78
|
-
|
79
|
-
|
103
|
+
# the sandbox has not yet been copied to the SUT.
|
104
|
+
install_command_string = <<-PS1
|
105
|
+
Write-Verbose 'Running Install Command...'
|
106
|
+
$modulesToRemove = @(
|
107
|
+
if ($#{config[:remove_builtin_powershellget]}) {
|
108
|
+
Get-module -ListAvailable -FullyQualifiedName @{ModuleName = 'PackageManagement'; RequiredVersion = '1.0.0.1'}
|
109
|
+
Get-module -ListAvailable -FullyQualifiedName @{ModuleName = 'PowerShellGet'; RequiredVersion = '1.0.0.1'}
|
110
|
+
}
|
80
111
|
|
81
|
-
|
112
|
+
if ($#{config[:remove_builtin_pester]}) {
|
113
|
+
Get-module -ListAvailable -FullyQualifiedName @{ModuleName = 'Pester'; RequiredVersion = '3.4.0'}
|
114
|
+
}
|
115
|
+
)
|
116
|
+
|
117
|
+
if ($modulesToRemove.ModuleBase.Count -eq 0) {
|
118
|
+
# for PS7 on linux
|
119
|
+
return
|
120
|
+
}
|
121
|
+
|
122
|
+
$modulesToRemove.ModuleBase | Foreach-Object {
|
123
|
+
$ModuleBaseLeaf = Split-Path -Path $_ -Leaf
|
124
|
+
if ($ModuleBaseLeaf -as [System.version]) {
|
125
|
+
Remove-Item -force -Recurse (Split-Path -Parent -Path $_) -ErrorAction SilentlyContinue
|
126
|
+
}
|
127
|
+
else {
|
128
|
+
Remove-Item -force -Recurse $_ -ErrorAction SilentlyContinue
|
129
|
+
}
|
130
|
+
}
|
131
|
+
PS1
|
132
|
+
really_wrap_shell_code(Util.outdent!(install_command_string))
|
82
133
|
end
|
83
134
|
|
84
135
|
# Generates a command string which will perform any data initialization
|
@@ -97,7 +148,10 @@ module Kitchen
|
|
97
148
|
# required, then `nil` will be returned.
|
98
149
|
#
|
99
150
|
# @return [String] a command string
|
100
|
-
def prepare_command
|
151
|
+
def prepare_command
|
152
|
+
info("Preparing the SUT and Pester dependencies...")
|
153
|
+
really_wrap_shell_code(install_command_script)
|
154
|
+
end
|
101
155
|
|
102
156
|
# Generates a command string which will invoke the main verifier
|
103
157
|
# command on the prepared instance. If no work is required, then `nil`
|
@@ -105,8 +159,6 @@ module Kitchen
|
|
105
159
|
#
|
106
160
|
# @return [String] a command string
|
107
161
|
def run_command
|
108
|
-
return if local_suite_files.empty?
|
109
|
-
|
110
162
|
really_wrap_shell_code(run_command_script)
|
111
163
|
end
|
112
164
|
|
@@ -116,7 +168,7 @@ module Kitchen
|
|
116
168
|
def call(state)
|
117
169
|
super
|
118
170
|
ensure
|
119
|
-
download_test_files(state)
|
171
|
+
download_test_files(state) unless config[:download].nil?
|
120
172
|
end
|
121
173
|
else
|
122
174
|
def call(state)
|
@@ -124,7 +176,7 @@ module Kitchen
|
|
124
176
|
rescue
|
125
177
|
# If the verifier reports failure, we need to download the files ourselves.
|
126
178
|
# Test Kitchen's base verifier doesn't have the download in an `ensure` block.
|
127
|
-
download_test_files(state)
|
179
|
+
download_test_files(state) unless config[:download].nil?
|
128
180
|
|
129
181
|
# Rethrow original exception, we still want to register the failure.
|
130
182
|
raise
|
@@ -133,8 +185,8 @@ module Kitchen
|
|
133
185
|
|
134
186
|
# private
|
135
187
|
def run_command_script
|
136
|
-
<<-
|
137
|
-
Import-Module -Name Pester -Force
|
188
|
+
<<-PS1
|
189
|
+
Import-Module -Name Pester -Force -ErrorAction Stop
|
138
190
|
|
139
191
|
$TestPath = Join-Path "#{config[:root_path]}" -ChildPath "suites"
|
140
192
|
$OutputFilePath = Join-Path "#{config[:root_path]}" -ChildPath 'PesterTestResults.xml'
|
@@ -148,148 +200,184 @@ module Kitchen
|
|
148
200
|
$host.SetShouldExit($LASTEXITCODE)
|
149
201
|
|
150
202
|
exit $LASTEXITCODE
|
151
|
-
|
203
|
+
PS1
|
204
|
+
end
|
205
|
+
|
206
|
+
def get_powershell_modules_from_nugetapi
|
207
|
+
# don't return anything is the modules subkey or bootstrap is null
|
208
|
+
return if config.dig(:bootstrap, :modules).nil?
|
209
|
+
|
210
|
+
bootstrap = config[:bootstrap]
|
211
|
+
# if the repository url is set, use that as parameter to Install-ModuleFromNuget. Default is the PSGallery url
|
212
|
+
gallery_url_param = bootstrap[:repository_url] ? "-GalleryUrl '#{bootstrap[:repository_url]}'" : ""
|
213
|
+
|
214
|
+
info("Bootstrapping environment without PowerShellGet Provider...")
|
215
|
+
Array(bootstrap[:modules]).map do |powershell_module|
|
216
|
+
if powershell_module.is_a? Hash
|
217
|
+
<<-PS1
|
218
|
+
${#{powershell_module[:Name]}} = #{ps_hash(powershell_module)}
|
219
|
+
Install-ModuleFromNuget -Module ${#{powershell_module[:Name]}} #{gallery_url_param}
|
220
|
+
PS1
|
221
|
+
else
|
222
|
+
<<-PS1
|
223
|
+
Install-ModuleFromNuget -Module @{Name = '#{powershell_module}'} #{gallery_url_param}
|
224
|
+
PS1
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
# Returns the string command to set a PS Repository
|
230
|
+
# for each PSRepo configured.
|
231
|
+
#
|
232
|
+
# @return [Array<String>] array of suite files
|
233
|
+
# @api private
|
234
|
+
def register_psrepository
|
235
|
+
return if config[:register_repository].nil?
|
236
|
+
|
237
|
+
info("Registering a new PowerShellGet Repository")
|
238
|
+
Array(config[:register_repository]).map do |psrepo|
|
239
|
+
# Using Set-PSRepo from ../../*/*/*/PesterUtil.psm1
|
240
|
+
debug("Command to set PSRepo #{psrepo[:Name]}.")
|
241
|
+
<<-PS1
|
242
|
+
Write-Host 'Registering psrepo #{psrepo[:Name]}...'
|
243
|
+
${#{psrepo[:Name]}} = #{ps_hash(psrepo)}
|
244
|
+
Set-PSRepo -Repository ${#{psrepo[:Name]}}
|
245
|
+
PS1
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
# Returns the string command set the PSGallery as trusted, and
|
250
|
+
# Install Pester from gallery based on the params from Pester_install_params config
|
251
|
+
#
|
252
|
+
# @return <String> command to install Pester Module
|
253
|
+
# @api private
|
254
|
+
def install_pester
|
255
|
+
return if config[:skip_pester_install]
|
256
|
+
|
257
|
+
pester_install_params = config[:pester_install] || {}
|
258
|
+
<<-PS1
|
259
|
+
if ((Get-PSRepository -Name PSGallery).InstallationPolicy -ne 'Trusted') {
|
260
|
+
Write-Host -Object "Trusting the PSGallery to install Pester without -Force"
|
261
|
+
Set-PSRepository -Name PSGallery -InstallationPolicy Trusted -ErrorAction SilentlyContinue
|
262
|
+
}
|
263
|
+
|
264
|
+
Write-Host "Installing Pester..."
|
265
|
+
$installPesterParams = #{ps_hash(pester_install_params)}
|
266
|
+
$installPesterParams['Name'] = 'Pester'
|
267
|
+
Install-module @installPesterParams
|
268
|
+
Write-Host 'Pester Installed.'
|
269
|
+
PS1
|
270
|
+
end
|
271
|
+
|
272
|
+
# returns a piece of PS scriptblock for each Module to install
|
273
|
+
# from gallery that has been sepcified in install_modules config.
|
274
|
+
#
|
275
|
+
# @return [Array<String>] array of PS commands.
|
276
|
+
# @api private
|
277
|
+
def install_modules_from_gallery
|
278
|
+
return if config[:install_modules].nil?
|
279
|
+
|
280
|
+
Array(config[:install_modules]).map do |powershell_module|
|
281
|
+
if powershell_module.is_a? Hash
|
282
|
+
# Sanitize variable name so that $powershell-yaml becomes $powershell_yaml
|
283
|
+
module_name = powershell_module[:Name].gsub(/[\W]/, "_")
|
284
|
+
# so we can splat that variable to install module
|
285
|
+
<<-PS1
|
286
|
+
$#{module_name} = #{ps_hash(powershell_module)}
|
287
|
+
Write-Host -NoNewline 'Installing #{module_name}'
|
288
|
+
Install-Module @#{module_name}
|
289
|
+
Write-host '... done.'
|
290
|
+
PS1
|
291
|
+
else
|
292
|
+
<<-PS1
|
293
|
+
Write-host -NoNewline 'Installing #{powershell_module} ...'
|
294
|
+
Install-Module -Name '#{powershell_module}'
|
295
|
+
Write-host '... done.'
|
296
|
+
PS1
|
297
|
+
end
|
298
|
+
end
|
152
299
|
end
|
153
300
|
|
154
301
|
def really_wrap_shell_code(code)
|
302
|
+
windows_os? ? really_wrap_windows_shell_code(code) : really_wrap_posix_shell_code(code)
|
303
|
+
end
|
304
|
+
|
305
|
+
def really_wrap_windows_shell_code(code)
|
155
306
|
wrap_shell_code(Util.outdent!(use_local_powershell_modules(code)))
|
156
307
|
end
|
157
308
|
|
309
|
+
# Writing the command to a ps1 file, adding the pwsh shebang
|
310
|
+
# invoke the file
|
311
|
+
def really_wrap_posix_shell_code(code)
|
312
|
+
if config[:sudo]
|
313
|
+
pwsh_cmd = "sudo pwsh"
|
314
|
+
else
|
315
|
+
pwsh_cmd = "pwsh"
|
316
|
+
end
|
317
|
+
|
318
|
+
my_command = <<-BASH
|
319
|
+
echo "Running as '$(whoami)'"
|
320
|
+
# Send the bash heredoc 'EOF' to the file current.ps1 using the tool cat
|
321
|
+
cat << 'EOF' > current.ps1
|
322
|
+
#!/usr/bin/env pwsh
|
323
|
+
#{Util.outdent!(use_local_powershell_modules(code))}
|
324
|
+
EOF
|
325
|
+
# create the modules folder, making sure it's done as current user (not root)
|
326
|
+
mkdir -p foo #{config[:root_path]}/modules
|
327
|
+
# Invoke the created current.ps1 file using pwsh
|
328
|
+
#{pwsh_cmd} -f current.ps1
|
329
|
+
BASH
|
330
|
+
|
331
|
+
debug(Util.outdent!(my_command))
|
332
|
+
Util.outdent!(my_command)
|
333
|
+
end
|
334
|
+
|
158
335
|
def use_local_powershell_modules(script)
|
159
|
-
<<-
|
336
|
+
<<-PS1
|
160
337
|
try {
|
338
|
+
if (!$IsLinux -and !$IsMacOs) {
|
161
339
|
Set-ExecutionPolicy Unrestricted -force
|
340
|
+
}
|
162
341
|
}
|
163
342
|
catch {
|
164
343
|
$_ | Out-String | Write-Warning
|
165
344
|
}
|
166
345
|
|
167
346
|
$global:ProgressPreference = 'SilentlyContinue'
|
168
|
-
$
|
347
|
+
$PSModPathToPrepend = Join-Path "#{config[:root_path]}" -ChildPath 'modules'
|
348
|
+
Write-Verbose "Adding '$PSModPathToPrepend' to `$Env:PSModulePath."
|
349
|
+
if (!$isLinux -and -not (Test-Path -Path $PSModPathToPrepend)) {
|
350
|
+
# if you create this folder now un Linux, it will run as root (via sudo).
|
351
|
+
$null = New-Item -Path $PSModPathToPrepend -Force -ItemType Directory
|
352
|
+
}
|
353
|
+
|
354
|
+
if ($Env:PSModulePath.Split([io.path]::PathSeparator) -notcontains $PSModPathToPrepend) {
|
355
|
+
$env:PSModulePath = @($PSModPathToPrepend, $env:PSModulePath) -Join [io.path]::PathSeparator
|
356
|
+
}
|
169
357
|
|
170
358
|
#{script}
|
171
|
-
|
359
|
+
PS1
|
172
360
|
end
|
173
361
|
|
174
362
|
def install_command_script
|
175
|
-
<<-
|
176
|
-
|
177
|
-
|
178
|
-
function Confirm-Directory {
|
179
|
-
[CmdletBinding()]
|
180
|
-
param($Path)
|
181
|
-
|
182
|
-
$Item = if (Test-Path $Path) {
|
183
|
-
Get-Item -Path $Path
|
184
|
-
}
|
185
|
-
else {
|
186
|
-
New-Item -Path $Path -ItemType Directory
|
187
|
-
}
|
188
|
-
|
189
|
-
$Item.FullName
|
190
|
-
}
|
191
|
-
|
192
|
-
function Test-Module {
|
193
|
-
[CmdletBinding()]
|
194
|
-
param($Name)
|
195
|
-
|
196
|
-
@(Get-Module -Name $Name -ListAvailable -ErrorAction SilentlyContinue).Count -gt 0
|
197
|
-
}
|
363
|
+
<<-PS1
|
364
|
+
$PSModPathToPrepend = "#{config[:root_path]}"
|
198
365
|
|
199
|
-
|
200
|
-
$VerifierDownloadPath = Confirm-Directory -Path (Join-Path #{config[:root_path]} -ChildPath 'pester')
|
366
|
+
Import-Module -ErrorAction Stop PesterUtil
|
201
367
|
|
202
|
-
|
368
|
+
#{get_powershell_modules_from_nugetapi.join("\n") unless config.dig(:bootstrap, :modules).nil?}
|
203
369
|
|
204
|
-
|
205
|
-
if (Test-Module -Name PowerShellGet) {
|
206
|
-
Import-Module PowerShellGet -Force
|
207
|
-
Import-Module PackageManagement -Force
|
370
|
+
#{register_psrepository.join("\n") unless config[:register_repository].nil?}
|
208
371
|
|
209
|
-
|
372
|
+
#{install_pester}
|
210
373
|
|
211
|
-
|
212
|
-
|
213
|
-
else {
|
214
|
-
if (-not (Test-Module -Name PsGet)){
|
215
|
-
$webClient = New-Object -TypeName System.Net.WebClient
|
216
|
-
|
217
|
-
if ($env:HTTP_PROXY){
|
218
|
-
if ($env:NO_PROXY){
|
219
|
-
Write-Host "Creating WebProxy with 'HTTP_PROXY' and 'NO_PROXY' environment variables.
|
220
|
-
$webproxy = New-Object -TypeName System.Net.WebProxy -ArgumentList $env:HTTP_PROXY, $true, $env:NO_PROXY
|
221
|
-
}
|
222
|
-
else {
|
223
|
-
Write-Host "Creating WebProxy with 'HTTP_PROXY' environment variable.
|
224
|
-
$webproxy = New-Object -TypeName System.Net.WebProxy -ArgumentList $env:HTTP_PROXY
|
225
|
-
}
|
226
|
-
|
227
|
-
$webClient.Proxy = $webproxy
|
228
|
-
}
|
229
|
-
|
230
|
-
Invoke-Expression -Command $webClient.DownloadString('http://bit.ly/GetPsGet')
|
231
|
-
}
|
232
|
-
|
233
|
-
try {
|
234
|
-
# If the module isn't already loaded, ensure we can import it.
|
235
|
-
if (-not (Get-Module -Name PsGet -ErrorAction SilentlyContinue)) {
|
236
|
-
Import-Module -Name PsGet -Force -ErrorAction Stop
|
237
|
-
}
|
238
|
-
|
239
|
-
Install-Module -Name Pester -Force
|
240
|
-
}
|
241
|
-
catch {
|
242
|
-
Write-Host "Installing from Github"
|
243
|
-
|
244
|
-
$zipFile = Join-Path (Get-Item -Path $VerifierDownloadPath).FullName -ChildPath "pester.zip"
|
245
|
-
|
246
|
-
if (-not (Test-Path $zipfile)) {
|
247
|
-
$source = 'https://github.com/pester/Pester/archive/4.10.1.zip'
|
248
|
-
$webClient = New-Object -TypeName Net.WebClient
|
249
|
-
|
250
|
-
if ($env:HTTP_PROXY) {
|
251
|
-
if ($env:NO_PROXY) {
|
252
|
-
Write-Host "Creating WebProxy with 'HTTP_PROXY' and 'NO_PROXY' environment variables."
|
253
|
-
$webproxy = New-Object -TypeName System.Net.WebProxy -ArgumentList $env:HTTP_PROXY, $true, $env:NO_PROXY
|
254
|
-
}
|
255
|
-
else {
|
256
|
-
Write-Host "Creating WebProxy with 'HTTP_PROXY' environment variable."
|
257
|
-
$webproxy = New-Object -TypeName System.Net.WebProxy -ArgumentList $env:HTTP_PROXY
|
258
|
-
}
|
259
|
-
|
260
|
-
$webClient.Proxy = $webproxy
|
261
|
-
}
|
262
|
-
|
263
|
-
[IO.File]::WriteAllBytes($zipfile, $webClient.DownloadData($source))
|
264
|
-
|
265
|
-
[GC]::Collect()
|
266
|
-
Write-Host "Downloaded Pester.zip"
|
267
|
-
}
|
268
|
-
|
269
|
-
Write-Host "Creating Shell.Application COM object"
|
270
|
-
$shellcom = New-Object -ComObject Shell.Application
|
271
|
-
|
272
|
-
Write-Host "Creating COM object for zip file."
|
273
|
-
$zipcomobject = $shellcom.Namespace($zipfile)
|
274
|
-
|
275
|
-
Write-Host "Creating COM object for module destination."
|
276
|
-
$destination = $shellcom.Namespace($VerifierModulePath)
|
277
|
-
|
278
|
-
Write-Host "Unpacking zip file."
|
279
|
-
$destination.CopyHere($zipcomobject.Items(), 0x610)
|
280
|
-
|
281
|
-
Rename-Item -Path (Join-Path $VerifierModulePath -ChildPath "Pester-4.10.1") -NewName 'Pester' -Force
|
282
|
-
}
|
283
|
-
}
|
284
|
-
}
|
285
|
-
|
286
|
-
if (-not (Test-Module Pester)) {
|
287
|
-
throw "Unable to install Pester. Please include Pester in your base image or install during your converge."
|
288
|
-
}
|
289
|
-
EOH
|
374
|
+
#{install_modules_from_gallery.join("\n") unless config[:install_modules].nil?}
|
375
|
+
PS1
|
290
376
|
end
|
291
377
|
|
292
378
|
def restart_winrm_service
|
379
|
+
return unless verifier.windows_os?
|
380
|
+
|
293
381
|
cmd = "schtasks /Create /TN restart_winrm /TR " \
|
294
382
|
'"powershell -Command Restart-Service winrm" ' \
|
295
383
|
"/SC ONCE /ST 00:00 "
|
@@ -301,8 +389,9 @@ module Kitchen
|
|
301
389
|
end
|
302
390
|
|
303
391
|
def download_test_files(state)
|
304
|
-
|
392
|
+
return if config[:downloads].nil?
|
305
393
|
|
394
|
+
info("Downloading test result files from #{instance.to_str}")
|
306
395
|
instance.transport.connection(state) do |conn|
|
307
396
|
config[:downloads].to_h.each do |remotes, local|
|
308
397
|
debug("Downloading #{Array(remotes).join(", ")} to #{local}")
|
@@ -319,29 +408,25 @@ module Kitchen
|
|
319
408
|
#
|
320
409
|
# @return [Array<String>] array of suite files
|
321
410
|
# @api private
|
322
|
-
|
323
411
|
def suite_test_folder
|
324
412
|
@suite_test_folder ||= File.join(test_folder, config[:suite_name])
|
325
413
|
end
|
326
414
|
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
def
|
332
|
-
|
333
|
-
end
|
334
|
-
|
335
|
-
def local_suite_files
|
336
|
-
suite = suite_level_glob
|
337
|
-
suite_verifier = suite_verifier_level_glob
|
338
|
-
(suite << suite_verifier).flatten!.reject do |f|
|
339
|
-
File.directory?(f)
|
340
|
-
end
|
415
|
+
# Returns the current file's parent folder's full path.
|
416
|
+
#
|
417
|
+
# @return [string]
|
418
|
+
# @api private
|
419
|
+
def script_root
|
420
|
+
@script_root ||= File.dirname(__FILE__)
|
341
421
|
end
|
342
422
|
|
343
|
-
|
344
|
-
|
423
|
+
# Returns the absolute path of the Supporting PS module to
|
424
|
+
# be copied to the SUT via the Sandbox.
|
425
|
+
#
|
426
|
+
# @return [string]
|
427
|
+
# @api private
|
428
|
+
def support_psmodule_folder
|
429
|
+
@support_psmodule_folder ||= Pathname.new(File.join(script_root, "../../support/modules/PesterUtil")).cleanpath
|
345
430
|
end
|
346
431
|
|
347
432
|
# Returns an Array of common helper filenames currently residing on the
|
@@ -369,50 +454,128 @@ module Kitchen
|
|
369
454
|
end
|
370
455
|
end
|
371
456
|
|
372
|
-
#
|
457
|
+
# Creates a PowerShell hashtable from a ruby map.
|
458
|
+
# The only types supported for now are hash, array, string and Boolean.
|
373
459
|
#
|
374
460
|
# @api private
|
375
|
-
def
|
376
|
-
|
461
|
+
def ps_hash(obj, depth = 0)
|
462
|
+
if [true, false].include? obj
|
463
|
+
%{$#{obj}} # Return $true or $false when value is a bool
|
464
|
+
elsif obj.is_a?(Hash)
|
465
|
+
obj.map do |k, v|
|
466
|
+
# Format "Key = Value" enabling recursion
|
467
|
+
%{#{pad(depth + 2)}#{ps_hash(k)} = #{ps_hash(v, depth + 2)}}
|
468
|
+
end
|
469
|
+
.join("\n") # append \n to the key/value definitions
|
470
|
+
.insert(0, "@{\n") # prepend @{\n
|
471
|
+
.insert(-1, "\n#{pad(depth)}}\n") # append \n}\n
|
472
|
+
|
473
|
+
elsif obj.is_a?(Array)
|
474
|
+
array_string = obj.map { |v| ps_hash(v, depth + 4) }.join(",")
|
475
|
+
"#{pad(depth)}@(\n#{array_string}\n)"
|
476
|
+
else
|
477
|
+
# When the object is not a string nor a hash or array, it will be quoted as a string.
|
478
|
+
# In most cases, PS is smart enough to convert back to the type it needs.
|
479
|
+
"'" + obj.to_s + "'"
|
480
|
+
end
|
481
|
+
end
|
377
482
|
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
483
|
+
# returns the path of the modules subfolder
|
484
|
+
# in the sandbox, where PS Modules and folders will be copied to.
|
485
|
+
#
|
486
|
+
# @api private
|
487
|
+
def sandbox_module_path
|
488
|
+
File.join(sandbox_path, "modules")
|
489
|
+
end
|
490
|
+
|
491
|
+
# copy files into the 'modules' folder of the sandbox,
|
492
|
+
# so that copied folders can be discovered with the updated $Env:PSModulePath.
|
493
|
+
#
|
494
|
+
# @api private
|
495
|
+
def prepare_copy_folders
|
496
|
+
return if config[:copy_folders].nil?
|
497
|
+
|
498
|
+
info("Preparing to copy specified folders to #{sandbox_module_path}.")
|
499
|
+
kitchen_root_path = config[:kitchen_root]
|
500
|
+
config[:copy_folders].each do |folder|
|
501
|
+
debug("copying #{folder}")
|
502
|
+
folder_to_copy = File.join(kitchen_root_path, folder)
|
503
|
+
copy_if_src_exists(folder_to_copy, sandbox_module_path)
|
383
504
|
end
|
384
505
|
end
|
385
506
|
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
507
|
+
# returns an array of string
|
508
|
+
# Creates a flat list of files contained in a folder.
|
509
|
+
# This is useful when trying to debug what has been copied to
|
510
|
+
# the sandbox.
|
511
|
+
#
|
512
|
+
# @return [Array<String>] array of files in a folder
|
513
|
+
# @api private
|
514
|
+
def list_files(path)
|
515
|
+
base_directory_content = Dir.glob(File.join(path, "*"))
|
516
|
+
nested_directory_content = Dir.glob(File.join(path, "*/**/*"))
|
517
|
+
[base_directory_content, nested_directory_content].flatten
|
518
|
+
end
|
519
|
+
|
520
|
+
# Copies all test suite files into the suites directory in the sandbox.
|
521
|
+
#
|
522
|
+
# @api private
|
523
|
+
def prepare_pester_tests
|
524
|
+
info("Preparing to copy files from '#{suite_test_folder}' to the SUT.")
|
525
|
+
sandboxed_suites_path = File.join(sandbox_path, "suites")
|
526
|
+
copy_if_src_exists(suite_test_folder, sandboxed_suites_path)
|
527
|
+
end
|
528
|
+
|
529
|
+
def prepare_supporting_psmodules
|
530
|
+
debug("Preparing to copy files from '#{support_psmodule_folder}' to the SUT.")
|
531
|
+
sandbox_module_path = File.join(sandbox_path, "modules")
|
532
|
+
copy_if_src_exists(support_psmodule_folder, sandbox_module_path)
|
393
533
|
end
|
394
534
|
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
535
|
+
# Copies a folder recursively preserving its layers,
|
536
|
+
# mostly used to copy to the sandbox.
|
537
|
+
#
|
538
|
+
# @api private
|
539
|
+
def copy_if_src_exists(src_to_validate, destination)
|
540
|
+
unless Dir.exist?(src_to_validate)
|
541
|
+
info("The path #{src_to_validate} was not found. Not copying to #{destination}.")
|
542
|
+
return
|
399
543
|
end
|
544
|
+
|
545
|
+
debug("Moving #{src_to_validate} to #{destination}")
|
546
|
+
unless Dir.exist?(destination)
|
547
|
+
FileUtils.mkdir_p(destination)
|
548
|
+
debug("Folder '#{destination}' created.")
|
549
|
+
end
|
550
|
+
FileUtils.mkdir_p(File.join(destination, "__bugfix"))
|
551
|
+
FileUtils.cp_r(src_to_validate, destination, preserve: true)
|
400
552
|
end
|
401
553
|
|
554
|
+
# returns the absolute path of the folders containing the
|
555
|
+
# test suites, use default if not set.
|
556
|
+
#
|
557
|
+
# @api private
|
402
558
|
def test_folder
|
403
|
-
|
404
|
-
|
405
|
-
absolute_test_folder
|
559
|
+
config[:test_folder].nil? ? config[:test_base_path] : absolute_test_folder
|
406
560
|
end
|
407
561
|
|
562
|
+
# returns the absolute path of the relative folders containing the
|
563
|
+
# test suites, use default i not set.
|
564
|
+
#
|
565
|
+
# @api private
|
408
566
|
def absolute_test_folder
|
409
567
|
path = (Pathname.new config[:test_folder]).realpath
|
410
568
|
integration_path = File.join(path, "integration")
|
411
|
-
|
412
|
-
|
413
|
-
integration_path
|
569
|
+
Dir.exist?(integration_path) ? integration_path : path
|
414
570
|
end
|
415
571
|
|
572
|
+
# returns a string of space of the specified depth.
|
573
|
+
# This is used to pad messages or when building PS hashtables.
|
574
|
+
#
|
575
|
+
# @api private
|
576
|
+
def pad(depth = 0)
|
577
|
+
" " * depth
|
578
|
+
end
|
416
579
|
end
|
417
580
|
end
|
418
581
|
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
|
2
|
+
|
3
|
+
function Install-ModuleFromNuget {
|
4
|
+
[CmdletBinding()]
|
5
|
+
param (
|
6
|
+
[hashtable]
|
7
|
+
$Module,
|
8
|
+
|
9
|
+
[string]
|
10
|
+
$GalleryUrl = 'https://www.powershellgallery.com/api/v2'
|
11
|
+
)
|
12
|
+
|
13
|
+
$tempPath = [System.IO.Path]::GetTempPath()
|
14
|
+
$zipFileName = "{0}.{1}.{2}" -f $Module.Name, $Module.Version, 'zip'
|
15
|
+
$downloadedZip = Join-Path -Path $tempPath $zipFileName
|
16
|
+
$ModulePath = Join-Path -Path $PSHome -ChildPath 'Modules'
|
17
|
+
$ModuleFolder = Join-Path -Path $ModulePath -ChildPath $Module.Name
|
18
|
+
|
19
|
+
if ((Test-Path $ModuleFolder) -and ($PSVersionTable.PSVersion.Major -lt 5 -or $module.Force)) {
|
20
|
+
# Check if available version is correct
|
21
|
+
$ModuleManifest = Join-Path -Path $ModuleFolder -ChildPath "$($Module.Name).psd1"
|
22
|
+
if ((Test-Path -Path $ModuleManifest) -and -not $Module.Force) {
|
23
|
+
# Import-PowerShellDataFile only exists since 5.1
|
24
|
+
$ManifestInfo = Import-LocalizedData -BaseDirectory (Split-Path -Parent -Path $ModuleManifest) -FileName $Module.Name
|
25
|
+
$ModuleVersionNoPreRelease = $Module.Version -replace '-.*$'
|
26
|
+
# Compare the version in manifest with version required without Pre-release
|
27
|
+
if ($ManifestInfo.ModuleVersion -eq $ModuleVersionNoPreRelease) {
|
28
|
+
Write-Host "Module $($Module.Name) already installed, skipping."
|
29
|
+
return
|
30
|
+
}
|
31
|
+
else {
|
32
|
+
Write-Host "Module $($Module.Name) found with version '$($ManifestInfo.ModuleVersion)', expecting '$ModuleVersionNoPreRelease'."
|
33
|
+
}
|
34
|
+
}
|
35
|
+
else {
|
36
|
+
# if incorrect, remove it before install
|
37
|
+
Remove-Item -Recurse -Force -Path $ModuleFolder
|
38
|
+
}
|
39
|
+
}
|
40
|
+
elseif ($PSVersionTable.PSVersion.Major -gt 5) {
|
41
|
+
# skip if the version already exists or if force is enabled
|
42
|
+
$ModuleVersionNoPreRelease = $Module.Version -replace '-.*$'
|
43
|
+
$ModuleFolder = Join-Path -Path $ModuleFolder -ChildPath $ModuleVersionNoPreRelease
|
44
|
+
if (-not $Module.Force -and (Test-Path -Path $ModuleFolder)) {
|
45
|
+
Write-Verbose -Message "Module already installed."
|
46
|
+
return
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
if (-not (Test-Path $ModuleFolder)) {
|
51
|
+
$null = New-Item -Path $ModuleFolder -force -ItemType Directory
|
52
|
+
}
|
53
|
+
|
54
|
+
$urlSuffix = "/package/$($Module.Name)/$($Module.Version)".TrimEnd('/')
|
55
|
+
$nupkgUrl = $GalleryUrl.TrimEnd('/') + '/' + $urlSuffix.Trim('/')
|
56
|
+
$webclient = New-Object 'system.net.webclient'
|
57
|
+
|
58
|
+
if ($env:HTTP_PROXY){
|
59
|
+
if ($env:NO_PROXY){
|
60
|
+
Write-Host "Creating WebProxy with 'HTTP_PROXY' and 'NO_PROXY' environment variables."
|
61
|
+
$webproxy = New-Object -TypeName System.Net.WebProxy -ArgumentList $env:HTTP_PROXY, $true, $env:NO_PROXY
|
62
|
+
}
|
63
|
+
else {
|
64
|
+
Write-Host "Creating WebProxy with 'HTTP_PROXY' environment variable."
|
65
|
+
$webproxy = New-Object -TypeName System.Net.WebProxy -ArgumentList $env:HTTP_PROXY
|
66
|
+
}
|
67
|
+
|
68
|
+
$webclient.Proxy = $webproxy
|
69
|
+
}
|
70
|
+
|
71
|
+
Write-Verbose -Message "Downloading Package from $nupkgUrl"
|
72
|
+
try {
|
73
|
+
if (Test-Path $downloadedZip) {
|
74
|
+
Remove-Item -Force -ErrorAction SilentlyContinue -Path $downloadedZip
|
75
|
+
}
|
76
|
+
$webclient.DownloadFile($nupkgUrl, $downloadedZip)
|
77
|
+
}
|
78
|
+
catch {
|
79
|
+
Write-Error "Error trying to download nupkg '$nupkgUrl' to '$downloadedZip'."
|
80
|
+
throw $_
|
81
|
+
}
|
82
|
+
|
83
|
+
if (-not (Test-Path -Path $downloadedZip)) {
|
84
|
+
throw "Error trying to download nupkg '$nupkgUrl' to '$downloadedZip'."
|
85
|
+
}
|
86
|
+
|
87
|
+
# Test to see if Expand-Archive is available first
|
88
|
+
if (Get-Command Expand-Archive) {
|
89
|
+
Expand-Archive -Path $downloadedZip -DestinationPath $ModuleFolder -Force
|
90
|
+
}
|
91
|
+
else {
|
92
|
+
# Fall back to COM object for Shell.Application zip extraction
|
93
|
+
Write-Host "Creating COM object for zip file '$downloadedZip'."
|
94
|
+
$shellcom = New-Object -ComObject Shell.Application
|
95
|
+
$zipcomobject = $shellcom.Namespace($downloadedZip)
|
96
|
+
$destination = $shellcom.Namespace($ModuleFolder)
|
97
|
+
$destination.CopyHere($zipcomobject.Items(), 0x610)
|
98
|
+
Write-Host "Nupkg installed at $ModuleFolder"
|
99
|
+
}
|
100
|
+
|
101
|
+
[GC]::Collect()
|
102
|
+
}
|
103
|
+
|
104
|
+
function Set-PSRepo {
|
105
|
+
param(
|
106
|
+
[Parameter(Mandatory)]
|
107
|
+
$Repository
|
108
|
+
)
|
109
|
+
if (-not (Get-Command Get-PSRepository) -and (Get-Command Get-PackageSource)) {
|
110
|
+
# Old version of PSGet do not have a *-PSrepository but have *-PackageSource instead.
|
111
|
+
if (Get-PackageSource -Name $Repository.Name) {
|
112
|
+
Set-PackageSource @Repository
|
113
|
+
}
|
114
|
+
else {
|
115
|
+
Register-PackageSource @Repository
|
116
|
+
}
|
117
|
+
}
|
118
|
+
elseif (Get-Command Get-PSRepository) {
|
119
|
+
if (Get-PSRepository -Name $Repository.Name -ErrorAction SilentlyContinue) {
|
120
|
+
# The repo exists, we should use Set-PSRepository and splat parameters
|
121
|
+
Set-PSRepository @Repository
|
122
|
+
}
|
123
|
+
else {
|
124
|
+
# The repo does not exist, use Register-PSRepository and splat
|
125
|
+
Register-PSRepository @Repository
|
126
|
+
}
|
127
|
+
}
|
128
|
+
else {
|
129
|
+
throw "Cannot Set PS Repository, command Set or Register for PSRepository or PackageSource not found."
|
130
|
+
}
|
131
|
+
}
|
132
|
+
|
133
|
+
function ConvertFrom-PesterOutputObject {
|
134
|
+
param (
|
135
|
+
[parameter(ValueFromPipeline=$true)]
|
136
|
+
[object]
|
137
|
+
$InputObject
|
138
|
+
)
|
139
|
+
begin {
|
140
|
+
$PesterModule = Import-Module Pester -Passthru
|
141
|
+
}
|
142
|
+
process {
|
143
|
+
$DescribeGroup = $InputObject.testresult | Group-Object Describe
|
144
|
+
foreach ($DescribeBlock in $DescribeGroup) {
|
145
|
+
$PesterModule.Invoke({Write-Screen $args[0]}, "Describing $($DescribeBlock.Name)")
|
146
|
+
$ContextGroup = $DescribeBlock.group | Group-Object Context
|
147
|
+
foreach ($ContextBlock in $ContextGroup) {
|
148
|
+
$PesterModule.Invoke({Write-Screen $args[0]}, "`tContext $($subheader.name)")
|
149
|
+
foreach ($TestResult in $ContextBlock.group) {
|
150
|
+
$PesterModule.Invoke({Write-PesterResult $args[0]}, $TestResult)
|
151
|
+
}
|
152
|
+
}
|
153
|
+
}
|
154
|
+
|
155
|
+
$PesterModule.Invoke({Write-PesterReport $args[0]}, $InputObject)
|
156
|
+
}
|
157
|
+
}
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kitchen-pester
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.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: 2020-
|
11
|
+
date: 2020-08-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -105,7 +105,7 @@ files:
|
|
105
105
|
- kitchen-pester.gemspec
|
106
106
|
- lib/kitchen/verifier/pester.rb
|
107
107
|
- lib/kitchen/verifier/pester_version.rb
|
108
|
-
- lib/support/
|
108
|
+
- lib/support/modules/PesterUtil/PesterUtil.psm1
|
109
109
|
homepage: https://github.com/test-kitchen/kitchen-pester
|
110
110
|
licenses:
|
111
111
|
- Apache-2.0
|
@@ -1,24 +0,0 @@
|
|
1
|
-
function ConvertFrom-PesterOutputObject {
|
2
|
-
param (
|
3
|
-
[parameter(ValueFromPipeline=$true)]
|
4
|
-
[object]
|
5
|
-
$InputObject
|
6
|
-
)
|
7
|
-
begin {
|
8
|
-
$PesterModule = Import-Module Pester -Passthru
|
9
|
-
}
|
10
|
-
process {
|
11
|
-
$DescribeGroup = $InputObject.testresult | Group-Object Describe
|
12
|
-
foreach ($DescribeBlock in $DescribeGroup) {
|
13
|
-
$PesterModule.Invoke({Write-Screen $args[0]}, "Describing $($DescribeBlock.Name)")
|
14
|
-
$ContextGroup = $DescribeBlock.group | Group-Object Context
|
15
|
-
foreach ($ContextBlock in $ContextGroup) {
|
16
|
-
$PesterModule.Invoke({Write-Screen $args[0]}, "`tContext $($subheader.name)")
|
17
|
-
foreach ($TestResult in $ContextBlock.group) {
|
18
|
-
$PesterModule.Invoke({Write-PesterResult $args[0]}, $TestResult)
|
19
|
-
}
|
20
|
-
}
|
21
|
-
}
|
22
|
-
$PesterModule.Invoke({Write-PesterReport $args[0]}, $InputObject)
|
23
|
-
}
|
24
|
-
}
|