Public/New-HDTExecutionContext.ps1
|
function New-HDTExecutionContext { <# .SYNOPSIS Builds the execution context every step is handed alongside its step. .DESCRIPTION One object carrying everything a step is allowed to reach: RunId, Phase, WorkspaceRoot Variable the LIVE ordered, case-insensitive dictionary. A step may write to it, and the next step's condition sees the write - which is what makes SetVariable work at all Service the New-HDTServiceCatalog catalog Log the 03-01 log context State the 03-01 run state, or $null Attempt the 1-based attempt number, set by the loop before each call so a step can tell a retry from a first try SetStep($index, $name, $type [, $stepLogPath]) IT SEEDS THE ENGINE VARIABLES into the live dictionary: _HDTRunId, _HDTPhase, _HDTLogPath, _HDTDeployRoot, _HDTVersion, and per step _HDTStepName and _HDTStepType. They are readable by conditions and by user scripts and are never assignable from a sequence - Assert-HDTSequenceDocument refuses a variables: block that names one, exactly as Assert-HDTRuleDocument does. SETSTEP FORWARDS TO THE LOG CONTEXT, which is what makes "entries carry the step name automatically, so a custom step's output is attributable without the author doing anything" true. One call updates the variables and the log at once, so the two cannot drift. IT PERFORMS NO I/O. It is built in WinPE before a disk exists. .PARAMETER RunId The deployment run id, also _HDTRunId. .PARAMETER Phase WinPE or FullOS, also _HDTPhase. .PARAMETER WorkspaceRoot The resolved workspace root, also _HDTDeployRoot. .PARAMETER Variable The live variable dictionary. Held by reference, never copied. .PARAMETER Service A New-HDTServiceCatalog catalog. .PARAMETER Log A New-HDTLogContext context. .PARAMETER State A New-HDTRunState document, or nothing in a phase that has none yet. .OUTPUTS System.Management.Automation.PSCustomObject. .EXAMPLE $context = New-HDTExecutionContext -RunId $runId -Phase WinPE ` -WorkspaceRoot 'X:\Deploy' -Variable $variable -Service $catalog -Log $log $context.SetStep(3, 'Apply OS', 'ApplyImage') #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = 'Builds an in-memory context object; it performs no I/O.')] [CmdletBinding()] [OutputType([object])] param( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $RunId, [Parameter(Mandatory = $true)] [ValidateSet('WinPE', 'FullOS')] [string] $Phase, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $WorkspaceRoot, [Parameter(Mandatory = $true)] [ValidateNotNull()] [System.Collections.IDictionary] $Variable, [Parameter(Mandatory = $true)] [ValidateNotNull()] [object] $Service, [Parameter(Mandatory = $true)] [ValidateNotNull()] [object] $Log, [Parameter()] [AllowNull()] [object] $State = $null ) Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' $Variable['_HDTRunId'] = $RunId $Variable['_HDTPhase'] = $Phase $Variable['_HDTLogPath'] = [string] $Log.LogPath $Variable['_HDTDeployRoot'] = $WorkspaceRoot $Variable['_HDTVersion'] = [string] (Get-HDTModuleVersion) $context = [pscustomobject] @{ RunId = $RunId Phase = $Phase WorkspaceRoot = $WorkspaceRoot Variable = $Variable Service = $Service Log = $Log State = $State Attempt = 1 } $context | Add-Member -MemberType ScriptMethod -Name SetStep -Value { param([int] $Index, [string] $Name, [string] $Type, [string] $StepLogPath) $this.Variable['_HDTStepName'] = $Name $this.Variable['_HDTStepType'] = $Type # THE OTHER HALF OF HDTDeploymentStart, and the reason a tattoo step can # write a duration at all. # # IT IS THE CLOCK, REFRESHED BEFORE EVERY STEP, and it has to be: the # deployment's real end is after the last step, when there is nothing # left to read it. A tattoo is the last step, so what it reads IS the # end to the second - and RESULT.json carries the true final value for # anything reading afterwards. # # UTC AND ISO 8601, LIKE THE START, and for its reason: WinPE runs on # the hardware clock and the deployed OS is put into a time zone half # way through, so two local readings are hours apart for reasons that # have nothing to do with how long the deployment took. $this.Variable['HDTDeploymentEnd'] = [System.DateTime]::UtcNow.ToString( 'yyyy-MM-ddTHH:mm:ssZ', [System.Globalization.CultureInfo]::InvariantCulture) $this.Log.SetStep($Index, $Name, $Type, $StepLogPath) } return $context } |