Preflight/Preflight.psm1

using module '../Common/Result'
Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath Util | Join-Path -ChildPath Util)
Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath CloudAccount | Join-Path -ChildPath CloudAccount)
Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath MigrationProfile | Join-Path -ChildPath MigrationProfile)
Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath DifferentialProfile | Join-Path -ChildPath DifferentialProfile)
function Get-RMCloudAttribute {
    param(
        [System.Object] $CloudAccount
    )

    $Response = Start-RMCloudAttributesPreflight -CloudAccount $CloudAccount
    Write-Output "Waiting for cloud attribute collection to complete..." | Out-Host
    $PreflightResult = Watch-RMPreflightStatus -PreflightId $Response.preflights[0].id `
        -TimeOutMessage "Timed out waiting for collection to complete"
    if ($PreflightResult.state -eq "success" -or ($PreflightResult.state -eq "error" -and $PreflightResult.error_type -eq "warning")) {
        return Get-CachedCloudAttribute -CloudAccount $CloudAccount
    } else {
        $PreflightID = $Response.preflights[0].id
        throw "Failed to collect attributes, preflight ID: $PreflightID"
    }
}

function Start-RMCloudAttributesPreflight {
    param(
        [System.Object] $CloudAccount
    )

    $RequestAttributes = @{
        "resource_id"= $CloudAccount.id
        "cloud_type" = $CloudAccount.type
        "type" = "target_cloud"
    }

    $CloudAttributesRequest = @($RequestAttributes)
    $CloudAttributesRequestJson = ConvertTo-Json $CloudAttributesRequest

    $RMLoginResult = Get-Variable -Name "RMContext-UserLogin" -ValueOnly
    $Uri = Get-Variable -Name "RMContext-ReactorURI" -ValueOnly

    $Headers = @{
        Accept = "application/rm+json"
        "X-Auth-Token" = $RMLoginResult.token
    }

    $Params = @{
        Method = "Post"
        Uri = $Uri + "/preflights"
        Body = $CloudAttributesRequestJson
        ContentType = "application/json"
        Headers = $Headers
    }

    Write-Output "Starting target cloud attribute collection..." | Out-Host
    return Invoke-RMRestMethod -Params $Params
}

function Start-RMMigrationPreflight {
    param(
        [string] $MigrationProfileId,
        [bool] $IgnoreValidationErrors,
        [RMMigrationReturn] $RMMigrationReturn
    )
    $MigrationProfileResponse = Get-RMMigrationProfileById -MigrationProfileId $MigrationProfileId
    $MigrationPreflightRequest = @(
        @{
            "type" = "source_config"
            "resource_id" = $MigrationProfileResponse.sources[0].id
            "overrides" = @{
                "ignore_validation_errors" = $IgnoreValidationErrors
            }
        }, @{
            "type" = "migration_profile"
            "resource_id" = $MigrationProfileId
            "overrides" = @{
                "ignore_validation_errors" = $IgnoreValidationErrors
            }
        }
    )

    $MigrationPreflightRequestJson = ConvertTo-Json $MigrationPreflightRequest

    $RMLoginResult = Get-Variable -Name "RMContext-UserLogin" -ValueOnly
    $Uri = Get-Variable -Name "RMContext-ReactorURI" -ValueOnly

    $Headers = @{
        Accept = "application/rm+json"
        "X-Auth-Token" = $RMLoginResult.token
    }

    $Params = @{
        Method = "Post"
        Uri = $Uri + "/preflights"
        Body = $MigrationPreflightRequestJson
        ContentType = "application/json"
        Headers = $Headers
    }

    Write-Output "Starting migration preflight..." | Out-Host
    $Response = Invoke-RMRestMethod -Params $Params
    Write-Output "Waiting for migration preflight to complete..." | Out-Host
    $ShouldExit = $false
    foreach ($Preflight in $Response.preflights) {
        $PreflightResult = Watch-RMPreflightStatus -PreflightId $Preflight.id `
            -TimeOutMessage "Timed out waiting for migration preflight to complete"
        $HasErrors = (Out-RMPreflight -PreflightResult $PreflightResult -IgnoreValidationErrors $IgnoreValidationErrors -RMMigrationReturn $RMMigrationReturn)[0]
        if ($HasErrors -and !$ShouldExit) {
            $ShouldExit = $true
        }
    }
    return $ShouldExit
}

function Start-RMDifferentialMigrationPreflight {
    param(
        [string] $DifferentialProfileId,
        [bool] $IgnoreValidationErrors,
        [RMMigrationReturn] $RMMigrationReturn
    )

    $DifferentialProfileResponse = Get-RMDifferentialProfileById -DifferentialProfileId $DifferentialProfileId 
    $PreflightRequest = @(
        @{
            "type" = "differential_config"
            "resource_id" = $DifferentialProfileResponse.differential_configs[0].id
            "overrides" = @{
            }
        }
    )

    $PreflightRequestJson = ConvertTo-Json $PreflightRequest

    $RMLoginResult = Get-Variable -Name "RMContext-UserLogin" -ValueOnly
    $Uri = Get-Variable -Name "RMContext-ReactorURI" -ValueOnly

    $Headers = @{
        Accept = "application/rm+json"
        "X-Auth-Token" = $RMLoginResult.token
    }

    $Params = @{
        Method = "Post"
        Uri = $Uri + "/preflights"
        Body = $PreflightRequestJson
        ContentType = "application/json"
        Headers = $Headers
    }

    Write-Output "Starting differential migration preflight..." | Out-Host
    $Response = Invoke-RMRestMethod -Params $Params
    Write-Output "Waiting for differential migration preflight to complete..." | Out-Host
    $ShouldExit = $false
    foreach ($Preflight in $Response.preflights) {
        $PreflightResult = Watch-RMPreflightStatus -PreflightId $Preflight.id `
            -TimeOutMessage "Timed out waiting for differential migration preflight to complete"
        $HasErrors = (Out-RMPreflight -PreflightResult $PreflightResult -IgnoreValidationErrors $IgnoreValidationErrors -RMMigrationReturn $RMMigrationReturn)[0]
        if ($HasErrors -and !$ShouldExit) {
            $ShouldExit = $true
        }
    }

    return $ShouldExit
}

function Out-RMPreflight {
    param(
        [System.Object] $PreflightResult,
        [bool] $IgnoreValidationErrors,
        [RMMigrationReturn] $RMMigrationReturn
    )

    $HasErrors = $false
    $PreflightWarning = @{}
    $PreflightError = @{}
    foreach ($Preflight in $PreflightResult.content) {
        [RMPreflightCheck] $RMPreflightCheck = [RMPreflightCheck]::new($Preflight.name, $Preflight.label, $Preflight.description, $Preflight.state)
        if ($Preflight.state -in "success", "pending") {
            $RMMigrationReturn.AddPreflightCheck($RMPreflightCheck)
            continue
        }
        if ($Preflight.error_type -eq "warning") {
            $Warnings = @()
            foreach ($Warning in $Preflight.errors) {
                $RMPreflightCheck.AddWarning($Warning.error_code, $Warning.message)
                $RMMigrationReturn.AddRMWarning([RMWarning]::new($Warning.message))
                $Warnings += $Warning.message
            }
            $PreflightWarning.Add($Preflight.label, $Warnings)

        } elseif ($Preflight.error_type -eq "fatal" -or $Preflight.error_type -eq "blocker") {
            $Errors = @()
            foreach ($Error in $Preflight.errors) {
                $RMPreflightCheck.AddError($Error.error_code, $Error.message)
                $RMMigrationReturn.AddRMError([RMError]::new($Error.message))
                $Errors += $Error.message
            }
            $PreflightError.Add($Preflight.label, $Errors)
            # It has been observed that the preflight state is "running" but the error_type is "fatal"
            # so explicitly set the status to "error" - could be ME or FE bug.
            $RMPreflightCheck.SetStatus("error")
            $RMMigrationReturn.SetReturnCode([RMReturn]::ERROR)
            $HasErrors = $true
        }
        $RMMigrationReturn.AddPreflightCheck($RMPreflightCheck)
    }
   
    $PreflightWarning.GetEnumerator() | Format-Table @{Label="Preflight Check"; Expression={$_.key}},`
        @{Label = "Warning(s)"; Expression={$_.value -join "`n"}} -AutoSize -Wrap | Out-Host

    $PreflightError.GetEnumerator() | Format-Table @{Label="Preflight Check"; Expression={$_.key}},`
        @{Label = "Error(s)"; Expression={$_.value -join "`n"}} -AutoSize -Wrap | Out-Host

    if ($HasErrors -and !$IgnoreValidationErrors) {
        return $HasErrors, $PreflightWarning, $PreflightError
    }
    return $false, $PreflightWarning, $PreflightError
}

Export-ModuleMember -Function Get-RMCloudAttribute, Start-RMMigrationPreflight, Start-RMDifferentialMigrationPreflight, Out-RMPreflight