Private/Logic/Eigenverft.Manifested.Sandbox.RuntimePack.Python.ps1

<#
    Eigenverft.Manifested.Sandbox.RuntimePack.Python
#>


function Get-ManifestedPythonRuntimeDescriptor {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$CommandName
    )

    $descriptor = Get-ManifestedRuntimeDescriptor -CommandName $CommandName
    if (-not $descriptor) {
        throw "Could not resolve runtime descriptor for '$CommandName'."
    }
    if ($descriptor.RuntimeFamily -ne 'Python') {
        throw "Runtime '$CommandName' does not belong to the Python runtime family."
    }

    return $descriptor
}

function Get-ManifestedPythonPlannedActions {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [pscustomobject]$Descriptor,

        [Parameter(Mandatory = $true)]
        [bool]$NeedsRepair,

        [Parameter(Mandatory = $true)]
        [bool]$NeedsInstall,

        [Parameter(Mandatory = $true)]
        [bool]$NeedsAcquire,

        [pscustomobject]$RuntimeState
    )

    $plannedActions = New-Object System.Collections.Generic.List[string]

    if ($NeedsRepair) {
        $plannedActions.Add($Descriptor.RepairFunctionName) | Out-Null
    }
    if ($NeedsInstall -and $NeedsAcquire) {
        $plannedActions.Add($Descriptor.SavePackageFunctionName) | Out-Null
    }
    if ($NeedsInstall) {
        $plannedActions.Add($Descriptor.TestPackageFunctionName) | Out-Null
        $plannedActions.Add($Descriptor.InstallFunctionName) | Out-Null
        $plannedActions.Add('Ensure-PythonPip') | Out-Null
    }
    elseif ($RuntimeState -and $RuntimeState.PSObject.Properties['RuntimeSource'] -and ($RuntimeState.RuntimeSource -eq 'Managed')) {
        $plannedActions.Add('Ensure-PythonPip') | Out-Null
    }

    $plannedActions.Add('Sync-ManifestedCommandLineEnvironment') | Out-Null
    return @($plannedActions)
}

function Get-ManifestedPythonPersistedDetails {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [pscustomobject]$Descriptor,

        [pscustomobject]$FinalState,

        [pscustomobject]$PackageInfo,

        [pscustomobject]$RuntimeTest,

        [pscustomobject]$PipSetupResult
    )

    return @{
        Version        = if ($FinalState -and $FinalState.PSObject.Properties['CurrentVersion']) { $FinalState.CurrentVersion } else { $null }
        Flavor         = if ($FinalState -and $FinalState.PSObject.Properties['Flavor']) { $FinalState.Flavor } else { $null }
        RuntimeHome    = if ($FinalState -and $FinalState.PSObject.Properties['RuntimeHome']) { $FinalState.RuntimeHome } else { $null }
        RuntimeSource  = if ($FinalState -and $FinalState.PSObject.Properties['RuntimeSource']) { $FinalState.RuntimeSource } else { $null }
        ExecutablePath = if ($FinalState -and $FinalState.PSObject.Properties['ExecutablePath']) { $FinalState.ExecutablePath } else { $null }
        PipVersion     = if ($RuntimeTest -and $RuntimeTest.PSObject.Properties['PipVersion']) { $RuntimeTest.PipVersion } else { $null }
        PipConfigPath  = if ($PipSetupResult -and $PipSetupResult.PSObject.Properties['PipProxyConfiguration'] -and $PipSetupResult.PipProxyConfiguration -and $PipSetupResult.PipProxyConfiguration.PSObject.Properties['PipConfigPath']) { $PipSetupResult.PipProxyConfiguration.PipConfigPath } else { $null }
        AssetName      = if ($PackageInfo -and $PackageInfo.PSObject.Properties['FileName']) { $PackageInfo.FileName } else { $null }
        Sha256         = if ($PackageInfo -and $PackageInfo.PSObject.Properties['Sha256']) { $PackageInfo.Sha256 } else { $null }
        ShaSource      = if ($PackageInfo -and $PackageInfo.PSObject.Properties['ShaSource']) { $PackageInfo.ShaSource } else { $null }
        DownloadUrl    = if ($PackageInfo -and $PackageInfo.PSObject.Properties['DownloadUrl']) { $PackageInfo.DownloadUrl } else { $null }
        ReleaseUrl     = if ($PackageInfo -and $PackageInfo.PSObject.Properties['ReleaseUrl']) { $PackageInfo.ReleaseUrl } else { $null }
        ReleaseId      = if ($PackageInfo -and $PackageInfo.PSObject.Properties['ReleaseId']) { $PackageInfo.ReleaseId } else { $null }
    }
}

function Get-ManifestedPythonManagedFinalizerStatus {
    [CmdletBinding()]
    param(
        [pscustomobject]$RuntimeState
    )

    $applicable = $false
    if ($RuntimeState -and $RuntimeState.PSObject.Properties['RuntimeSource'] -and $RuntimeState.PSObject.Properties['ExecutablePath']) {
        $applicable = ($RuntimeState.RuntimeSource -eq 'Managed') -and (-not [string]::IsNullOrWhiteSpace($RuntimeState.ExecutablePath))
    }

    return [pscustomobject]@{
        Applicable = [bool]$applicable
        ActionName = 'Ensure-PythonPip'
        Target     = if ($applicable -and $RuntimeState.PSObject.Properties['RuntimeHome']) { $RuntimeState.RuntimeHome } else { $null }
    }
}

function Invoke-ManifestedPythonManagedFinalization {
    [CmdletBinding()]
    param(
        [pscustomobject]$Status,

        [Parameter(Mandatory = $true)]
        [pscustomobject]$RuntimeState
    )

    if (-not $Status) {
        $Status = Get-ManifestedPythonManagedFinalizerStatus -RuntimeState $RuntimeState
    }

    if (-not $Status.Applicable) {
        return $null
    }

    return (Ensure-PythonPip -PythonExe $RuntimeState.ExecutablePath -PythonHome $RuntimeState.RuntimeHome -LocalRoot $RuntimeState.LocalRoot)
}

function Invoke-ManifestedPythonRuntimeInitialization {
    [CmdletBinding(SupportsShouldProcess = $true)]
    param(
        [Parameter(Mandatory = $true)]
        [string]$CommandName,

        [switch]$Refresh
    )

    $descriptor = Get-ManifestedPythonRuntimeDescriptor -CommandName $CommandName
    $LocalRoot = (Get-ManifestedLayout).LocalRoot
    $selfElevationContext = Get-ManifestedSelfElevationContext

    $actionsTaken = New-Object System.Collections.Generic.List[string]
    $repairResult = $null
    $packageInfo = $null
    $packageTest = $null
    $installResult = $null
    $pipSetupResult = $null
    $commandEnvironment = $null

    $initialState = & $descriptor.StateFunctionName -LocalRoot $LocalRoot
    $state = $initialState
    $elevationPlan = Get-ManifestedCommandElevationPlan -CommandName $descriptor.InitializeCommandName -LocalRoot $LocalRoot -SkipSelfElevation:$selfElevationContext.SkipSelfElevation -WasSelfElevated:$selfElevationContext.WasSelfElevated -WhatIfMode:$WhatIfPreference

    if ($state.Status -eq 'Blocked') {
        $commandEnvironment = Get-ManifestedCommandEnvironmentResult -CommandName $descriptor.InitializeCommandName -RuntimeState $state
        $result = New-ManifestedRuntimeResult -LocalRoot $state.LocalRoot -Layout $state.Layout -InitialState $initialState -FinalState $state -ActionTaken @('None') -PlannedActions @() -RestartRequired:$false -AdditionalProperties ([ordered]@{
            Package               = $null
            PackageTest           = $null
            RuntimeTest           = $null
            RepairResult          = $null
            InstallResult         = $null
            PipSetupResult        = $null
            PipProxyConfiguration = $null
            CommandEnvironment    = $commandEnvironment
            Elevation             = $elevationPlan
        })

        return (Complete-ManifestedRuntimeResult -CommandName $descriptor.InitializeCommandName -Result $result -LocalRoot $LocalRoot -Details @{} -PersistState:(-not $WhatIfPreference))
    }

    $needsRepair = $state.Status -in @('Partial', 'NeedsRepair')
    $needsInstall = $Refresh -or -not $state.RuntimeHome
    $needsAcquire = $Refresh -or (-not $state.PackagePath) -or (-not (Test-PythonRuntimePackageHasTrustedHash -PackageInfo $state.Package))
    $plannedActions = @(Get-ManifestedPythonPlannedActions -Descriptor $descriptor -NeedsRepair:$needsRepair -NeedsInstall:$needsInstall -NeedsAcquire:$needsAcquire -RuntimeState $state)
    $elevationPlan = Get-ManifestedCommandElevationPlan -CommandName $descriptor.InitializeCommandName -PlannedActions @($plannedActions) -LocalRoot $LocalRoot -SkipSelfElevation:$selfElevationContext.SkipSelfElevation -WasSelfElevated:$selfElevationContext.WasSelfElevated -WhatIfMode:$WhatIfPreference

    if ($WhatIfPreference) {
        return (Complete-ManifestedRuntimeResult -CommandName $descriptor.InitializeCommandName -LocalRoot $LocalRoot -PersistState:$false -Result (
            New-ManifestedRuntimeResult -LocalRoot $state.LocalRoot -Layout $state.Layout -InitialState $initialState -FinalState $state -ActionTaken @('WhatIf') -PlannedActions @($plannedActions) -RestartRequired:$false -AdditionalProperties ([ordered]@{
                Package               = $state.Package
                PackageTest           = $null
                RuntimeTest           = $state.Runtime
                RepairResult          = $null
                InstallResult         = $null
                PipSetupResult        = $null
                PipProxyConfiguration = $null
                CommandEnvironment    = (Get-ManifestedCommandEnvironmentResult -CommandName $descriptor.InitializeCommandName -RuntimeState $state)
                Elevation             = $elevationPlan
            })
        ))
    }

    if ($needsRepair) {
        if (-not $PSCmdlet.ShouldProcess($state.Layout.($descriptor.ToolsRootPropertyName), ('Repair {0} runtime state' -f $descriptor.DisplayName))) {
            return (Complete-ManifestedRuntimeResult -CommandName $descriptor.InitializeCommandName -LocalRoot $LocalRoot -PersistState:$false -Result (
                New-ManifestedRuntimeResult -LocalRoot $state.LocalRoot -Layout $state.Layout -InitialState $initialState -FinalState $state -ActionTaken @('Cancelled') -PlannedActions @($plannedActions) -RestartRequired:$false -AdditionalProperties ([ordered]@{
                    Package               = $state.Package
                    PackageTest           = $null
                    RuntimeTest           = $state.Runtime
                    RepairResult          = $null
                    InstallResult         = $null
                    PipSetupResult        = $null
                    PipProxyConfiguration = $null
                    CommandEnvironment    = (Get-ManifestedCommandEnvironmentResult -CommandName $descriptor.InitializeCommandName -RuntimeState $state)
                    Elevation             = $elevationPlan
                })
            ))
        }

        $repairResult = & $descriptor.RepairFunctionName -State $state -Flavor $state.Flavor -LocalRoot $state.LocalRoot
        if ($repairResult.Action -eq 'Repaired') {
            $actionsTaken.Add($descriptor.RepairFunctionName) | Out-Null
        }

        $state = & $descriptor.StateFunctionName -Flavor $state.Flavor -LocalRoot $state.LocalRoot
    }

    $needsInstall = $Refresh -or -not $state.RuntimeHome
    $needsAcquire = $Refresh -or (-not $state.PackagePath) -or (-not (Test-PythonRuntimePackageHasTrustedHash -PackageInfo $state.Package))

    if ($needsInstall) {
        if ($needsAcquire) {
            $saveParameters = @{
                LocalRoot = $state.LocalRoot
                Flavor    = $state.Flavor
            }
            $saveParameters[$descriptor.RefreshParameterName] = [bool]$Refresh
            $packageInfo = & $descriptor.SavePackageFunctionName @saveParameters
            if ($packageInfo.Action -eq 'Downloaded') {
                $actionsTaken.Add($descriptor.SavePackageFunctionName) | Out-Null
            }
        }
        else {
            $packageInfo = $state.Package
        }

        $packageTest = & $descriptor.TestPackageFunctionName -PackageInfo $packageInfo
        if ($packageTest.Status -eq 'UnverifiedCache') {
            $trustedPackageResolution = Resolve-PythonRuntimeTrustedPackageInfo -PackageInfo $packageInfo -Flavor $state.Flavor -LocalRoot $state.LocalRoot
            $packageInfo = $trustedPackageResolution.PackageInfo
            $packageTest = & $descriptor.TestPackageFunctionName -PackageInfo $packageInfo

            if ($packageTest.Status -eq 'UnverifiedCache') {
                throw (New-PythonRuntimePackageTrustFailureMessage -PackageInfo $packageInfo -MetadataRefreshError $trustedPackageResolution.MetadataRefreshError)
            }
        }

        if ($packageTest.Status -eq 'CorruptCache') {
            if (-not $PSCmdlet.ShouldProcess($packageInfo.Path, ('Repair corrupt {0} runtime package' -f $descriptor.DisplayName))) {
                return (Complete-ManifestedRuntimeResult -CommandName $descriptor.InitializeCommandName -LocalRoot $LocalRoot -PersistState:$false -Result (
                    New-ManifestedRuntimeResult -LocalRoot $state.LocalRoot -Layout $state.Layout -InitialState $initialState -FinalState $state -ActionTaken @('Cancelled') -PlannedActions @($plannedActions) -RestartRequired:$false -AdditionalProperties ([ordered]@{
                        Package               = $packageInfo
                        PackageTest           = $packageTest
                        RuntimeTest           = $state.Runtime
                        RepairResult          = $repairResult
                        InstallResult         = $null
                        PipSetupResult        = $null
                        PipProxyConfiguration = $null
                        CommandEnvironment    = (Get-ManifestedCommandEnvironmentResult -CommandName $descriptor.InitializeCommandName -RuntimeState $state)
                        Elevation             = $elevationPlan
                    })
                ))
            }

            $repairResult = & $descriptor.RepairFunctionName -State $state -CorruptPackagePaths @($packageInfo.Path) -Flavor $state.Flavor -LocalRoot $state.LocalRoot
            if ($repairResult.Action -eq 'Repaired') {
                $actionsTaken.Add($descriptor.RepairFunctionName) | Out-Null
            }

            $refreshSaveParameters = @{
                LocalRoot = $state.LocalRoot
                Flavor    = $state.Flavor
            }
            $refreshSaveParameters[$descriptor.RefreshParameterName] = $true
            $packageInfo = & $descriptor.SavePackageFunctionName @refreshSaveParameters
            if ($packageInfo.Action -eq 'Downloaded') {
                $actionsTaken.Add($descriptor.SavePackageFunctionName) | Out-Null
            }

            $packageTest = & $descriptor.TestPackageFunctionName -PackageInfo $packageInfo
        }

        if ($packageTest.Status -ne 'Ready') {
            throw ('{0} runtime package validation failed with status {1}.' -f $descriptor.DisplayName, $packageTest.Status)
        }

        $commandParameters = @{}
        if ($Refresh) {
            $commandParameters[$descriptor.RefreshParameterName] = $true
        }
        if ($PSBoundParameters.ContainsKey('WhatIf')) {
            $commandParameters['WhatIf'] = $true
        }

        $elevatedResult = Invoke-ManifestedElevatedCommand -ElevationPlan $elevationPlan -CommandName $descriptor.InitializeCommandName -CommandParameters $commandParameters
        if ($null -ne $elevatedResult) {
            return $elevatedResult
        }

        if (-not $PSCmdlet.ShouldProcess($state.Layout.($descriptor.ToolsRootPropertyName), ('Install {0} runtime' -f $descriptor.DisplayName))) {
            return (Complete-ManifestedRuntimeResult -CommandName $descriptor.InitializeCommandName -LocalRoot $LocalRoot -PersistState:$false -Result (
                New-ManifestedRuntimeResult -LocalRoot $state.LocalRoot -Layout $state.Layout -InitialState $initialState -FinalState $state -ActionTaken @('Cancelled') -PlannedActions @($plannedActions) -RestartRequired:$false -AdditionalProperties ([ordered]@{
                    Package               = $packageInfo
                    PackageTest           = $packageTest
                    RuntimeTest           = $state.Runtime
                    RepairResult          = $repairResult
                    InstallResult         = $null
                    PipSetupResult        = $null
                    PipProxyConfiguration = $null
                    CommandEnvironment    = (Get-ManifestedCommandEnvironmentResult -CommandName $descriptor.InitializeCommandName -RuntimeState $state)
                    Elevation             = $elevationPlan
                })
            ))
        }

        $installResult = & $descriptor.InstallFunctionName -PackageInfo $packageInfo -Flavor $state.Flavor -LocalRoot $state.LocalRoot -ForceInstall:$Refresh
        if ($installResult.Action -eq 'Installed') {
            $actionsTaken.Add($descriptor.InstallFunctionName) | Out-Null
        }
        if ($installResult.PSObject.Properties['PipResult'] -and $installResult.PipResult) {
            if ($installResult.PipResult.Action -ne 'Reused') {
                $actionsTaken.Add('Ensure-PythonPip') | Out-Null
            }
            elseif ($installResult.PipResult.PipProxyConfiguration -and $installResult.PipResult.PipProxyConfiguration.Action -eq 'ConfiguredManagedProxy') {
                $actionsTaken.Add('Sync-ManifestedPipProxyConfiguration') | Out-Null
            }
        }
    }

    $finalState = & $descriptor.StateFunctionName -Flavor $state.Flavor -LocalRoot $state.LocalRoot
    $runtimeTestParameters = if ($descriptor.PSObject.Properties['RuntimeTestParameterResolver'] -and $descriptor.RuntimeTestParameterResolver) {
        & $descriptor.RuntimeTestParameterResolver $finalState
    }
    else {
        @{}
    }

    $runtimeTest = & $descriptor.RuntimeTestFunctionName @runtimeTestParameters
    if ($finalState.Status -ne 'Ready' -or -not $runtimeTest -or -not $runtimeTest.IsReady) {
        throw 'Python runtime validation did not reach the Ready state.'
    }

    $pythonManagedFinalizerStatus = Get-ManifestedPythonManagedFinalizerStatus -RuntimeState $finalState
    if ($pythonManagedFinalizerStatus.Applicable) {
        $pipSetupResult = & $descriptor.ManagedFinalizerFunctionName -Status $pythonManagedFinalizerStatus -RuntimeState $finalState
        if ($pipSetupResult.Action -ne 'Reused') {
            $actionsTaken.Add('Ensure-PythonPip') | Out-Null
        }
        elseif ($pipSetupResult.PipProxyConfiguration -and $pipSetupResult.PipProxyConfiguration.Action -eq 'ConfiguredManagedProxy') {
            $actionsTaken.Add('Sync-ManifestedPipProxyConfiguration') | Out-Null
        }

        $runtimeTest = & $descriptor.RuntimeTestFunctionName @runtimeTestParameters
    }

    $commandEnvironmentSync = Invoke-ManifestedRuntimeCommandEnvironmentSync -Cmdlet $PSCmdlet -CommandName $descriptor.InitializeCommandName -DisplayName $descriptor.DisplayName -RuntimeState $finalState -ActionsTaken $actionsTaken -UseShouldProcess:$false -RequireNeedsSync:$true
    $commandEnvironment = $commandEnvironmentSync.CommandEnvironment

    $effectivePackageInfo = if ($packageInfo) { $packageInfo } elseif ($finalState.Package) { $finalState.Package } else { $null }
    $result = New-ManifestedRuntimeResult -LocalRoot $finalState.LocalRoot -Layout $finalState.Layout -InitialState $initialState -FinalState $finalState -ActionTaken (if ($actionsTaken.Count -gt 0) { @($actionsTaken) } else { @('None') }) -PlannedActions @($plannedActions) -RestartRequired:$false -AdditionalProperties ([ordered]@{
        Package               = $effectivePackageInfo
        PackageTest           = $packageTest
        RuntimeTest           = $runtimeTest
        RepairResult          = $repairResult
        InstallResult         = $installResult
        PipSetupResult        = $pipSetupResult
        PipProxyConfiguration = if ($pipSetupResult) { $pipSetupResult.PipProxyConfiguration } else { $null }
        CommandEnvironment    = $commandEnvironment
        Elevation             = $elevationPlan
    })

    return (Complete-ManifestedRuntimeResult -CommandName $descriptor.InitializeCommandName -Result $result -LocalRoot $LocalRoot -Details (& $descriptor.PersistedDetailsFunctionName -Descriptor $descriptor -FinalState $finalState -PackageInfo $effectivePackageInfo -RuntimeTest $runtimeTest -PipSetupResult $pipSetupResult))
}