Skip to content

Commit

Permalink
Support for base image label/annotation
Browse files Browse the repository at this point in the history
  • Loading branch information
mthalman committed Jul 7, 2024
1 parent c83d920 commit aca500c
Show file tree
Hide file tree
Showing 8 changed files with 274 additions and 159 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,4 @@ jobs:
context: ./container
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.event.inputs.version }},${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.event.inputs.version }},${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:dev
6 changes: 1 addition & 5 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,7 @@ runs:
$baseStage = "${{ inputs.base-stage-name }}"
$arch = "${{ inputs.arch }}"
if (-not $baseImage -and -not $dockerfile) {
throw "'dockerfile' input not provided. This is required when 'base-image-name' is not provided."
}
$dockerBumpCheckerVersion = "0.3.0"
$dockerBumpCheckerVersion = "dev"
$containerName = "docker-bump-checker"
$containerSrcPath = "/src"
Expand Down
4 changes: 2 additions & 2 deletions container/check-image.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ Set-StrictMode -Version 2.0

function GetDigest($imageName) {
$digestCmd = "dredge manifest resolve $imageName --os linux --arch $Architecture"
$digest = $(InvokeTool $digestCmd "dredge manifest resolve failed")
$digest = $(InvokeTool $digestCmd)
return $digest
}

Import-Module $PSScriptRoot/common.psm1

$compareCmd = "dredge image compare layers --output json $BaseImage $TargetImage --os linux --arch $Architecture"
$layerComparisonStr = $(InvokeTool $compareCmd "dredge image compare failed")
$layerComparisonStr = $(InvokeTool $compareCmd)
$layerComparison = $layerComparisonStr | ConvertFrom-Json

$imageUpToDate = [bool]$($layerComparison.summary.targetIncludesAllBaseLayers)
Expand Down
4 changes: 2 additions & 2 deletions container/common.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ function LogMessage ([string] $Message) {
Write-Output $Message | Out-File $env:HOME/log.txt -Append
}

function InvokeTool([string]$ToolCommand, [string] $ErrorMessage) {
function InvokeTool([string]$ToolCommand) {
LogMessage "Invoke: $ToolCommand"

# Reset $LASTEXITCODE in case it was tripped somewhere
Expand All @@ -17,7 +17,7 @@ function InvokeTool([string]$ToolCommand, [string] $ErrorMessage) {
$exitCode = $LASTEXITCODE
LogMessage "Result: $result"
if ($exitCode -ne 0) {
throw $ErrorMessage
throw "Command failed with exit code ${exitCode}: $ToolCommand"
}

return $result
Expand Down
2 changes: 1 addition & 1 deletion container/entrypoint.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ $ProgressPreference = 'SilentlyContinue'
Set-StrictMode -Version 2.0

if (-not $BaseImage) {
$BaseImage = $(& $PSScriptRoot/get-base-image.ps1 -DockerfilePath $DockerfilePath -BaseStageName $BaseStageName)
$BaseImage = $(& $PSScriptRoot/get-base-image.ps1 -DockerfilePath $DockerfilePath -BaseStageName $BaseStageName -TargetImage $targetImage -Architecture $Architecture)
}

$result = $(& $PSScriptRoot/check-image.ps1 -TargetImage $targetImage -BaseImage $BaseImage -Architecture $Architecture -DockerfilePath $DockerfilePath)
Expand Down
113 changes: 80 additions & 33 deletions container/get-base-image.ps1
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
[cmdletbinding()]
param(
[Parameter(Mandatory = $True)]
[string]$DockerfilePath,
[string]$BaseStageName
[string]$BaseStageName,
[string]$TargetImage,
[string]$Architecture
)

$ErrorActionPreference = 'Stop'
Expand All @@ -11,49 +12,95 @@ Set-StrictMode -Version 2.0

Import-Module $PSScriptRoot/common.psm1

if (-not (Test-Path $DockerfilePath)) {
throw "Dockerfile path '$DockerfilePath' does not exist."
}
function GetBaseImageFromDockerfile() {
$dfspyArgs = @(
'query',
'from',
'-f',
$DockerfilePath
)

$dfspyArgs = @(
'query',
'from',
'-f',
$DockerfilePath
)
# If a stage name is provided we can directly filter the list-based output of the "query from" command.
# Otherwise, we need to derive the base image by finding the last stage of the Dockerfile and walking
# its parent chain. To do that, we need the graph layout.

if (-not $BaseStageName) {
LogMessage 'Stage name not provided. Will derive base image by walking parent chain.'
$dfspyArgs += '--layout'
$dfspyArgs += 'graph'
}

$dfspyArgsString = $dfspyArgs -join ' '

$cmd = "dfspy $dfspyArgsString"
$fromOutput = $(InvokeTool $cmd)
$fromOutput = $fromOutput | ConvertFrom-Json

if ($BaseStageName) {
$baseImage = $fromOutput | Where-Object { $_.PSObject.Properties.Name -contains "stageName" -and $_.stageName -eq $BaseStageName } | Select-Object -ExpandProperty imageName
if (-not $baseImage) {
throw "Could not find stage with name '$BaseStageName'."
}
} else {
# Find the last stage of the Dockerfile and walk its parent chain to find the base image
$currentNode = $fromOutput | Select-Object -Last 1

LogMessage "Deriving base image by walking parent chain from '$($currentNode.fromInstruction.imageName)'."

# If a stage name is provided we can directly filter the list-based output of the "query from" command.
# Otherwise, we need to derive the base image by finding the last stage of the Dockerfile and walking
# its parent chain. To do that, we need the graph layout.
while ($currentNode.PSObject.Properties.Name -contains "parent") {
$currentNode = $currentNode.parent
}

if (-not $BaseStageName) {
LogMessage 'Stage name not provided. Will derive base image by walking parent chain.'
$dfspyArgs += '--layout'
$dfspyArgs += 'graph'
$baseImage = $currentNode.fromInstruction.imageName
}

return $baseImage
}

$dfspyArgsString = $dfspyArgs -join ' '
function GetBaseImageFromAnnotation() {
$baseNameAnnotationKey = "org.opencontainers.image.base.name"
# Check whether a LABEL exists for the OCI annotation key
$inspectOutput = $(InvokeTool "dredge image inspect $TargetImage --os linux --arch $Architecture") | ConvertFrom-Json
Set-StrictMode -Off
if ($inspectOutput.config.Labels `
-and $inspectOutput.config.Labels.$baseNameAnnotationKey) {
$baseImage = $inspectOutput.config.Labels.$baseNameAnnotationKey
LogMessage "Found base image annotation from LABEL: '$baseImage'."
return $baseImage
}
Set-StrictMode -Version 2.0

$cmd = "dfspy $dfspyArgsString"
$fromOutput = $(InvokeTool $cmd "dfspy failed")
$fromOutput = $fromOutput | ConvertFrom-Json
# Check whether the annotation exists in the manifest
$resolvedDigest = $(InvokeTool "dredge manifest resolve $TargetImage --os linux --arch $Architecture")
$manifest = $(InvokeTool "dredge manifest get $resolvedDigest") | ConvertFrom-Json

if ($BaseStageName) {
$baseImage = $fromOutput | Where-Object { $_.PSObject.Properties.Name -contains "stageName" -and $_.stageName -eq $BaseStageName } | Select-Object -ExpandProperty imageName
if (-not $baseImage) {
throw "Could not find stage with name '$BaseStageName'."
Set-StrictMode -Off
if ($manifest.annotations -and $manifest.annotations.$baseNameAnnotationKey) {
$baseImage = $manifest.Annotations.$baseNameAnnotationKey
LogMessage "Found base image annotation from manifest: '$baseImage'."
return $baseImage
}
} else {
# Find the last stage of the Dockerfile and walk its parent chain to find the base image
$currentNode = $fromOutput | Select-Object -Last 1
Set-StrictMode -Version 2.0

LogMessage "Deriving base image by walking parent chain from '$($currentNode.fromInstruction.imageName)'."
LogMessage "Could not find base image annotation '$baseNameAnnotationKey'."

while ($currentNode.PSObject.Properties.Name -contains "parent") {
$currentNode = $currentNode.parent
return $null
}

if ($DockerfilePath) {
if (-not (Test-Path $DockerfilePath)) {
throw "Dockerfile path '$DockerfilePath' does not exist."
}
$baseImage = GetBaseImageFromDockerfile
}
else {
# If a Dockerfile path is not provided, we need to rely on the base image annotation being set on
# the target image.
$baseImage = GetBaseImageFromAnnotation
}

$baseImage = $currentNode.fromInstruction.imageName
if (-not $baseImage) {
throw "Could not derive base image name."
}

LogMessage "Using base image name '$baseImage'."
Expand Down
2 changes: 1 addition & 1 deletion container/tests/check-image.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,6 @@ Describe 'Get result' {
It 'Given a failed dredge command, it throws an error' {
Mock Invoke-Expression { $global:LASTEXITCODE = 1 } -ParameterFilter { $Command -like "dredge *" } -ModuleName common

{ & $targetScript -TargetImage $targetImage -BaseImage $baseImage -Architecture $architecture } | Should -Throw "dredge image compare failed"
{ & $targetScript -TargetImage $targetImage -BaseImage $baseImage -Architecture $architecture } | Should -Throw "Command failed with exit code 1: dredge image compare layers --output json bar foo --os linux --arch amd64"
}
}
Loading

0 comments on commit aca500c

Please sign in to comment.