Skip to content

Build Azure SIG Image #30

Build Azure SIG Image

Build Azure SIG Image #30

# GitHub Actions workflow for building and publishing Azure Shared Image Gallery (SIG) images
#
# Required secrets:
# - AZURE_CLIENT_ID - Azure service principal client ID (for OIDC authentication)
# - AZURE_TENANT_ID - Azure tenant ID
# - AZURE_SUBSCRIPTION_ID - Azure subscription ID
#
# Required environment variables (set in GitHub repository or organization settings):
# - EULA_LINK - the URL to the EULA for the image (for promote stage)
# - PUBLISHER_EMAIL - the email for the image publisher (for promote stage)
# - PUBLISHER_URI - the URI for the image publisher (for promote stage)
# - SIG_PUBLISHER - the publisher for the image definition (for promote stage)
#
# Required inputs:
# - kubernetes_version - version of Kubernetes to build the image with, e.g. `1.31.1`
# - os - operating system distro, such as 'Ubuntu', 'AzureLinux', or 'Windows'
# - os_version - version of distro, such as `24.04` or `2022-containerd`
#
# Optional inputs:
# - resource_group - name of the Azure resource group to use for the compute galleries
# - staging_gallery_name - name of the Azure compute gallery for initial image publishing
# - gallery_name - name of the Azure community gallery for final image publishing
# - packer_flags - additional flags to pass to packer
# - tags - tags to apply to the image
# - replicated_regions - space-separated list of Azure regions to replicate the image to
# - skip_test - skip the test stage
# - skip_promote - skip the promote stage
# - packer_debug - enable Packer debug logging (sets PACKER_LOG=1)
name: Build Azure SIG Image
on:
workflow_dispatch:
inputs:
kubernetes_version:
description: 'Kubernetes version (e.g., 1.31.1)'
required: true
type: string
os:
description: 'Operating system (Ubuntu, AzureLinux, Windows)'
required: true
type: choice
options:
- Ubuntu
- AzureLinux
- Windows
os_version:
description: 'OS version (e.g., 24.04, 2022-containerd)'
required: true
type: string
resource_group:
description: 'Azure resource group name'
required: false
type: string
default: 'cluster-api-gallery'
staging_gallery_name:
description: 'Staging gallery name'
required: false
type: string
default: 'staging_gallery'
gallery_name:
description: 'Community gallery name'
required: false
type: string
default: 'community_gallery'
packer_flags:
description: 'Additional Packer flags'
required: false
type: string
default: ''
tags:
description: 'Tags to apply to the image'
required: false
type: string
default: ''
replicated_regions:
description: 'Space-separated Azure regions to replicate the image to (image build region is always included)'
required: false
type: string
default: 'australiaeast canadacentral eastus eastus2 francecentral germanywestcentral northeurope switzerlandnorth uksouth westeurope'
skip_test:
description: 'Skip the test stage'
required: false
type: boolean
default: true
skip_promote:
description: 'Skip the promote stage (requires manual approval)'
required: false
type: boolean
default: false
packer_debug:
description: 'Enable Packer debug logging (PACKER_LOG=1)'
required: false
type: boolean
default: false
permissions:
id-token: write
contents: read
jobs:
# ---------------------------------------------------------------------------
# Build
# ---------------------------------------------------------------------------
build:
name: Build SIG Image
runs-on: ubuntu-latest
timeout-minutes: 120
env:
KUBERNETES_VERSION: ${{ inputs.kubernetes_version }}
OS: ${{ inputs.os }}
OS_VERSION: ${{ inputs.os_version }}
RESOURCE_GROUP: ${{ inputs.resource_group }}
STAGING_GALLERY_NAME: ${{ inputs.staging_gallery_name }}
PACKER_FLAGS: ${{ inputs.packer_flags }}
TAGS_INPUT: ${{ inputs.tags }}
PACKER_LOG: ${{ inputs.packer_debug && '1' || '0' }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Configure Kubernetes version
uses: ./.github/actions/configure-k8s-version
with:
kubernetes_version: ${{ inputs.kubernetes_version }}
- name: Check for Windows kube-proxy image
if: inputs.os == 'Windows'
run: |
set -euo pipefail
IMAGE="sigwindowstools/kube-proxy"
TAG="v${KUBERNETES_VERSION/+/_}-calico-hostprocess"
echo "Checking for Windows kube-proxy image ${IMAGE}:${TAG}"
# Use the Docker Hub Registry v2 API to verify the tag exists.
TOKEN=$(curl -fsSL "https://auth.docker.io/token?service=registry.docker.io&scope=repository:${IMAGE}:pull" | jq -r .token)
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
-H "Accept: application/vnd.docker.distribution.manifest.list.v2+json" \
"https://registry-1.docker.io/v2/${IMAGE}/manifests/${TAG}")
if [[ "${HTTP_STATUS}" != "200" ]]; then
echo "kube-proxy image ${IMAGE}:${TAG} not found (HTTP ${HTTP_STATUS})"
exit 1
fi
echo "kube-proxy image ${IMAGE}:${TAG} exists"
- name: Azure Login
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install dependencies
working-directory: images/capi
run: |
pip install ansible ansible-lint
make deps-azure
- name: Build SIG Image
id: build
working-directory: images/capi
run: |
set -euo pipefail
os=$(echo "${OS}" | tr '[:upper:]' '[:lower:]')
version=$(echo "${OS_VERSION}" | tr '[:upper:]' '[:lower:]' | tr -d .)
export RESOURCE_GROUP="${RESOURCE_GROUP:-cluster-api-gallery}"
export RESOURCE_GROUP_NAME="${RESOURCE_GROUP}"
# timestamp is in RFC-3339 format to match kubetest
export TIMESTAMP="$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
export JOB_NAME="${JOB_NAME:-"image-builder-sig-${os}-${version}"}"
if [[ -n "${TAGS_INPUT}" ]]; then
export TAGS="${TAGS_INPUT}"
else
export TAGS="creationTimestamp=${TIMESTAMP} jobName=${JOB_NAME} DO-NOT-DELETE=UpstreamInfra"
fi
printf '%s' "${TAGS}" | tee packer/azure/tags.out
export GALLERY_NAME="${STAGING_GALLERY_NAME:-staging_gallery}"
DISTRO="${os}-${version}"
echo "DISTRO=${DISTRO}" >> $GITHUB_ENV
export PACKER_FLAGS="${PACKER_FLAGS} --var sig_image_version=${KUBERNETES_VERSION}"
export PATH=$PATH:$HOME/.local/bin
export USE_AZURE_CLI_AUTH="True"
make build-azure-sig-${os}-${version} | tee packer/azure/packer.out
- name: Generate SIG publishing info
id: publishing_info
working-directory: images/capi
run: |
set -euo pipefail
PACKER_OUTPUT=packer/azure/packer.out
OS_TYPE=$(sed -n 's/^OSType: \(.*\)/\1/p' $PACKER_OUTPUT | tail -1)
MANAGED_IMAGE_RESOURCE_GROUP_NAME=$(sed -n "s/^ManagedImageResourceGroupName: \(.*\)/\1/p" $PACKER_OUTPUT | tail -1)
MANAGED_IMAGE_NAME=$(sed -n "s/^ManagedImageName: \(.*\)/\1/p" $PACKER_OUTPUT | tail -1)
MANAGED_IMAGE_ID=$(sed -n "s/^ManagedImageId: \(.*\)/\1/p" $PACKER_OUTPUT | tail -1)
MANAGED_IMAGE_LOCATION=$(sed -n "s/^ManagedImageLocation: \(.*\)/\1/p" $PACKER_OUTPUT | tail -1)
MANAGED_IMAGE_SHARED_IMAGE_GALLERY_ID=$(sed -n "s/^ManagedImageSharedImageGalleryId: \(.*\)/\1/p" $PACKER_OUTPUT | tail -1)
SHARED_IMAGE_GALLERY_RESOURCE_GROUP=$(sed -n "s/^SharedImageGalleryResourceGroup: \(.*\)/\1/p" $PACKER_OUTPUT | tail -1)
SHARED_IMAGE_GALLERY_NAME=$(sed -n "s/^SharedImageGalleryName: \(.*\)/\1/p" $PACKER_OUTPUT | tail -1)
SHARED_IMAGE_GALLERY_IMAGE_NAME=$(sed -n "s/^SharedImageGalleryImageName: \(.*\)/\1/p" $PACKER_OUTPUT | tail -1)
SHARED_IMAGE_GALLERY_IMAGE_VERSION=$(sed -n "s/^SharedImageGalleryImageVersion: \(.*\)/\1/p" $PACKER_OUTPUT | tail -1)
TAGS=$(cat packer/azure/tags.out)
if [[ ${SHARED_IMAGE_GALLERY_IMAGE_NAME} == *gen2 ]]; then
HYPERV_GEN="V2"
else
HYPERV_GEN="V1"
fi
# Create JSON and output it
PUBLISHING_INFO=$(jq -n \
--arg distro "${DISTRO}" \
--arg hyperv_gen "${HYPERV_GEN}" \
--arg os_type "${OS_TYPE}" \
--arg managed_image_resource_group_name "${MANAGED_IMAGE_RESOURCE_GROUP_NAME}" \
--arg managed_image_name "${MANAGED_IMAGE_NAME}" \
--arg managed_image_id "${MANAGED_IMAGE_ID}" \
--arg managed_image_location "${MANAGED_IMAGE_LOCATION}" \
--arg managed_image_shared_image_gallery_id "${MANAGED_IMAGE_SHARED_IMAGE_GALLERY_ID}" \
--arg shared_image_gallery_resource_group "${SHARED_IMAGE_GALLERY_RESOURCE_GROUP}" \
--arg shared_image_gallery_name "${SHARED_IMAGE_GALLERY_NAME}" \
--arg shared_image_gallery_image_name "${SHARED_IMAGE_GALLERY_IMAGE_NAME}" \
--arg shared_image_gallery_image_version "${SHARED_IMAGE_GALLERY_IMAGE_VERSION}" \
--arg tags "${TAGS}" \
'$ARGS.named')
echo "Publishing info: ${PUBLISHING_INFO}"
# Save to file for artifact
echo "${PUBLISHING_INFO}" > packer/azure/sig-publishing-info.json
- name: Upload publishing info artifact
uses: actions/upload-artifact@v4
with:
name: publishing-info
path: images/capi/packer/azure/sig-publishing-info.json
retention-days: 7
# ---------------------------------------------------------------------------
# Test
# ---------------------------------------------------------------------------
test:
name: Test SIG Image
needs: build
if: ${{ !inputs.skip_test }}
runs-on: ubuntu-latest
timeout-minutes: 120
env:
KUBERNETES_VERSION: ${{ inputs.kubernetes_version }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Download publishing info artifact
uses: actions/download-artifact@v4
with:
name: publishing-info
path: images/capi/packer/azure/sig/
- name: Import variables from build
id: vars
run: |
set -euo pipefail
PUBLISHING_INFO=$(jq -c . images/capi/packer/azure/sig/sig-publishing-info.json)
echo "PUBLISHING_INFO=${PUBLISHING_INFO}"
echo "OS_TYPE=$(echo "$PUBLISHING_INFO" | jq -r .os_type)" >> $GITHUB_OUTPUT
echo "MANAGED_IMAGE_RESOURCE_GROUP_NAME=$(echo "$PUBLISHING_INFO" | jq -r .managed_image_resource_group_name)" >> $GITHUB_OUTPUT
echo "MANAGED_IMAGE_NAME=$(echo "$PUBLISHING_INFO" | jq -r .managed_image_name)" >> $GITHUB_OUTPUT
echo "MANAGED_IMAGE_ID=$(echo "$PUBLISHING_INFO" | jq -r .managed_image_id)" >> $GITHUB_OUTPUT
echo "MANAGED_IMAGE_LOCATION=$(echo "$PUBLISHING_INFO" | jq -r .managed_image_location)" >> $GITHUB_OUTPUT
echo "MANAGED_IMAGE_SHARED_IMAGE_GALLERY_ID=$(echo "$PUBLISHING_INFO" | jq -r .managed_image_shared_image_gallery_id)" >> $GITHUB_OUTPUT
echo "SHARED_IMAGE_GALLERY_RESOURCE_GROUP=$(echo "$PUBLISHING_INFO" | jq -r .shared_image_gallery_resource_group)" >> $GITHUB_OUTPUT
echo "SHARED_IMAGE_GALLERY_NAME=$(echo "$PUBLISHING_INFO" | jq -r .shared_image_gallery_name)" >> $GITHUB_OUTPUT
echo "SHARED_IMAGE_GALLERY_IMAGE_NAME=$(echo "$PUBLISHING_INFO" | jq -r .shared_image_gallery_image_name)" >> $GITHUB_OUTPUT
echo "SHARED_IMAGE_GALLERY_IMAGE_VERSION=$(echo "$PUBLISHING_INFO" | jq -r .shared_image_gallery_image_version)" >> $GITHUB_OUTPUT
echo "TAGS=$(echo "$PUBLISHING_INFO" | jq -r .tags)" >> $GITHUB_OUTPUT
- name: Configure Kubernetes version
uses: ./.github/actions/configure-k8s-version
with:
kubernetes_version: ${{ inputs.kubernetes_version }}
- name: Setup kustomize
working-directory: images/capi
run: |
set -euo pipefail
export PATH=${PATH}:.local/bin
./packer/azure/scripts/ensure-kustomize.sh
- name: Generate cluster template
working-directory: images/capi
env:
OS_TYPE: ${{ steps.vars.outputs.OS_TYPE }}
run: |
set -euo pipefail
export PATH=${PATH}:.local/bin
if [ "$OS_TYPE" == "Windows" ]; then
kustomize build --load-restrictor LoadRestrictionsNone packer/azure/scripts/test-templates/windows/ > packer/azure/scripts/test-templates/cluster-template.yaml
else
kustomize build --load-restrictor LoadRestrictionsNone packer/azure/scripts/test-templates/linux/ > packer/azure/scripts/test-templates/cluster-template.yaml
fi
- name: Azure Login
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Install Azure CAPI extension
run: |
set -euo pipefail
# Install the Azure CLI Cluster API extension from the official release
az extension add --name capi --yes || az extension add --source "https://github.com/Azure/azure-capi-cli-extension/releases/latest/download/capi-0.0.vnext-py2.py3-none-any.whl" --yes
# Install required binaries
mkdir -p ~/test-binaries
export PATH=${PATH}:~/test-binaries
az capi install -a -ip ~/test-binaries
- name: Create test cluster
working-directory: images/capi
env:
OS_TYPE: ${{ steps.vars.outputs.OS_TYPE }}
MANAGED_IMAGE_LOCATION: ${{ steps.vars.outputs.MANAGED_IMAGE_LOCATION }}
TAGS: ${{ steps.vars.outputs.TAGS }}
run: |
set -euo pipefail
TEST_TEMPLATE="packer/azure/scripts/test-templates/cluster-template.yaml"
export PATH=${PATH}:~/test-binaries
params=()
if [ "$OS_TYPE" == "Windows" ]; then
params+=(--windows)
fi
# Create a dedicated test resource group (not the build resource group)
TEST_RESOURCE_GROUP="image-builder-test-${GITHUB_RUN_ID}"
echo "TEST_RESOURCE_GROUP=${TEST_RESOURCE_GROUP}" >> $GITHUB_ENV
AZURE_LOCATION="${MANAGED_IMAGE_LOCATION}"
az group create -n "${TEST_RESOURCE_GROUP}" -l "${AZURE_LOCATION}" --tags ${TAGS:-}
# Create a cluster
az capi create \
--yes \
--debug \
--name testvm \
--kubernetes-version="${KUBERNETES_VERSION}" \
--location="${AZURE_LOCATION}" \
--resource-group="${TEST_RESOURCE_GROUP}" \
--management-cluster-resource-group-name="${TEST_RESOURCE_GROUP}" \
--control-plane-machine-count=1 \
--node-machine-count=1 \
--template="${TEST_TEMPLATE}" \
--tags="${TAGS}" \
--wait-for-nodes=2 \
"${params[@]}"
# Test if the VM's provisionState is "Succeeded" otherwise fail
timeout 60s bash -c 'set -o pipefail; while ! az vm list -g "$TEST_RESOURCE_GROUP" | jq -e "length > 0 and all(.provisioningState == \"Succeeded\")"; do sleep 1; done'
- name: Clean up test resource group
if: always()
run: |
set -euo pipefail
TEST_RESOURCE_GROUP="${TEST_RESOURCE_GROUP:-}"
if [[ -n "${TEST_RESOURCE_GROUP}" ]]; then
echo "Cleaning up test resource group: ${TEST_RESOURCE_GROUP}"
az group delete -n "${TEST_RESOURCE_GROUP}" --yes --no-wait || true
else
echo "No test resource group to clean up"
fi
# ---------------------------------------------------------------------------
# Promote
# ---------------------------------------------------------------------------
approve_promotion:
name: "Approve Promotion: ${{ inputs.os }} ${{ inputs.os_version }} (k8s ${{ inputs.kubernetes_version }})"
needs: [build, test]
if: ${{ always() && !inputs.skip_promote && needs.build.result == 'success' && (needs.test.result == 'success' || needs.test.result == 'skipped') }}
runs-on: ubuntu-latest
environment: image-promotion-approval
steps:
- name: Promotion Approved
run: echo "Image promotion approved"
promote:
name: Promote to Community Gallery
needs: [build, approve_promotion]
if: ${{ always() && needs.build.result == 'success' && needs.approve_promotion.result == 'success' }}
runs-on: ubuntu-latest
timeout-minutes: 120
env:
RESOURCE_GROUP: ${{ inputs.resource_group }}
GALLERY_NAME: ${{ inputs.gallery_name }}
REPLICATED_REGIONS_INPUT: ${{ inputs.replicated_regions }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Download publishing info artifact
uses: actions/download-artifact@v4
with:
name: publishing-info
path: images/capi/packer/azure/sig/
- name: Import variables from build
id: vars
run: |
set -euo pipefail
PUBLISHING_INFO=$(jq -c . images/capi/packer/azure/sig/sig-publishing-info.json)
echo "PUBLISHING_INFO=${PUBLISHING_INFO}"
echo "DISTRO=$(echo "$PUBLISHING_INFO" | jq -r .distro)" >> $GITHUB_OUTPUT
echo "HYPERV_GEN=$(echo "$PUBLISHING_INFO" | jq -r .hyperv_gen)" >> $GITHUB_OUTPUT
echo "OS_TYPE=$(echo "$PUBLISHING_INFO" | jq -r .os_type)" >> $GITHUB_OUTPUT
echo "MANAGED_IMAGE_RESOURCE_GROUP_NAME=$(echo "$PUBLISHING_INFO" | jq -r .managed_image_resource_group_name)" >> $GITHUB_OUTPUT
echo "MANAGED_IMAGE_NAME=$(echo "$PUBLISHING_INFO" | jq -r .managed_image_name)" >> $GITHUB_OUTPUT
echo "MANAGED_IMAGE_ID=$(echo "$PUBLISHING_INFO" | jq -r .managed_image_id)" >> $GITHUB_OUTPUT
echo "MANAGED_IMAGE_LOCATION=$(echo "$PUBLISHING_INFO" | jq -r .managed_image_location)" >> $GITHUB_OUTPUT
echo "MANAGED_IMAGE_SHARED_IMAGE_GALLERY_ID=$(echo "$PUBLISHING_INFO" | jq -r .managed_image_shared_image_gallery_id)" >> $GITHUB_OUTPUT
echo "SHARED_IMAGE_GALLERY_RESOURCE_GROUP=$(echo "$PUBLISHING_INFO" | jq -r .shared_image_gallery_resource_group)" >> $GITHUB_OUTPUT
echo "SHARED_IMAGE_GALLERY_NAME=$(echo "$PUBLISHING_INFO" | jq -r .shared_image_gallery_name)" >> $GITHUB_OUTPUT
echo "SHARED_IMAGE_GALLERY_IMAGE_NAME=$(echo "$PUBLISHING_INFO" | jq -r .shared_image_gallery_image_name)" >> $GITHUB_OUTPUT
echo "SHARED_IMAGE_GALLERY_IMAGE_VERSION=$(echo "$PUBLISHING_INFO" | jq -r .shared_image_gallery_image_version)" >> $GITHUB_OUTPUT
echo "TAGS=$(echo "$PUBLISHING_INFO" | jq -r .tags)" >> $GITHUB_OUTPUT
- name: Azure Login
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Publish to community gallery
working-directory: images/capi
env:
DISTRO: ${{ steps.vars.outputs.DISTRO }}
HYPERV_GEN: ${{ steps.vars.outputs.HYPERV_GEN }}
OS_TYPE: ${{ steps.vars.outputs.OS_TYPE }}
MANAGED_IMAGE_ID: ${{ steps.vars.outputs.MANAGED_IMAGE_ID }}
MANAGED_IMAGE_LOCATION: ${{ steps.vars.outputs.MANAGED_IMAGE_LOCATION }}
SHARED_IMAGE_GALLERY_IMAGE_NAME: ${{ steps.vars.outputs.SHARED_IMAGE_GALLERY_IMAGE_NAME }}
SHARED_IMAGE_GALLERY_IMAGE_VERSION: ${{ steps.vars.outputs.SHARED_IMAGE_GALLERY_IMAGE_VERSION }}
TAGS: ${{ steps.vars.outputs.TAGS }}
EULA_LINK: ${{ vars.EULA_LINK }}
PUBLISHER_EMAIL: ${{ vars.PUBLISHER_EMAIL }}
PUBLISHER_URI: ${{ vars.PUBLISHER_URI }}
SIG_PUBLISHER: ${{ vars.SIG_PUBLISHER }}
run: |
set -euo pipefail
EOL_DATE=$(date --date='+6 months' +"%Y-%m-%dT00:00:00+00:00")
GALLERY_DESCRIPTION="Shared image gallery for Cluster API Provider Azure"
GALLERY_NAME="${GALLERY_NAME}"
PUBLIC_NAME_PREFIX="ClusterAPI"
RESOURCE_GROUP="${RESOURCE_GROUP}"
SIG_OFFER="reference-images"
# Set replicated regions
REPLICATED_REGIONS="${MANAGED_IMAGE_LOCATION} ${REPLICATED_REGIONS_INPUT}"
# Create the resource group if needed
if ! az group show -n "${RESOURCE_GROUP}" -o none 2>/dev/null; then
az group create -n "${RESOURCE_GROUP}" -l "${MANAGED_IMAGE_LOCATION}" --tags ${TAGS:-}
fi
# Create the public community shared image gallery if it doesn't exist
if ! az sig show --gallery-name "${GALLERY_NAME}" --resource-group "${RESOURCE_GROUP}" -o none 2>/dev/null; then
sig_create_args=(
--gallery-name "${GALLERY_NAME}"
--resource-group "${RESOURCE_GROUP}"
--description "${GALLERY_DESCRIPTION}"
--eula "${EULA_LINK}"
--location "${MANAGED_IMAGE_LOCATION}"
--public-name-prefix "${PUBLIC_NAME_PREFIX}"
--publisher-email "${PUBLISHER_EMAIL}"
--publisher-uri "${PUBLISHER_URI}"
--permissions Community
)
if [[ -n "${TAGS:-}" ]]; then
sig_create_args+=(--tags ${TAGS})
fi
az sig create "${sig_create_args[@]}"
fi
# Translate prohibited words to alternatives in the image definition name
GALLERY_IMAGE_DEFINITION=${SHARED_IMAGE_GALLERY_IMAGE_NAME//ubuntu/ubun2}
GALLERY_IMAGE_DEFINITION=${GALLERY_IMAGE_DEFINITION//windows/win}
# Create image definition if it doesn't exist
if ! az sig image-definition show --gallery-name "${GALLERY_NAME}" --gallery-image-definition "${GALLERY_IMAGE_DEFINITION}" --resource-group "${RESOURCE_GROUP}" -o none 2>/dev/null; then
az sig image-definition create \
--resource-group "${RESOURCE_GROUP}" \
--gallery-name "${GALLERY_NAME}" \
--gallery-image-definition "${GALLERY_IMAGE_DEFINITION}" \
--publisher "${SIG_PUBLISHER}" \
--offer "${SIG_OFFER}" \
--sku "${DISTRO}" \
--hyper-v-generation "${HYPERV_GEN}" \
--os-type "${OS_TYPE}" \
| tee -a sig-publishing.json
fi
# Delete the image version if it exists (always create a new image, overwriting if necessary)
if az sig image-version show --gallery-name "${GALLERY_NAME}" --gallery-image-definition "${GALLERY_IMAGE_DEFINITION}" --gallery-image-version "${SHARED_IMAGE_GALLERY_IMAGE_VERSION}" --resource-group "${RESOURCE_GROUP}" -o none 2>/dev/null; then
az sig image-version delete \
--resource-group "${RESOURCE_GROUP}" \
--gallery-name "${GALLERY_NAME}" \
--gallery-image-definition "${GALLERY_IMAGE_DEFINITION}" \
--gallery-image-version "${SHARED_IMAGE_GALLERY_IMAGE_VERSION}"
fi
# Copy the tags from the managed image to the image version
IMAGE_TAGS=$(az tag list --resource-id "${MANAGED_IMAGE_ID}" | jq -r '.properties.tags | to_entries | map("\(.key)=\(.value)") | join(" ")')
# Create the image version
az sig image-version create \
--resource-group "${RESOURCE_GROUP}" \
--gallery-name "${GALLERY_NAME}" \
--gallery-image-definition "${GALLERY_IMAGE_DEFINITION}" \
--gallery-image-version "${SHARED_IMAGE_GALLERY_IMAGE_VERSION}" \
--target-regions ${REPLICATED_REGIONS} \
--managed-image "${MANAGED_IMAGE_ID}" \
--end-of-life-date "${EOL_DATE}" \
--tags ${IMAGE_TAGS:-} \
| tee sig-publishing.json
- name: Upload publishing artifact
uses: actions/upload-artifact@v4
with:
name: sig-publishing
path: images/capi/sig-publishing.json
retention-days: 30
# ---------------------------------------------------------------------------
# Clean
# ---------------------------------------------------------------------------
clean:
name: Clean Staging Resources
needs: [build, test, approve_promotion, promote]
if: ${{ always() && needs.build.result == 'success' }}
runs-on: ubuntu-latest
timeout-minutes: 30
env:
RESOURCE_GROUP: ${{ inputs.resource_group }}
STAGING_GALLERY_NAME: ${{ inputs.staging_gallery_name }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Download publishing info artifact
uses: actions/download-artifact@v4
with:
name: publishing-info
path: images/capi/packer/azure/sig/
- name: Import variables from build
id: vars
run: |
set -euo pipefail
PUBLISHING_INFO=$(jq -c . images/capi/packer/azure/sig/sig-publishing-info.json)
echo "PUBLISHING_INFO=${PUBLISHING_INFO}"
echo "MANAGED_IMAGE_ID=$(echo "$PUBLISHING_INFO" | jq -r .managed_image_id)" >> $GITHUB_OUTPUT
echo "SHARED_IMAGE_GALLERY_IMAGE_NAME=$(echo "$PUBLISHING_INFO" | jq -r .shared_image_gallery_image_name)" >> $GITHUB_OUTPUT
echo "SHARED_IMAGE_GALLERY_IMAGE_VERSION=$(echo "$PUBLISHING_INFO" | jq -r .shared_image_gallery_image_version)" >> $GITHUB_OUTPUT
- name: Azure Login
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Clean up staging resources
working-directory: images/capi
env:
MANAGED_IMAGE_ID: ${{ steps.vars.outputs.MANAGED_IMAGE_ID }}
SHARED_IMAGE_GALLERY_IMAGE_NAME: ${{ steps.vars.outputs.SHARED_IMAGE_GALLERY_IMAGE_NAME }}
SHARED_IMAGE_GALLERY_IMAGE_VERSION: ${{ steps.vars.outputs.SHARED_IMAGE_GALLERY_IMAGE_VERSION }}
run: |
set -euo pipefail
GALLERY_NAME="${STAGING_GALLERY_NAME:-staging_gallery}"
RESOURCE_GROUP="${RESOURCE_GROUP:-cluster-api-gallery}"
# Delete the source managed image if it exists
if az image show --ids "${MANAGED_IMAGE_ID}" -o none 2>/dev/null; then
echo "Deleting managed image: ${MANAGED_IMAGE_ID}"
az image delete --ids "${MANAGED_IMAGE_ID}"
else
echo "Managed image not found, skipping deletion"
fi
# Delete the staging image version if it exists
if az sig image-version show --resource-group "${RESOURCE_GROUP}" --gallery-name "${GALLERY_NAME}" --gallery-image-definition "${SHARED_IMAGE_GALLERY_IMAGE_NAME}" --gallery-image-version "${SHARED_IMAGE_GALLERY_IMAGE_VERSION}" -o none 2>/dev/null; then
echo "Deleting staging image version: ${SHARED_IMAGE_GALLERY_IMAGE_VERSION}"
az sig image-version delete \
--resource-group "${RESOURCE_GROUP}" \
--gallery-name "${GALLERY_NAME}" \
--gallery-image-definition "${SHARED_IMAGE_GALLERY_IMAGE_NAME}" \
--gallery-image-version "${SHARED_IMAGE_GALLERY_IMAGE_VERSION}"
else
echo "Staging image version not found, skipping deletion"
fi