Modules/businessdev.ALbuild.Containers/Public/Get-BcDockerDataRoot.ps1
|
function Get-BcDockerDataRoot { <# .SYNOPSIS Where Docker stores its images and container layers, and whether that place is a good one. .DESCRIPTION On a build server this one directory decides how much can be cached and how many containers can run. A BC container copies the service tier, the web client and the apps into its writable layer and restores the demo database there - many GB each - so a Docker root on the system drive is the usual reason a container dies with a cryptic "Failed to start service" after a long wait. This reports the current root, what daemon.json actually says (which is not the same question - an absent setting means the built-in default), the service that owns it, the volume's properties, and how much would have to be pulled again after a move. Read-only: it changes nothing. Use Set-BcDockerDataRoot to move it. .PARAMETER DockerExecutable The Docker executable to query. Default 'docker'. .PARAMETER ConfigPath The daemon configuration file. Defaults to the standard Windows location. .EXAMPLE Get-BcDockerDataRoot | Format-List .EXAMPLE (Get-BcDockerDataRoot).Warnings Just the reasons this host's Docker root is a poor place to keep an image cache. .OUTPUTS PSCustomObject with Path, ConfiguredPath, IsDefault, ConfigPath, ServiceName, ServiceStatus, IsDockerDesktop, Drive, FileSystem, DriveType, FreeGb, TotalGb, DeduplicationEnabled, ContainerCount, ImageCount, Warnings. #> [CmdletBinding()] [OutputType([PSCustomObject])] param( [string] $DockerExecutable = 'docker', [string] $ConfigPath = (Join-Path $env:ProgramData 'Docker\config\daemon.json') ) $warnings = [System.Collections.Generic.List[string]]::new() # --- what the daemon is actually using ------------------------------------------------------- $path = $null $docker = Test-BcDocker -DockerExecutable $DockerExecutable if (-not $docker.Installed) { $warnings.Add("The Docker CLI ('$DockerExecutable') is not on PATH, so nothing about the daemon could be read.") } elseif (-not $docker.DaemonRunning) { $warnings.Add('The Docker daemon is not reachable, so the root in use could not be read; only daemon.json was inspected.') } else { $info = Invoke-BcDocker -DockerExecutable $DockerExecutable -Quiet -PassThru -Arguments @('info', '--format', '{{.DockerRootDir}}') if ($info.Success) { $path = "$($info.StdOut)".Trim() } } # --- what the configuration says ------------------------------------------------------------ # A missing 'data-root' is not a missing answer: it means the daemon is on its built-in default, # which is exactly the case a move is meant to fix. $configured = $null $configExists = Test-Path -LiteralPath $ConfigPath if ($configExists) { try { $json = Get-Content -LiteralPath $ConfigPath -Raw -Encoding UTF8 | ConvertFrom-Json if ($json.PSObject.Properties['data-root']) { $configured = "$($json.'data-root')" } # 'graph' is the deprecated spelling; still honoured by the engine, so still worth reporting. elseif ($json.PSObject.Properties['graph']) { $configured = "$($json.graph)" $warnings.Add("daemon.json uses the deprecated 'graph' key instead of 'data-root'. Set-BcDockerDataRoot writes 'data-root'.") } } catch { $warnings.Add("daemon.json at '$ConfigPath' could not be parsed: $($_.Exception.Message). Fix it before moving the data root, or the daemon may refuse to start.") } } # --- who owns the service ------------------------------------------------------------------- # 'docker' is the Docker Engine service; 'com.docker.service' belongs to Docker Desktop, which # manages its own settings and overwrites daemon.json - worth knowing before editing it. $serviceName = $null $serviceStatus = $null $isDesktop = $false foreach ($candidate in @('docker', 'com.docker.service')) { $svc = Get-Service -Name $candidate -ErrorAction SilentlyContinue if (-not $svc) { continue } if (-not $serviceName) { $serviceName = $svc.Name $serviceStatus = "$($svc.Status)" } if ($candidate -eq 'com.docker.service') { $isDesktop = $true } } if (-not $serviceName) { $warnings.Add('No Docker service was found. On this host the daemon is started some other way, and Set-BcDockerDataRoot cannot restart it - use -ServiceName to name it.') } if ($isDesktop -and $serviceName -eq 'com.docker.service') { $warnings.Add('This looks like Docker Desktop, which owns its own configuration and can overwrite daemon.json. Change the disk image location in Docker Desktop settings instead.') } # --- the volume ------------------------------------------------------------------------------ $volume = if ($path) { Get-BcHostVolumeInfo -Path $path } else { $null } if ($volume) { if ($volume.FileSystem -and $volume.FileSystem -ne 'NTFS') { $warnings.Add("The data root is on $($volume.FileSystem); the Windows storage driver needs NTFS.") } if ($volume.DeduplicationEnabled) { $warnings.Add('Data Deduplication is enabled on this volume. It rewrites layer files underneath the storage driver and corrupts images - exclude the Docker root or move it.') } if ($null -ne $volume.FreeGb -and $volume.FreeGb -lt 200) { $warnings.Add("Only $($volume.FreeGb) GB free on $($volume.Drive). A BC container needs tens of GB, and a version image cache needs about 6.4 GB per platform version.") } } # --- what a move would cost ------------------------------------------------------------------ # The new root starts empty, so this is the inventory that would have to be pulled again. $containerCount = $null $imageCount = $null if ($docker.DaemonRunning) { $c = Invoke-BcDocker -DockerExecutable $DockerExecutable -Quiet -PassThru -Arguments @('ps', '-aq') if ($c.Success) { $containerCount = @("$($c.StdOut)".Trim() -split '\r?\n' | Where-Object { $_ }).Count } $i = Invoke-BcDocker -DockerExecutable $DockerExecutable -Quiet -PassThru -Arguments @('images', '-q') if ($i.Success) { $imageCount = @("$($i.StdOut)".Trim() -split '\r?\n' | Where-Object { $_ } | Sort-Object -Unique).Count } } return [PSCustomObject]@{ Path = $path ConfiguredPath = $configured IsDefault = [bool](-not $configured) ConfigPath = $ConfigPath ConfigExists = $configExists ServiceName = $serviceName ServiceStatus = $serviceStatus IsDockerDesktop = $isDesktop Drive = if ($volume) { $volume.Drive } else { $null } FileSystem = if ($volume) { $volume.FileSystem } else { $null } DriveType = if ($volume) { $volume.DriveType } else { $null } FreeGb = if ($volume) { $volume.FreeGb } else { $null } TotalGb = if ($volume) { $volume.TotalGb } else { $null } DeduplicationEnabled = if ($volume) { $volume.DeduplicationEnabled } else { $null } ContainerCount = $containerCount ImageCount = $imageCount Warnings = @($warnings) } } |