Modules/businessdev.ALbuild.Containers/Private/Get-BcContainerStartFailure.ps1

function Get-BcContainerStartFailure {
    <#
    .SYNOPSIS
        Reads a failed container start and says what actually went wrong, and whether recreating the
        container could ever help.
 
    .DESCRIPTION
        The readiness failure used to carry ONE hypothesis, hard-coded, no matter what the container
        had said: "if the container log shows a corrupt artifact ... the artifact download is being
        truncated - typically a proxy/firewall". When the real cause was something else, that sentence
        did not merely fail to help - it pointed the reader at the network and away from the answer,
        which was sitting in the very log text appended to the same message.
 
        Measured case: a generic image older than 90 days makes Microsoft's start script refuse and
        exit 0. BC installs perfectly (102 s), the container dies at the nag, and the run then retried
        the identical refusal twice more before reporting a proxy problem. Five minutes spent to reach
        a wrong conclusion.
 
        Retryable is the second half. Recreating a container is only ever worth it for a TRANSIENT
        cause; against a deterministic refusal it just multiplies the wait. So a classified,
        non-transient cause fails fast, and only the unknown case keeps the old benefit of the doubt -
        an unrecognised failure may well be the flaky download this retry loop was built for.
 
    .PARAMETER Message
        The failure text, including the container log tail that Wait-BcContainerReady appends.
 
    .OUTPUTS
        PSCustomObject with Reason (string, '' when unrecognised) and Retryable (bool).
    #>

    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param(
        [AllowNull()] [AllowEmptyString()] [string] $Message = ''
    )

    $text = if ($null -eq $Message) { '' } else { $Message }

    # Ordered: the first match wins, so the more specific cause is listed before the general one.
    $known = @(
        @{
            Match     = 'ACCEPT_OUTDATED'
            Retryable = $false
            Reason    = ("The generic container image is more than 90 days old and its start script refused to run " +
                "(BC itself installed fine). Pull a newer generic image, or pass " +
                "-EnvironmentVariables @{ ACCEPT_OUTDATED = 'Y' } to run it anyway.")
        }
        @{
            Match     = 'End of Central Directory record could not be found'
            Retryable = $true
            Reason    = ('The artifact inside the image is truncated or corrupt - typically a proxy or firewall ' +
                'interfering with large downloads on this host.')
        }
        @{
            Match     = 'There is not enough space on the disk|no space left on device'
            Retryable = $false
            Reason    = ('The drive Docker stores containers on ran out of space while the container was ' +
                'installing BC into its writable layer.')
        }
        @{
            Match     = 'The license file .* (is not valid|has expired)|Invalid license'
            Retryable = $false
            Reason    = 'The BC licence the container was given was rejected.'
        }
    )

    foreach ($k in $known) {
        if ($text -match $k.Match) {
            return [PSCustomObject]@{ Reason = [string]$k.Reason; Retryable = [bool]$k.Retryable }
        }
    }

    # Unrecognised: keep retrying, and say nothing rather than guess. The log tail is already part of
    # the message the caller reports.
    return [PSCustomObject]@{ Reason = ''; Retryable = $true }
}