如何在Packer流水线中使PowerShell provisioner的Pester测试失败时中止流程

52次阅读
没有评论

问题描述

在构建一个Packer流水线时,我遇到了一个问题:无法在Powershell provisioner步骤的测试失败时使Packer作业本身失败。这一点很重要,因为如果不使Packer作业失败,AMI将被发布,尽管测试失败。

我的操作步骤如下:
1. 运行设置步骤进行配置。
2. 将Pester脚本复制到远程机器。
3. 使用内联PowerShell语句调用Invoke-Pester进行测试。
4. 从远程运行的Pester测试中下载测试结果。
5. 接下来的配置步骤解析文件以计算错误数量,并在问题数大于0时抛出错误。

然而,即使错误数大于0,它也没有导致流水线中止。此外,其他配置步骤中抛出的一些错误也未能导致Packer作业停止。我在其中一个步骤上设置了$ErrorActionPreference = 'Stop',但似乎没有任何改变。

以下是Packer的一部分配置流程:

...
{
  "type": "file",
  "source": "{{ template_dir }}/tests/windows-server.tests.ps1",
  "destination": "{{ user `TEMP_DIRECTORY` }}"
},
{
  "type": "powershell",
  "environment_vars": [
    "TEMP_DIRECTORY={{ user `TEMP_DIRECTORY` }}/"
  ],
  "inline": [
    "if(-not (Get-InstalledModule Pester -MinimumVersion 4.10.1)){ Install-Module Pester -Scope AllUsers -AllowClobber -Force -verbose}",
    "Import-Module Pester -MinimumVersion 4.10.1 -Force -DisableNameChecking",
    "$null = New-Item -Path $ENV:TEMP_DIRECTORY -ItemType Directory -Force -ErrorAction SilentlyContinue",
    "$testresult = Invoke-Pester -Script (Join-Path $ENV:TEMP_DIRECTORY 'windows-server.tests.ps1') -OutputFile (Join-Path $ENV:TEMP_DIRECTORY 'TEST-pester-results.xml') -OutputFormat 'NUnitXML' -PassThru"
  ]
},
{
  "type": "file",
  "direction": "download",
  "source": "{{ user `TEMP_DIRECTORY` }}/TEST-pester-results.xml",
  "destination": "{{ template_dir }}/artifacts/TEST-pester-results.xml"
},
...

完成上述部分后,会执行下面的步骤,其中包含一些来自此博客文章的逻辑:

{
  "environment_vars": [
    "TEMP_DIRECTORY={{ user `TEMP_DIRECTORY` }}/"
  ],
  "script": "./scripts/Get-TestResults.ps1",
  "type": "powershell"
}

Get-TestResults.ps1 脚本如下,感谢这篇文章提供的帮助:

[cmdletbinding()]
param()
$TestFile = Join-Path $ENV:TEMP_DIRECTORY 'TEST-pester-results.xml'
if (Test-Path -Path $TestFile -PathType Leaf) {
    [xml]$testResults = Get-Content -Path $TestFile -Raw
    $totalIssues = [int]$($testResults.'test-results'.errors) + [int]$($testResults.'test-results'.failures) + [int]$($testResults.'test-results'.invalid)
    exit $totalIssues
} else {
    Write-Error " Failed to find `$TestFile: $TestFile"
    Write-Warning "$(Get-ChildItem $ENV:TEMP_DIRECTORY -Recurse | Format-Table -autosize -wrap | Out-String)"
    Write-Error " Failed to find `$TestFile: $TestFile"
    exit 1
}

解决方案

请注意以下操作可能存在版本差异,修改前请备份。

解决方案1

在处理上述问题时,你需要确保在PowerShell provisioner的步骤中,错误的发生能够正确中止Packer作业。为此,你需要在出现错误时抛出一个非零的退出代码。

下面是一个解决方案示例,使用$ErrorActionPreference = 'Stop' 来确保错误能够中止脚本,然后在出现错误时抛出一个非零的退出代码:

$ErrorActionPreference = 'Stop'
try {
    # 在此处执行操作
} catch {
    Write-Error "发生了重要错误: $_"
    exit 1  # Packer将会在此时识别到失败
}

解决方案2

要使Packer作业在Powershell provisioner步骤失败时中止,你需要设置Azure DevOps Pipeline以便无论是否失败都能发布测试结果。你可以按如下方式配置:

- task: PowerShell@2
  displayName: 运行Packer配置
  inputs:
    filePath: build/build.ps1
    arguments: '-Task PackerBuild -Configuration $(Configuration)'
    errorActionPreference: 'Continue'
    pwsh: true
  continueOnError: true  # 我希望无论如何都能发布测试结果

- task: PublishTestResults@2
  displayName: 发布Pester测试结果
  inputs:
    testResultsFormat: 'NUnit'
    testResultsFiles: '**/TEST-*.xml'
  condition: succeededOrFailed()

总结

通过在PowerShell provisioner的步骤中正确设置$ErrorActionPreference以及抛出适当的退出代码,你可以使Packer作业在Pester测试失败时中止,并确保测试结果的发布。

希望这些解决方案能够帮助你解决Packer流水线中的问题。如果你在实施解决方案时遇到任何问题,欢迎随时提问。

正文完