test(ft546): verify read-only shadow operation
This commit is contained in:
157
tools/shadow/ShadowScriptCommon.ps1
Normal file
157
tools/shadow/ShadowScriptCommon.ps1
Normal file
@@ -0,0 +1,157 @@
|
||||
function Resolve-ShadowFile {
|
||||
param([Parameter(Mandatory = $true)][string]$Path)
|
||||
$item = Get-Item -LiteralPath $Path -ErrorAction Stop
|
||||
if ($item.PSIsContainer) { throw "FILE_REQUIRED:$Path" }
|
||||
return $item.FullName
|
||||
}
|
||||
|
||||
function Resolve-ShadowDirectory {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string]$Path,
|
||||
[switch]$Create
|
||||
)
|
||||
if ($Create -and -not (Test-Path -LiteralPath $Path)) {
|
||||
New-Item -ItemType Directory -Path $Path -Force | Out-Null
|
||||
}
|
||||
$item = Get-Item -LiteralPath $Path -ErrorAction Stop
|
||||
if (-not $item.PSIsContainer) { throw "DIRECTORY_REQUIRED:$Path" }
|
||||
return $item.FullName
|
||||
}
|
||||
|
||||
function Write-ShadowJson {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]$Value,
|
||||
[Parameter(Mandatory = $true)][string]$Path,
|
||||
[int]$Depth = 12
|
||||
)
|
||||
$parent = Split-Path -Parent ([IO.Path]::GetFullPath($Path))
|
||||
if (-not (Test-Path -LiteralPath $parent)) {
|
||||
New-Item -ItemType Directory -Path $parent -Force | Out-Null
|
||||
}
|
||||
$Value | ConvertTo-Json -Depth $Depth | Set-Content -LiteralPath $Path `
|
||||
-Encoding UTF8
|
||||
}
|
||||
|
||||
function Read-ShadowJson {
|
||||
param([Parameter(Mandatory = $true)][string]$Path)
|
||||
$resolved = Resolve-ShadowFile $Path
|
||||
return Get-Content -LiteralPath $resolved -Raw | ConvertFrom-Json
|
||||
}
|
||||
|
||||
function Get-ShadowSha256 {
|
||||
param([Parameter(Mandatory = $true)][string]$Path)
|
||||
return (Get-FileHash -LiteralPath (Resolve-ShadowFile $Path) `
|
||||
-Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
}
|
||||
|
||||
function Get-ShadowCommandLine {
|
||||
param([Parameter(Mandatory = $true)][int]$ProcessId)
|
||||
try {
|
||||
$row = Get-CimInstance Win32_Process -Filter "ProcessId=$ProcessId" `
|
||||
-ErrorAction Stop
|
||||
return $row.CommandLine
|
||||
}
|
||||
catch { return $null }
|
||||
}
|
||||
|
||||
function Get-ShadowListeningPorts {
|
||||
param([Parameter(Mandatory = $true)][int]$ProcessId)
|
||||
try {
|
||||
return @(Get-NetTCPConnection -OwningProcess $ProcessId -State Listen `
|
||||
-ErrorAction Stop | Sort-Object LocalPort | ForEach-Object {
|
||||
[pscustomobject]@{
|
||||
address = $_.LocalAddress
|
||||
port = [int]$_.LocalPort
|
||||
state = $_.State.ToString()
|
||||
}
|
||||
})
|
||||
}
|
||||
catch { return @() }
|
||||
}
|
||||
|
||||
function Test-ShadowPathWithin {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string]$Candidate,
|
||||
[Parameter(Mandatory = $true)][string]$Directory
|
||||
)
|
||||
$candidatePath = [IO.Path]::GetFullPath($Candidate)
|
||||
$directoryPath = [IO.Path]::GetFullPath($Directory).TrimEnd('\', '/')
|
||||
if ($candidatePath.Equals($directoryPath,
|
||||
[StringComparison]::OrdinalIgnoreCase)) { return $true }
|
||||
$prefix = $directoryPath + [IO.Path]::DirectorySeparatorChar
|
||||
return $candidatePath.StartsWith($prefix,
|
||||
[StringComparison]::OrdinalIgnoreCase)
|
||||
}
|
||||
|
||||
function Get-ShadowFreeBytes {
|
||||
param([Parameter(Mandatory = $true)][string]$Path)
|
||||
$root = [IO.Path]::GetPathRoot([IO.Path]::GetFullPath($Path))
|
||||
return ([IO.DriveInfo]::new($root)).AvailableFreeSpace
|
||||
}
|
||||
|
||||
function Resolve-ShadowScalarPlaceholder {
|
||||
param([AllowNull()][string]$Value)
|
||||
if ($null -eq $Value) { return $null }
|
||||
if ($Value -match '^\$\{([A-Za-z_][A-Za-z0-9_]*):(.*)\}$') {
|
||||
$environmentValue = [Environment]::GetEnvironmentVariable($matches[1])
|
||||
return if ($null -eq $environmentValue) { $matches[2] } `
|
||||
else { $environmentValue }
|
||||
}
|
||||
return $Value
|
||||
}
|
||||
|
||||
function Read-ShadowYamlScalars {
|
||||
param([Parameter(Mandatory = $true)][string]$Path)
|
||||
$resolved = Resolve-ShadowFile $Path
|
||||
$parents = @{}
|
||||
$values = @{}
|
||||
foreach ($line in Get-Content -LiteralPath $resolved) {
|
||||
if ($line -match '^\s*(#.*)?$') { continue }
|
||||
if ($line -notmatch '^(\s*)([A-Za-z0-9_-]+):(?:\s*(.*))?$') {
|
||||
continue
|
||||
}
|
||||
$indent = $matches[1].Length
|
||||
$key = $matches[2]
|
||||
$raw = if ($null -eq $matches[3]) { '' } else { $matches[3].Trim() }
|
||||
foreach ($level in @($parents.Keys | Where-Object { [int]$_ -ge $indent })) {
|
||||
$parents.Remove($level)
|
||||
}
|
||||
if ([string]::IsNullOrWhiteSpace($raw)) {
|
||||
$parents[$indent] = $key
|
||||
continue
|
||||
}
|
||||
$segments = @($parents.Keys | Sort-Object { [int]$_ } | ForEach-Object {
|
||||
$parents[$_]
|
||||
}) + $key
|
||||
if (($raw.StartsWith('"') -and $raw.EndsWith('"')) -or `
|
||||
($raw.StartsWith("'") -and $raw.EndsWith("'"))) {
|
||||
$raw = $raw.Substring(1, $raw.Length - 2)
|
||||
}
|
||||
$values[($segments -join '.')] = Resolve-ShadowScalarPlaceholder $raw
|
||||
}
|
||||
return $values
|
||||
}
|
||||
|
||||
function Get-ShadowSqlServerJdbcIdentity {
|
||||
param([Parameter(Mandatory = $true)][string]$JdbcUrl)
|
||||
if ($JdbcUrl -notmatch '(?i)^jdbc:sqlserver://([^;]+)(?:;(.*))?$') {
|
||||
throw 'SQLSERVER_JDBC_URL_REQUIRED'
|
||||
}
|
||||
$server = $matches[1].Trim()
|
||||
$properties = @{}
|
||||
foreach ($part in @($matches[2] -split ';')) {
|
||||
if ($part -match '^\s*([^=]+)=(.*)$') {
|
||||
$properties[$matches[1].Trim().ToLowerInvariant()] =
|
||||
$matches[2].Trim()
|
||||
}
|
||||
}
|
||||
$database = $properties['databasename']
|
||||
if ([string]::IsNullOrWhiteSpace($database)) {
|
||||
$database = $properties['database']
|
||||
}
|
||||
if ([string]::IsNullOrWhiteSpace($server) -or `
|
||||
[string]::IsNullOrWhiteSpace($database)) {
|
||||
throw 'SQLSERVER_JDBC_IDENTITY_INCOMPLETE'
|
||||
}
|
||||
return [pscustomobject]@{ server = $server; database = $database }
|
||||
}
|
||||
Reference in New Issue
Block a user