Class: Kitchen::Verifier::Pester

Inherits:
Base
  • Object
show all
Defined in:
lib/kitchen/verifier/pester.rb

Instance Method Summary collapse

Constructor Details

#initialize(config = {}) ⇒ Pester

Creates a new Verifier object using the provided configuration data which will be merged with any default configuration.

Parameters:

  • config (Hash) (defaults to: {})

    provided verifier configuration



43
44
45
# File 'lib/kitchen/verifier/pester.rb', line 43

def initialize(config = {})
  init_config(config)
end

Instance Method Details

#absolute_test_folderObject



408
409
410
411
412
413
414
# File 'lib/kitchen/verifier/pester.rb', line 408

def absolute_test_folder
  path = (Pathname.new config[:test_folder]).realpath
  integration_path = File.join(path, "integration")
  return path unless Dir.exist?(integration_path)

  integration_path
end

#call(state) ⇒ Object



116
117
118
119
120
# File 'lib/kitchen/verifier/pester.rb', line 116

def call(state)
  super
ensure
  download_test_files(state)
end

#create_sandboxObject

Creates a temporary directory on the local workstation into which verifier related files and directories can be copied or created. The contents of this directory will be copied over to the instance before invoking the verifier’s run command. After this method completes, it is expected that the contents of the sandbox is complete and ready for copy to the remote instance.

Note: any subclasses would be well advised to call super first when overriding this method, for example:

Examples:

overriding #create_sandbox


class MyVerifier < Kitchen::Verifier::Base
  def create_sandbox
    super
    # any further file copies, preparations, etc.
  end
end


65
66
67
68
69
70
# File 'lib/kitchen/verifier/pester.rb', line 65

def create_sandbox
  super
  prepare_powershell_modules
  prepare_pester_tests
  prepare_helpers
end

#download_test_files(state) ⇒ Object



303
304
305
306
307
308
309
310
311
312
313
314
# File 'lib/kitchen/verifier/pester.rb', line 303

def download_test_files(state)
  info("Downloading test result files from #{instance.to_str}")

  instance.transport.connection(state) do |conn|
    config[:downloads].to_h.each do |remotes, local|
      debug("Downloading #{Array(remotes).join(", ")} to #{local}")
      conn.download(remotes, local)
    end
  end

  debug("Finished downloading test result files from #{instance.to_str}")
end

#helper_filesArray<String>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns an Array of common helper filenames currently residing on the local workstation.

Returns:

  • (Array<String>)

    array of helper files



352
353
354
355
# File 'lib/kitchen/verifier/pester.rb', line 352

def helper_files
  glob = Dir.glob(File.join(test_folder, "helpers", "*/**/*"))
  glob.reject { |f| File.directory?(f) }
end

#init_commandString

Generates a command string which will perform any data initialization or configuration required after the verifier software is installed but before the sandbox has been transferred to the instance. If no work is required, then nil will be returned.

Returns:

  • (String)

    a command string



90
91
92
# File 'lib/kitchen/verifier/pester.rb', line 90

def init_command
  restart_winrm_service if config[:restart_winrm]
end

#install_commandString

Generates a command string which will install and configure the verifier software on an instance. If no work is required, then nil will be returned.

Returns:

  • (String)

    a command string



77
78
79
80
81
82
# File 'lib/kitchen/verifier/pester.rb', line 77

def install_command
  return if local_suite_files.empty?
  return if config[:use_local_pester_module]

  really_wrap_shell_code(install_command_script)
end

#install_command_scriptObject



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
# File 'lib/kitchen/verifier/pester.rb', line 174

def install_command_script
  <<-EOH
    [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12

    function Confirm-Directory {
        [CmdletBinding()]
        param($Path)

        $Item = if (Test-Path $Path) {
            Get-Item -Path $Path
        }
        else {
            New-Item -Path $Path -ItemType Directory
        }

        $Item.FullName
    }

    function Test-Module {
        [CmdletBinding()]
        param($Name)

        @(Get-Module -Name $Name -ListAvailable -ErrorAction SilentlyContinue).Count -gt 0
    }

    $VerifierModulePath = Confirm-Directory -Path (Join-Path #{config[:root_path]} -ChildPath 'modules')
    $VerifierDownloadPath = Confirm-Directory -Path (Join-Path #{config[:root_path]} -ChildPath 'pester')

    $env:PSModulePath = "$VerifierModulePath;$PSModulePath"

    if (-not (Test-Module -Name Pester)) {
        if (Test-Module -Name PowerShellGet) {
            Import-Module PowerShellGet -Force
            Import-Module PackageManagement -Force

            Get-PackageProvider -Name NuGet -Force > $null

            Install-Module Pester -Force
        }
        else {
            if (-not (Test-Module -Name PsGet)){
                $webClient = New-Object -TypeName System.Net.WebClient

                if ($env:HTTP_PROXY){
                    if ($env:NO_PROXY){
                        Write-Host "Creating WebProxy with 'HTTP_PROXY' and 'NO_PROXY' environment variables.
                        $webproxy = New-Object -TypeName System.Net.WebProxy -ArgumentList $env:HTTP_PROXY, $true, $env:NO_PROXY
                    }
                    else {
                        Write-Host "Creating WebProxy with 'HTTP_PROXY' environment variable.
                        $webproxy = New-Object -TypeName System.Net.WebProxy -ArgumentList $env:HTTP_PROXY
                    }

                    $webClient.Proxy = $webproxy
                }

                Invoke-Expression -Command $webClient.DownloadString('http://bit.ly/GetPsGet')
            }

            try {
                # If the module isn't already loaded, ensure we can import it.
                if (-not (Get-Module -Name PsGet -ErrorAction SilentlyContinue)) {
                    Import-Module -Name PsGet -Force -ErrorAction Stop
                }

                Install-Module -Name Pester -Force
            }
            catch {
                Write-Host "Installing from Github"

                $zipFile = Join-Path (Get-Item -Path $VerifierDownloadPath).FullName -ChildPath "pester.zip"

                if (-not (Test-Path $zipfile)) {
                    $source = 'https://github.com/pester/Pester/archive/4.10.1.zip'
                    $webClient = New-Object -TypeName Net.WebClient

                    if ($env:HTTP_PROXY) {
                        if ($env:NO_PROXY) {
                            Write-Host "Creating WebProxy with 'HTTP_PROXY' and 'NO_PROXY' environment variables."
                            $webproxy = New-Object -TypeName System.Net.WebProxy -ArgumentList $env:HTTP_PROXY, $true, $env:NO_PROXY
                        }
                        else {
                            Write-Host "Creating WebProxy with 'HTTP_PROXY' environment variable."
                            $webproxy = New-Object -TypeName System.Net.WebProxy -ArgumentList $env:HTTP_PROXY
                        }

                        $webClient.Proxy = $webproxy
                    }

                    [IO.File]::WriteAllBytes($zipfile, $webClient.DownloadData($source))

                    [GC]::Collect()
                    Write-Host "Downloaded Pester.zip"
                }

                Write-Host "Creating Shell.Application COM object"
                $shellcom = New-Object -ComObject Shell.Application

                Write-Host "Creating COM object for zip file."
                $zipcomobject = $shellcom.Namespace($zipfile)

                Write-Host "Creating COM object for module destination."
                $destination = $shellcom.Namespace($VerifierModulePath)

                Write-Host "Unpacking zip file."
                $destination.CopyHere($zipcomobject.Items(), 0x610)

                Rename-Item -Path (Join-Path $VerifierModulePath -ChildPath "Pester-4.10.1") -NewName 'Pester' -Force
            }
        }
    }

    if (-not (Test-Module Pester)) {
        throw "Unable to install Pester.  Please include Pester in your base image or install during your converge."
    }
  EOH
end

#local_suite_filesObject



335
336
337
338
339
340
341
# File 'lib/kitchen/verifier/pester.rb', line 335

def local_suite_files
  suite = suite_level_glob
  suite_verifier = suite_verifier_level_glob
  (suite << suite_verifier).flatten!.reject do |f|
    File.directory?(f)
  end
end

#prepare_commandString

Generates a command string which will perform any commands or configuration required just before the main verifier run command but after the sandbox has been transferred to the instance. If no work is required, then nil will be returned.

Returns:

  • (String)

    a command string



100
# File 'lib/kitchen/verifier/pester.rb', line 100

def prepare_command; end

#prepare_helpersObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Copies all common testing helper files into the suites directory in the sandbox.



361
362
363
364
365
366
367
368
369
370
# File 'lib/kitchen/verifier/pester.rb', line 361

def prepare_helpers
  base = File.join(test_folder, "helpers")

  helper_files.each do |src|
    dest = File.join(sandbox_path, src.sub("#{base}/", ""))
    debug("Copying #{src} to #{dest}")
    FileUtils.mkdir_p(File.dirname(dest))
    FileUtils.cp(src, dest, preserve: true)
  end
end

#prepare_pester_testsObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Copies all test suite files into the suites directory in the sandbox.



375
376
377
378
379
380
381
382
383
384
# File 'lib/kitchen/verifier/pester.rb', line 375

def prepare_pester_tests
  info("Preparing to copy files from #{suite_test_folder} to the SUT.")

  local_suite_files.each do |src|
    dest = sandboxify_path(src)
    debug("Copying #{src} to #{dest}")
    FileUtils.mkdir_p(File.dirname(dest))
    FileUtils.cp(src, dest, preserve: true)
  end
end

#prepare_powershell_module(name) ⇒ Object



386
387
388
389
390
391
392
393
# File 'lib/kitchen/verifier/pester.rb', line 386

def prepare_powershell_module(name)
  FileUtils.mkdir_p(File.join(sandbox_path, "modules/#{name}"))
  FileUtils.cp(
    File.join(File.dirname(__FILE__), "../../support/powershell/#{name}/#{name}.psm1"),
    File.join(sandbox_path, "modules/#{name}/#{name}.psm1"),
    preserve: true
  )
end

#prepare_powershell_modulesObject



395
396
397
398
399
400
# File 'lib/kitchen/verifier/pester.rb', line 395

def prepare_powershell_modules
  info("Preparing to copy supporting powershell modules.")
  %w{PesterUtil}.each do |module_name|
    prepare_powershell_module module_name
  end
end

#really_wrap_shell_code(code) ⇒ Object



154
155
156
# File 'lib/kitchen/verifier/pester.rb', line 154

def really_wrap_shell_code(code)
  wrap_shell_code(Util.outdent!(use_local_powershell_modules(code)))
end

#restart_winrm_serviceObject



292
293
294
295
296
297
298
299
300
301
# File 'lib/kitchen/verifier/pester.rb', line 292

def restart_winrm_service
  cmd = "schtasks /Create /TN restart_winrm /TR " \
        '"powershell -Command Restart-Service winrm" ' \
        "/SC ONCE /ST 00:00 "
  wrap_shell_code(Util.outdent!(<<-CMD
    #{cmd}
    schtasks /RUN /TN restart_winrm
  CMD
                               ))
end

#run_commandString

Generates a command string which will invoke the main verifier command on the prepared instance. If no work is required, then nil will be returned.

Returns:

  • (String)

    a command string



107
108
109
110
111
# File 'lib/kitchen/verifier/pester.rb', line 107

def run_command
  return if local_suite_files.empty?

  really_wrap_shell_code(run_command_script)
end

#run_command_scriptObject

private



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/kitchen/verifier/pester.rb', line 135

def run_command_script
  <<-CMD
    Import-Module -Name Pester -Force

    $TestPath = Join-Path "#{config[:root_path]}" -ChildPath "suites"
    $OutputFilePath = Join-Path "#{config[:root_path]}" -ChildPath 'PesterTestResults.xml'

    $options = New-PesterOption -TestSuiteName "Pester - #{instance.to_str}"

    $result = Invoke-Pester -Script $TestPath -OutputFile $OutputFilePath -OutputFormat NUnitXml -PesterOption $options -PassThru
    $result | Export-CliXml -Path (Join-Path -Path $TestPath -ChildPath 'result.xml')

    $LASTEXITCODE = $result.FailedCount
    $host.SetShouldExit($LASTEXITCODE)

    exit $LASTEXITCODE
  CMD
end

#sandboxify_path(path) ⇒ Object



343
344
345
# File 'lib/kitchen/verifier/pester.rb', line 343

def sandboxify_path(path)
  File.join(sandbox_path, "suites", path.sub(%r{#{suite_test_folder}/}i, ""))
end

#suite_level_globObject



327
328
329
# File 'lib/kitchen/verifier/pester.rb', line 327

def suite_level_glob
  Dir.glob(File.join(suite_test_folder, "*"))
end

#suite_test_folderArray<String>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns an Array of test suite filenames for the related suite currently residing on the local workstation. Any special provisioner-specific directories (such as a Chef roles/ directory) are excluded.

Returns:

  • (Array<String>)

    array of suite files



323
324
325
# File 'lib/kitchen/verifier/pester.rb', line 323

def suite_test_folder
  @suite_test_folder ||= File.join(test_folder, config[:suite_name])
end

#suite_verifier_level_globObject



331
332
333
# File 'lib/kitchen/verifier/pester.rb', line 331

def suite_verifier_level_glob
  Dir.glob(File.join(suite_test_folder, "*/**/*"))
end

#test_folderObject



402
403
404
405
406
# File 'lib/kitchen/verifier/pester.rb', line 402

def test_folder
  return config[:test_base_path] if config[:test_folder].nil?

  absolute_test_folder
end

#use_local_powershell_modules(script) ⇒ Object



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/kitchen/verifier/pester.rb', line 158

def use_local_powershell_modules(script)
  <<-EOH
    try {
        Set-ExecutionPolicy Unrestricted -force
    }
    catch {
        $_ | Out-String | Write-Warning
    }

    $global:ProgressPreference = 'SilentlyContinue'
    $env:PSModulePath = "$(Join-Path "#{config[:root_path]}" -ChildPath 'modules');$env:PSModulePath"

    #{script}
  EOH
end