Skip to content

Commit 206fa41

Browse files
committed
ci: run the DSC provisioner end to end on Windows
kitchen-dsc had no Test Kitchen coverage at all: the unit specs assert on the PowerShell we generate, but nothing ever ran that PowerShell against a real DSC engine. A change that produced syntactically valid but semantically wrong script would sail through CI. This adds a Windows integration suite modelled on the one in kitchen-pester. The GitHub runner is both workstation and system under test: the proxy driver points Test Kitchen at localhost, WinRM connects back to a local admin account created by the job, and the shell verifier then asserts against the machine DSC just configured. The suite exercises every phase of the provisioner: - install_command, by checking the LCM meta-configuration was applied - create_sandbox and prepare_command, by checking test/fixtures/modules reached the PSModulePath and the configuration compiled to a MOF - run_command, by checking the built-in File resource created the file - ps_hash, by checking the marker set in configuration_data survived the round trip into the compiled MOF The fixture configuration deliberately uses only the built-in File resource, so a failure points at kitchen-dsc rather than at a third-party DSC resource. lint.yml is renamed to match what it actually runs now that integration lives in its own workflow. Signed-off-by: Tim Smith <tim@mondoo.com>
1 parent 521e1e9 commit 206fa41

8 files changed

Lines changed: 276 additions & 1 deletion

File tree

.github/workflows/integration.yml

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
---
2+
name: "Windows Integration"
3+
4+
"on":
5+
pull_request:
6+
push:
7+
branches:
8+
- main
9+
10+
permissions:
11+
contents: read
12+
13+
concurrency:
14+
group: windows-integration-${{ github.ref }}
15+
cancel-in-progress: true
16+
17+
jobs:
18+
windows:
19+
name: Kitchen Verify on Windows (Ruby ${{ matrix.ruby }})
20+
runs-on: windows-latest
21+
timeout-minutes: 30
22+
env:
23+
# kitchen.windows.yml points the proxy driver at this account over WinRM
24+
MACHINE_USER: test_user
25+
MACHINE_PASS: Pass@word1
26+
KITCHEN_YAML: kitchen.windows.yml
27+
strategy:
28+
fail-fast: false
29+
matrix:
30+
ruby: ["3.1", "3.4", "4.0"]
31+
steps:
32+
- name: Checkout code
33+
uses: actions/checkout@v7
34+
35+
- name: Setup Ruby
36+
uses: ruby/setup-ruby@v1
37+
with:
38+
ruby-version: ${{ matrix.ruby }}
39+
bundler-cache: true
40+
41+
- name: Set up WinRM and the local test account
42+
shell: powershell
43+
run: |
44+
winrm quickconfig -q
45+
# Runners ship with basic/unencrypted WinRM disabled, and local
46+
# admins are filtered over the network by default.
47+
winrm set winrm/config/service/auth '@{Basic="true"}'
48+
winrm set winrm/config/service '@{AllowUnencrypted="true"}'
49+
New-ItemProperty `
50+
-Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System `
51+
-Name LocalAccountTokenFilterPolicy -Value 1 -PropertyType DWord -Force | Out-Null
52+
net user /add $env:MACHINE_USER $env:MACHINE_PASS
53+
net localgroup administrators $env:MACHINE_USER /add
54+
55+
- name: Report the DSC engine in use
56+
shell: powershell
57+
run: |
58+
$PSVersionTable | Format-List
59+
Get-DscLocalConfigurationManager | Format-List
60+
61+
- name: Show the resolved provisioner configuration
62+
run: bundle exec kitchen diagnose --no-instances --loader
63+
64+
- name: Kitchen Verify
65+
run: bundle exec kitchen verify
66+
67+
- name: Collect diagnostics on failure
68+
if: failure()
69+
shell: powershell
70+
run: |
71+
Get-ChildItem -Recurse C:\configurations -ErrorAction SilentlyContinue |
72+
Select-Object -ExpandProperty FullName
73+
Get-DscConfigurationStatus -All -ErrorAction SilentlyContinue |
74+
Format-List

.github/workflows/lint.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
name: 'Lint, Unit & Integration Tests'
2+
name: 'Lint & Unit Tests'
33

44
'on':
55
pull_request:

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,7 @@ doc/
2727

2828
# RSpec example status, used by --only-failures
2929
spec/examples.txt
30+
31+
# Test Kitchen working directory
32+
.kitchen/
33+
/kitchen.local.yml

kitchen.windows.yml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
---
2+
# Integration configuration used by the Windows CI job. The GitHub runner is
3+
# both the workstation and the system under test, so the proxy driver just
4+
# points Test Kitchen at localhost and WinRM does the rest. Nothing is created
5+
# or destroyed, so `reset_command` is a no-op.
6+
driver:
7+
name: proxy
8+
host: localhost
9+
port: 5985
10+
reset_command: "exit 0"
11+
12+
# Credentials belong to the transport; the proxy driver only supplies the host.
13+
transport:
14+
name: winrm
15+
username: <%= ENV["MACHINE_USER"] %>
16+
password: <%= ENV["MACHINE_PASS"] %>
17+
18+
provisioner:
19+
name: dsc
20+
# windows-latest runners ship PowerShell 5.1, so the WMF 5 meta-configuration
21+
# is the one that applies.
22+
dsc_local_configuration_manager_version: wmf5
23+
# Keep the fixtures out of the gem root: both paths are relative to the
24+
# directory holding this file.
25+
configuration_script_folder: test/fixtures/dsc
26+
configuration_script: kitchen_dsc_test.ps1
27+
modules_path: test/fixtures/modules
28+
configuration_name: KitchenDscTest
29+
configuration_data:
30+
AllNodes:
31+
- NodeName: localhost
32+
TargetPath: C:\kitchen-dsc-integration
33+
Marker: staged-by-kitchen-dsc
34+
35+
platforms:
36+
- name: windows-latest
37+
38+
# The shell verifier runs on the workstation, which here is the same machine
39+
# DSC just configured.
40+
verifier:
41+
name: shell
42+
command: powershell -NoProfile -ExecutionPolicy Bypass -File test/integration/verify.ps1
43+
44+
suites:
45+
- name: default
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# DSC configuration applied by the Windows integration job.
2+
#
3+
# It deliberately uses only the built-in File resource so the suite exercises
4+
# kitchen-dsc itself -- LCM setup, sandbox staging, MOF compilation and
5+
# Start-DscConfiguration -- rather than a third-party DSC resource module.
6+
Configuration KitchenDscTest
7+
{
8+
Import-DscResource -ModuleName PSDesiredStateConfiguration
9+
10+
Node $AllNodes.NodeName
11+
{
12+
File TestDirectory
13+
{
14+
Ensure = 'Present'
15+
Type = 'Directory'
16+
DestinationPath = $Node.TargetPath
17+
}
18+
19+
File TestFile
20+
{
21+
Ensure = 'Present'
22+
Type = 'File'
23+
DestinationPath = Join-Path -Path $Node.TargetPath -ChildPath 'kitchen-dsc.txt'
24+
Contents = $Node.Marker
25+
DependsOn = '[File]TestDirectory'
26+
}
27+
}
28+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
@{
2+
RootModule = 'KitchenDscExample.psm1'
3+
ModuleVersion = '1.0.0'
4+
GUID = 'a2f0f0f5-6a1a-4a4f-9a2f-1f0f0d1c2b3a'
5+
Author = 'Test Kitchen Team'
6+
CompanyName = 'Test Kitchen'
7+
Copyright = 'Apache-2.0'
8+
Description = 'Fixture module for the kitchen-dsc integration suite.'
9+
PowerShellVersion = '5.0'
10+
FunctionsToExport = @('Get-KitchenDscExampleMarker')
11+
CmdletsToExport = @()
12+
VariablesToExport = @()
13+
AliasesToExport = @()
14+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Trivial module used to prove that repository-style staging copied
2+
# test/fixtures/modules onto the PSModulePath of the system under test.
3+
function Get-KitchenDscExampleMarker
4+
{
5+
[CmdletBinding()]
6+
[OutputType([string])]
7+
param ()
8+
9+
'staged-by-kitchen-dsc'
10+
}
11+
12+
Export-ModuleMember -Function Get-KitchenDscExampleMarker

test/integration/verify.ps1

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#Requires -Version 5
2+
<#
3+
.SYNOPSIS
4+
Asserts that the kitchen-dsc integration suite actually converged.
5+
6+
.DESCRIPTION
7+
Run by the shell verifier after `kitchen converge`. The GitHub runner is
8+
both the workstation and the system under test, so this executes locally
9+
against the machine DSC just configured. Every check maps to one thing
10+
kitchen-dsc is responsible for, so a failure names the phase that broke.
11+
#>
12+
[CmdletBinding()]
13+
param (
14+
[string] $TargetPath = 'C:\kitchen-dsc-integration',
15+
[string] $Marker = 'staged-by-kitchen-dsc',
16+
[string] $ModuleName = 'KitchenDscExample'
17+
)
18+
19+
$ErrorActionPreference = 'Stop'
20+
21+
$failures = [System.Collections.Generic.List[string]]::new()
22+
23+
function Assert-KitchenDsc
24+
{
25+
param (
26+
[Parameter(Mandatory)] [string] $Description,
27+
[Parameter(Mandatory)] [scriptblock] $Condition
28+
)
29+
30+
$result = $false
31+
try
32+
{
33+
$result = [bool] (& $Condition)
34+
}
35+
catch
36+
{
37+
$script:failures.Add("$Description -- threw: $($_.Exception.Message)")
38+
Write-Host "FAIL $Description"
39+
return
40+
}
41+
42+
if ($result)
43+
{
44+
Write-Host "PASS $Description"
45+
}
46+
else
47+
{
48+
$script:failures.Add($Description)
49+
Write-Host "FAIL $Description"
50+
}
51+
}
52+
53+
# install_command: the LCM meta-configuration was compiled and applied.
54+
$lcm = Get-DscLocalConfigurationManager
55+
Assert-KitchenDsc 'the LCM refresh mode was set to Push' { $lcm.RefreshMode -eq 'Push' }
56+
Assert-KitchenDsc 'the LCM configuration mode was set to ApplyAndAutoCorrect' {
57+
$lcm.ConfigurationMode -eq 'ApplyAndAutoCorrect'
58+
}
59+
60+
# create_sandbox + prepare_command: modules_path was staged and copied onto the
61+
# PSModulePath of the system under test.
62+
Assert-KitchenDsc "the $ModuleName module reached the PSModulePath" {
63+
$null -ne (Get-Module -ListAvailable -Name $ModuleName)
64+
}
65+
Assert-KitchenDsc "the $ModuleName module is loadable and exports its function" {
66+
Import-Module $ModuleName -Force
67+
(Get-KitchenDscExampleMarker) -eq $Marker
68+
}
69+
70+
# prepare_command: the configuration script was uploaded and compiled to a MOF.
71+
Assert-KitchenDsc 'the configuration compiled to a MOF' {
72+
Test-Path -Path 'C:\configurations\KitchenDscTest\localhost.mof'
73+
}
74+
75+
# run_command: Start-DscConfiguration applied the compiled MOF.
76+
$testFile = Join-Path -Path $TargetPath -ChildPath 'kitchen-dsc.txt'
77+
Assert-KitchenDsc "the File resource created $TargetPath" {
78+
Test-Path -Path $TargetPath -PathType Container
79+
}
80+
Assert-KitchenDsc "the File resource wrote $testFile" { Test-Path -Path $testFile -PathType Leaf }
81+
82+
# The contents come from configuration_data in kitchen.windows.yml, so this is
83+
# also the end-to-end check that ps_hash rendered it into the compiled MOF.
84+
Assert-KitchenDsc 'the configuration_data marker reached the compiled MOF' {
85+
(Get-Content -Path $testFile -Raw).Trim() -eq $Marker
86+
}
87+
88+
if ($failures.Count -gt 0)
89+
{
90+
Write-Host ''
91+
Write-Host "$($failures.Count) check(s) failed:"
92+
$failures | ForEach-Object { Write-Host " - $_" }
93+
exit 1
94+
}
95+
96+
Write-Host ''
97+
Write-Host 'All kitchen-dsc integration checks passed.'
98+
exit 0

0 commit comments

Comments
 (0)