TM-DockerUtility.psm1

if ((Test-ApplicationExistsInPath -ApplicationName 'docker') -eq $false) {
    Write-Warning 'docker does not exist in the Path. Skipping the import of docker ProfileUtility commands.'
    break
}


class MountPoint {
<#
    .SYNOPSIS
    Holds the HostPath and ContainerPath for use connecting to a given docker image.
#>

    [string]$HostPath
    [string]$ContainerPath

    MountPoint(
        [string]$hostPath = (Read-Host -Prompt 'Enter the local mount path'),
        [string]$containerPath = (Read-Host -Prompt 'Enter the containers mount path')
    ) {
        $this.HostPath = $hostPath
        $this.ContainerPath = $containerPath
    }

    MountPoint() {
        $this.HostPath = Read-Host -Prompt 'Enter the local mount path'
        $this.ContainerPath = Read-Host -Prompt 'Enter the containers mount path'
    }

    [string]ToString() { return "-v '$($this.HostPath)`:$($this.ContainerPath)' " }
}


function Enter-DockerImageWithNewEntryPoint {
<#
    .SYNOPSIS
    Enters a Docker container image with a new entry point.
 
    .DESCRIPTION
    This function allows you to enter a selected Docker container with a specified entry point.
    This can be useful if your containers default entrypoint is causing errors and the container
    will not stay running long enough for you to exec into it and determine what is wrong.
 
    .PARAMETER EntryPoint
    The entry point command to use with the debug image. The default value is determined by user input.
 
    .PARAMETER DeleteDebugImage
    A boolean value that indicates whether to delete the debug image after exiting the container.
    The default value is $true.
 
    .PARAMETER MountPoints
    An array of mount points to add to the Docker container.
#>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $false)]
        [string]$EntryPoint = (
            Read-Host -Prompt "Provide the entrypoint command to use with '$DebugName'.`nexample: /bin/bash "),

        [Parameter(Mandatory = $false)]
        [bool]$DeleteDebugImage = $true,

        [Parameter(Mandatory = $false)]
        [MountPoint[]]$MountPoints
    )

    if (($env:OS -eq 'Windows_NT') -and ($null -eq (Get-Process -Name 'Docker Desktop' -ErrorAction Ignore))) {
        Write-Warning -Message 'Start Docker Desktop before trying to enter a docker image'
        return
    }

    $PwshPath = if ($env:OS -eq 'Windows_NT') { 'pwsh.exe' } else { '/mnt/c/Program Files/PowerShell/7/pwsh.exe' }

    $DockerPSOut = docker ps -a --format '{{json .}}' | ConvertFrom-Json -ErrorAction Ignore

    if ($null -ne $DockerPSOut) {
        Write-Host 'Select a container to enter:'
        $Count = 0
        $Container = $DockerPSOut | ForEach-Object {
            Select-Object -InputObject $_ -Property @{n = '#'; e = { $Count } }, ID, Names, Status, Image
            $Count++
        } | Out-GridView -Title 'Available Containers' -OutputMode Single

        $ContainerImage = $Container.Image.Split('/')[-1]
        $DebugName = "debug/$ContainerImage"
        docker commit $Container.ID $DebugName | Write-Host

        $dockerMount = ''
        foreach ($MountPoint in $MountPoints) {
            $dockerMount += $MountPoint.ToString()
        }

        $StartDockerRun = @"
`$startProcessSplat = @{
FilePath = 'pwsh.exe'
PassThru = `$true
Wait = `$true
NoNewWindow = `$true
ArgumentList = "-NoProfile -c docker.exe run -it $dockerMount --rm --entrypoint '$EntryPoint' $DebugName"
}
`$Process = Start-Process @startProcessSplat
if ((`$null -eq `$Process) -or ((`$Process.ExitCode ?? 0) -ne 0)){
`$err = if (`$null -eq `$Process){ 'Process is null' } else { "Process Exitcode: `$(`$Process.ExitCode)" }
`$errMessage = "`$err. Sleeping for 2 minutes after error to allow visibility. Press ctrl+c to exit."
Microsoft.PowerShell.Utility\Write-Host `$errMessage
Start-Sleep -Seconds 120
}
if ('true' -ieq '$DeleteDebugImage'){
`$DockerImagesPSOut = docker images -a --format '{{json .}}' | ConvertFrom-Json -ErrorAction Ignore
`$ImageName = "$DebugName".Split(':')[0]
`$ImageId = `$DockerImagesPSOut | Where-object {`$_.Repository -eq `$ImageName} | Select-Object -ExpandProperty ID
docker.exe image rm `$ImageId
}
"@

        Write-Debug "Running`n$StartDockerRun"
        $cmd = '-ec ' + [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($StartDockerRun))
        Start-Process -FilePath $PwshPath -ArgumentList $cmd -NoNewWindow -Wait
    } else {
        Write-Warning 'Docker did not return any output from the ''docker ps -a'' command.'
    }
}


function Remove-DockerResources {
<#
    .SYNOPSIS
    Removes Docker resources, including unused containers, networks, images, and optionally volumes.
 
    .DESCRIPTION
    This function removes unused Docker resources such as containers, networks, images, and optionally volumes.
    It serves as a cleanup tool to reset the docker environment state back to the default install state.
 
    .PARAMETER Volumes
    A boolean value that indicates whether to include volumes in the Docker resource removal.
    The default value is $true.
#>

    [Alias('Prune-DockerResources')]
    [CmdletBinding()]
    [OutputType([Void])]
    param (
        [Parameter(Mandatory = $false)]
        [bool]$Volumes = $true
    )
    [List[string]]$DockerArgs = [List[string]]::new()
    $DockerArgs.AddRange([string[]]@('system', 'prune', '--all', '--force'))
    if ($Volumes) { $DockerArgs.Add('--volumes') }
    docker @DockerArgs
}


Export-ModuleMember -Function 'Enter-DockerImageWithNewEntryPoint', 'Remove-DockerResources'