Build Azure SIG Image #30
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # 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 |