[CmdletBinding()] param( [switch]$ConfirmReadOnlyShadow, [Parameter(Mandatory = $true)][string]$PreflightJson, [string]$JavaExecutable = 'java', [int]$MaximumPreflightAgeMinutes = 30 ) $ErrorActionPreference = 'Stop' Set-StrictMode -Version Latest . (Join-Path $PSScriptRoot 'ShadowScriptCommon.ps1') if (-not $ConfirmReadOnlyShadow.IsPresent) { throw 'CONFIRM_READ_ONLY_SHADOW_REQUIRED' } $preflightPath = Resolve-ShadowFile $PreflightJson $preflight = Read-ShadowJson $preflightPath if (-not [bool]$preflight.success) { throw 'SUCCESSFUL_PREFLIGHT_REQUIRED' } $generatedAt = [DateTime]::Parse($preflight.generatedAtUtc).ToUniversalTime() if (([DateTime]::UtcNow - $generatedAt).TotalMinutes -gt ` $MaximumPreflightAgeMinutes) { throw 'PREFLIGHT_EXPIRED' } if ($null -eq (Get-Process -Id ([int]$preflight.stable.pid) ` -ErrorAction SilentlyContinue)) { throw 'STABLE_PROCESS_NOT_HEALTHY' } $artifact = Resolve-ShadowFile $preflight.shadowArtifact.path $config = Resolve-ShadowFile $preflight.config.path $deployment = Resolve-ShadowDirectory $preflight.deploymentDirectory if (-not (Test-ShadowPathWithin $artifact $deployment) -or ` -not (Test-ShadowPathWithin $config $deployment)) { throw 'SHADOW_FILES_MUST_BE_INSIDE_DEPLOYMENT_DIRECTORY' } if ((Get-ShadowSha256 $artifact) -ne $preflight.shadowArtifact.sha256) { throw 'SHADOW_ARTIFACT_CHANGED_AFTER_PREFLIGHT' } if ((Get-ShadowSha256 $config) -ne $preflight.config.sha256) { throw 'SHADOW_CONFIG_CHANGED_AFTER_PREFLIGHT' } if ((Get-ShadowSha256 $preflight.permissionReport.path) -ne ` $preflight.permissionReport.sha256) { throw 'PERMISSION_REPORT_CHANGED_AFTER_PREFLIGHT' } if ((Get-ShadowSha256 $preflight.networkDenyReport.path) -ne ` $preflight.networkDenyReport.sha256) { throw 'NETWORK_DENY_REPORT_CHANGED_AFTER_PREFLIGHT' } $pidFile = [IO.Path]::GetFullPath($preflight.pidFile) if (-not (Test-ShadowPathWithin $pidFile $deployment)) { throw 'PID_FILE_MUST_BE_INSIDE_DEPLOYMENT_DIRECTORY' } if (Test-Path -LiteralPath $pidFile) { throw 'SHADOW_PID_FILE_ALREADY_EXISTS' } $java = (Get-Command $JavaExecutable -ErrorAction Stop).Source $logs = Join-Path $deployment 'logs' New-Item -ItemType Directory -Path $logs -Force | Out-Null $stdout = Join-Path $logs 'shadow-stdout.log' $stderr = Join-Path $logs 'shadow-stderr.log' $configUri = ([uri]$config).AbsoluteUri $statusUri = [uri]$preflight.statusUri $runtimeProperties = @( '--spring.profiles.active=shadow', '--shadow.enabled=true', "--spring.config.additional-location=$configUri", "--server.address=$($statusUri.Host)", "--server.port=$($statusUri.Port)", "--shadow.evidence.run.artifact-sha256=$($preflight.shadowArtifact.sha256)", "--shadow.evidence.run.configuration-sha256=$($preflight.config.sha256)", "--shadow.evidence.run.source-server=$($preflight.sourceIdentity.server)", "--shadow.evidence.run.source-database=$($preflight.sourceIdentity.database)", "--shadow.evidence.minimum-free-bytes=$($preflight.minimumFreeBytes)" ) $quotedProperties = @($runtimeProperties | ForEach-Object { '"{0}"' -f $_.Replace('"', '\"') }) -join ' ' $arguments = '-jar "{0}" {1}' -f $artifact.Replace('"', '\"'), ` $quotedProperties $process = $null try { $process = Start-Process -FilePath $java -ArgumentList $arguments ` -WorkingDirectory $deployment -WindowStyle Hidden -PassThru ` -RedirectStandardOutput $stdout -RedirectStandardError $stderr Start-Sleep -Milliseconds 300 if ($process.HasExited) { throw "SHADOW_PROCESS_EXITED_EARLY:$($process.ExitCode)" } $record = [pscustomobject]@{ schemaVersion = 1 pid = [int]$process.Id processStartTimeUtc = $process.StartTime.ToUniversalTime().ToString('o') javaExecutable = [IO.Path]::GetFullPath($java) shadowArtifact = $artifact shadowArtifactSha256 = Get-ShadowSha256 $artifact config = $config configSha256 = Get-ShadowSha256 $config deploymentDirectory = $deployment preflightJson = $preflightPath preflightSha256 = Get-ShadowSha256 $preflightPath startedAtUtc = [DateTime]::UtcNow.ToString('o') } Write-ShadowJson -Value $record -Path $pidFile return $record } catch { if ($null -ne $process -and -not $process.HasExited) { Stop-Process -Id $process.Id -Force -ErrorAction SilentlyContinue } throw }