Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
275 changes: 72 additions & 203 deletions .github/workflows/build-publish-mcr.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
# This Github Action will build and publish images to Azure Container Registry (ACR), from where the published images will be
# automatically pushed to the trusted registry, Microsoft Container Registry (MCR).
# This Github Action packages the ARC member cluster agents Helm chart and publishes it to Azure Container
# Registry (ACR), from where it is automatically pushed to the trusted registry, Microsoft Container Registry (MCR).

# TO-DO (chenyu1): evaluate if we need to hide arch-specific images in ACR.
# The agent images are built and published by a separate pipeline; this workflow only resolves their
# already-published tags.

name: Building and Pushing to MCR
on:
workflow_dispatch:
inputs:
releaseTag:
description: 'Release tag to publish images, defaults to the latest one'
description: 'Release tag of the Azure/fleet images, defaults to the latest one'
type: string
fleetNetworkingReleaseTag:
description: 'Release tag of the Azure/fleet-networking images, defaults to the latest one'
type: string

permissions:
Expand All @@ -17,86 +21,99 @@ permissions:

env:
# `public` indicates images to MCR will be publicly available, and will be removed in the final MCR images
REGISTRY_REPO: public/aks/fleet
ARC_REGISTRY_REPO: public/microsoft.fleetmember
# The MCR registry API omits the `public` prefix that the ACR push path carries.
MCR_FLEET_REPO: aks/v2/fleet

jobs:
prepare-variables:
runs-on: ubuntu-latest
outputs:
git_ref: ${{ steps.vars.outputs.git_ref }}
release_tag: ${{ steps.vars.outputs.release_tag }}
fleet_networking_version: ${{ steps.vars.outputs.fleet_networking_version }}
arc_helmchart_version: ${{ steps.vars.outputs.arc_helmchart_version }}
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: 'Set output variables'
id: vars
run: |
# set the image version
RELEASE_TAG=${{ inputs.releaseTag }}
if [ -z "$RELEASE_TAG" ]; then
RELEASE_TAG=$(git describe --tags "$(git rev-list --tags --max-count=1)")
echo "The user input release tag is empty, will use the latest tag $RELEASE_TAG."
set -euo pipefail

# Images are published to MCR as <release tag>-<revision>; the revision is bumped on every rebuild
# of the same source, so the highest one is current.
resolve_mcr_tag() {
local repo="$1" base="$2" tags revision
tags=$(curl -sf --retry 3 --retry-delay 5 --retry-all-errors --max-time 30 \
"https://mcr.microsoft.com/v2/${repo}/tags/list" | jq -r '.tags[]?')
if [ -z "$tags" ]; then
echo "Failed to list tags for ${repo}." >&2
return 1
fi
revision=$(echo "$tags" | grep -E "^${base}-[0-9]+$" | sed "s/^${base}-//" | sort -n | tail -1)
if [ -z "$revision" ]; then
echo "No published revision of ${base} found in ${repo}; available tags:" >&2
echo "$tags" >&2
return 1
fi
echo "${base}-${revision}"
}

# Release tags are read from the upstream repositories rather than the checkout, which on a fork
# carries no tags.
latest_github_tag() {
local repo="$1" tag
tag=$(curl -sf --retry 3 --retry-delay 5 --retry-all-errors --max-time 30 \
"https://api.github.com/repos/${repo}/tags" | jq -r '.[0].name // empty')
if [ -z "$tag" ]; then
echo "Failed to resolve the latest tag of ${repo}." >&2
return 1
fi
echo "$tag"
}

FLEET_RELEASE_TAG="${{ inputs.releaseTag }}"
if [ -z "$FLEET_RELEASE_TAG" ]; then
FLEET_RELEASE_TAG=$(latest_github_tag "Azure/fleet")
echo "The user input release tag is empty, will use the latest tag $FLEET_RELEASE_TAG."
fi

FLEET_NETWORKING_RELEASE_TAG="${{ inputs.fleetNetworkingReleaseTag }}"
if [ -z "$FLEET_NETWORKING_RELEASE_TAG" ]; then
FLEET_NETWORKING_RELEASE_TAG=$(latest_github_tag "Azure/fleet-networking")
echo "The user input fleet-networking release tag is empty, will use the latest tag $FLEET_NETWORKING_RELEASE_TAG."
fi

# Member agent, refresh token and CRD installer share a revision, so one lookup tags all three.
RELEASE_TAG=$(resolve_mcr_tag "${{ env.MCR_FLEET_REPO }}/member-agent" "$FLEET_RELEASE_TAG")
echo "release_tag=$RELEASE_TAG" >> "$GITHUB_OUTPUT"
echo "Using fleet image tag: $RELEASE_TAG"

# Strip 'v' prefix from RELEASE_TAG for helm chart version
ARC_HELMCHART_VERSION="${RELEASE_TAG#v}"
echo "arc_helmchart_version=$ARC_HELMCHART_VERSION" >> "$GITHUB_OUTPUT"
echo "Using Arc Helm Chart version: $ARC_HELMCHART_VERSION"
# The revision suffix has no matching git tag, so checkout uses the plain release tag.
echo "git_ref=$FLEET_RELEASE_TAG" >> "$GITHUB_OUTPUT"

# Fetch the latest fleet-networking version
# NOTE: The fleet-networking image must be cut and pushed to MCR first before retrieving this version
FLEET_NETWORKING_VERSION="${FLEET_NETWORKING_VERSION:-$(curl "https://api.github.com/repos/Azure/fleet-networking/tags" | jq -r '.[0].name')}"
FLEET_NETWORKING_VERSION=$(resolve_mcr_tag "${{ env.MCR_FLEET_REPO }}/mcs-controller-manager" "$FLEET_NETWORKING_RELEASE_TAG")
echo "fleet_networking_version=$FLEET_NETWORKING_VERSION" >> "$GITHUB_OUTPUT"
echo "Using Fleet Networking version: $FLEET_NETWORKING_VERSION"
echo "Using fleet-networking image tag: $FLEET_NETWORKING_VERSION"

# NOTE: As exporting a variable from a secret is not possible, the shared variable registry obtained
# from AZURE_REGISTRY secret is not exported from here.
# Helm chart versions must be valid semver, which disallows the leading 'v'.
ARC_HELMCHART_VERSION="${RELEASE_TAG#v}"
echo "arc_helmchart_version=$ARC_HELMCHART_VERSION" >> "$GITHUB_OUTPUT"
echo "Using Arc Helm Chart version: $ARC_HELMCHART_VERSION"

publish-images-amd64:
publish-arc-helm-chart:
runs-on:
labels: [self-hosted, "1ES.Pool=1es-aks-fleet-pool-ubuntu"]
needs: prepare-variables
steps:
- uses: actions/checkout@v6
with:
ref: ${{ needs.prepare-variables.outputs.release_tag }}
ref: ${{ needs.prepare-variables.outputs.git_ref }}
- name: 'Login the ACR'
run: |
az login --identity
az login --identity
az acr login -n ${{ secrets.AZURE_REGISTRY }}
- name: Build and publish hub-agent
run: |
make docker-build-hub-agent
env:
HUB_AGENT_IMAGE_VERSION: ${{ needs.prepare-variables.outputs.release_tag }}-amd64
REGISTRY: ${{ secrets.AZURE_REGISTRY }}/${{ env.REGISTRY_REPO}}
- name: Build and publish member-agent
run: |
make docker-build-member-agent
env:
MEMBER_AGENT_IMAGE_VERSION: ${{ needs.prepare-variables.outputs.release_tag }}-amd64
REGISTRY: ${{ secrets.AZURE_REGISTRY }}/${{ env.REGISTRY_REPO}}
- name: Build and publish refresh-token
run: |
make docker-build-refresh-token
env:
REFRESH_TOKEN_IMAGE_VERSION: ${{ needs.prepare-variables.outputs.release_tag }}-amd64
REGISTRY: ${{ secrets.AZURE_REGISTRY }}/${{ env.REGISTRY_REPO}}
- name: Build and publish crd-installer
run: |
make docker-build-crd-installer
env:
CRD_INSTALLER_IMAGE_VERSION: ${{ needs.prepare-variables.outputs.release_tag }}-amd64
REGISTRY: ${{ secrets.AZURE_REGISTRY }}/${{ env.REGISTRY_REPO}}
# Build Arc Extension for member clusters
# Arc-connected clusters can join fleets as member clusters through an Arc Extension.
# An Arc Extension is a packaged Helm chart that gets deployed to Arc clusters.
# This step packages both the fleet member agent and networking agents into a single
# Helm chart for Arc deployment, since Arc Extensions require all components to be bundled together.
# Arc Extensions require all components bundled in one chart, hence the member agent and the
# networking agents are packaged together here.
- name: Build and publish ARC member cluster agents helm chart
run: |
make helm-package-arc-member-cluster-agents
Expand All @@ -108,151 +125,3 @@ jobs:
MCS_CONTROLLER_IMAGE_VERSION: ${{ needs.prepare-variables.outputs.fleet_networking_version }}
MEMBER_NET_CONTROLLER_IMAGE_VERSION: ${{ needs.prepare-variables.outputs.fleet_networking_version }}
REGISTRY: ${{ secrets.AZURE_REGISTRY }}/${{ env.ARC_REGISTRY_REPO}}

publish-images-arm64:
runs-on:
labels: [self-hosted, "1ES.Pool=1es-aks-fleet-pool-ubuntu-arm64"]
needs: prepare-variables
steps:
- uses: actions/checkout@v6
with:
ref: ${{ needs.prepare-variables.outputs.release_tag }}
- name: 'Install the Azure CLI'
# Note (chenyu1): the self-hosted 1ES ARM64 pool, for some reason, does not have Azure CLI installed by default;
# install it manually here.
run:
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
- name: 'Set up build dependencies'
# Note (chenyu1): the self-hosted 1ES ARM64 pool, for some reason, does not have the common build
# tools (e.g., make) installed by default; install them manually.
run: |
sudo apt-get update
sudo apt-get install -y build-essential acl
- name: 'Set up Docker'
# Note (chenyu1): the self-hosted 1ES ARM64 pool, for some reason, does not have Docker installed by default,
# and cannot have Docker installed via the docker/setup-docker-action Github Action, hence the manual setup
# steps here.
run: |
sudo apt-get update
sudo apt-get -y install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
- name: 'Enable Docker access'
# Note (chenyu1): there are situations where the newgrp command will not take effect; set access
# to the docker daemon directly just in case.
run: |
sudo groupadd docker || true
echo "Adding $USER to the docker group"
sudo usermod -aG docker "$USER"
newgrp docker
sudo setfacl --modify "user:$USER:rw" /var/run/docker.sock
- name: 'Login the ACR'
# Note (chenyu1): must not use root privileges; the system seems to have some trouble
# retrieving credentials when sudo is used.
run: |
az login --identity
az acr login -n ${{ secrets.AZURE_REGISTRY }}
- name: 'Verify Docker CLI'
run: |
docker version
docker info
- name: Build and publish hub-agent
run: |
make docker-build-hub-agent
env:
HUB_AGENT_IMAGE_VERSION: ${{ needs.prepare-variables.outputs.release_tag }}-arm64
REGISTRY: ${{ secrets.AZURE_REGISTRY }}/${{ env.REGISTRY_REPO}}
TARGET_ARCH: arm64
- name: Build and publish member-agent
run: |
make docker-build-member-agent
env:
MEMBER_AGENT_IMAGE_VERSION: ${{ needs.prepare-variables.outputs.release_tag }}-arm64
REGISTRY: ${{ secrets.AZURE_REGISTRY }}/${{ env.REGISTRY_REPO}}
TARGET_ARCH: arm64
- name: Build and publish refresh-token
run: |
make docker-build-refresh-token
env:
REFRESH_TOKEN_IMAGE_VERSION: ${{ needs.prepare-variables.outputs.release_tag }}-arm64
REGISTRY: ${{ secrets.AZURE_REGISTRY }}/${{ env.REGISTRY_REPO}}
TARGET_ARCH: arm64
- name: Build and publish crd-installer
run: |
make docker-build-crd-installer
env:
CRD_INSTALLER_IMAGE_VERSION: ${{ needs.prepare-variables.outputs.release_tag }}-arm64
REGISTRY: ${{ secrets.AZURE_REGISTRY }}/${{ env.REGISTRY_REPO}}
TARGET_ARCH: arm64

create-image-manifest-bundle:
runs-on:
# Use the x86_64 1ES pool to run this job; in theory it can be run on the ARM64 1ES pool as well.
labels: [self-hosted, "1ES.Pool=1es-aks-fleet-pool-ubuntu"]
needs: [prepare-variables, publish-images-amd64, publish-images-arm64]
steps:
- name: 'Wait until images are processed'
# Note (chenyu1): as we are pulling from ACR rather than MCR, the images should be available almost
# immediately after the push is done; the delay is added here as a precaution.
run: |
echo "Waiting for 3 minutes to ensure that images are fully processed"
sleep 180
- name: 'Login the ACR'
run: |
az login --identity
az acr login -n ${{ secrets.AZURE_REGISTRY }}
- name: 'Pull the hub agent images from ACR'
# Note (chenyu1): must set the target platform explictly.
run: |
docker pull --platform linux/amd64 ${{ secrets.AZURE_REGISTRY }}/${{ env.REGISTRY_REPO}}/hub-agent:${{ needs.prepare-variables.outputs.release_tag }}-amd64
docker pull --platform linux/arm64 ${{ secrets.AZURE_REGISTRY }}/${{ env.REGISTRY_REPO}}/hub-agent:${{ needs.prepare-variables.outputs.release_tag }}-arm64
- name: 'Create and push multi-arch image manifests for the hub agent image'
# Note (chenyu1): use `docker buildx imagetools create`, otherwise attestations cannot be perserved.
run: |
docker buildx imagetools create \
-t ${{ secrets.AZURE_REGISTRY }}/${{ env.REGISTRY_REPO}}/hub-agent:${{ needs.prepare-variables.outputs.release_tag }} \
${{ secrets.AZURE_REGISTRY }}/${{ env.REGISTRY_REPO}}/hub-agent:${{ needs.prepare-variables.outputs.release_tag }}-amd64 \
${{ secrets.AZURE_REGISTRY }}/${{ env.REGISTRY_REPO}}/hub-agent:${{ needs.prepare-variables.outputs.release_tag }}-arm64
- name: 'Pull the member agent images from ACR'
# Note (chenyu1): must set the target platform explictly.
run: |
docker pull --platform linux/amd64 ${{ secrets.AZURE_REGISTRY }}/${{ env.REGISTRY_REPO}}/member-agent:${{ needs.prepare-variables.outputs.release_tag }}-amd64
docker pull --platform linux/arm64 ${{ secrets.AZURE_REGISTRY }}/${{ env.REGISTRY_REPO}}/member-agent:${{ needs.prepare-variables.outputs.release_tag }}-arm64
- name: 'Create and push multi-arch image manifests for the member agent image'
# Note (chenyu1): use `docker buildx imagetools create`, otherwise attestations cannot be perserved.
run: |
docker buildx imagetools create \
-t ${{ secrets.AZURE_REGISTRY }}/${{ env.REGISTRY_REPO}}/member-agent:${{ needs.prepare-variables.outputs.release_tag }} \
${{ secrets.AZURE_REGISTRY }}/${{ env.REGISTRY_REPO}}/member-agent:${{ needs.prepare-variables.outputs.release_tag }}-amd64 \
${{ secrets.AZURE_REGISTRY }}/${{ env.REGISTRY_REPO}}/member-agent:${{ needs.prepare-variables.outputs.release_tag }}-arm64
- name: 'Pull the refresh token images from ACR'
# Note (chenyu1): must set the target platform explictly.
run: |
docker pull --platform linux/amd64 ${{ secrets.AZURE_REGISTRY }}/${{ env.REGISTRY_REPO}}/refresh-token:${{ needs.prepare-variables.outputs.release_tag }}-amd64
docker pull --platform linux/arm64 ${{ secrets.AZURE_REGISTRY }}/${{ env.REGISTRY_REPO}}/refresh-token:${{ needs.prepare-variables.outputs.release_tag }}-arm64
- name: 'Create and push multi-arch image manifests for the refresh token image'
# Note (chenyu1): use `docker buildx imagetools create`, otherwise attestations cannot be perserved.
run: |
docker buildx imagetools create \
-t ${{ secrets.AZURE_REGISTRY }}/${{ env.REGISTRY_REPO}}/refresh-token:${{ needs.prepare-variables.outputs.release_tag }} \
${{ secrets.AZURE_REGISTRY }}/${{ env.REGISTRY_REPO}}/refresh-token:${{ needs.prepare-variables.outputs.release_tag }}-amd64 \
${{ secrets.AZURE_REGISTRY }}/${{ env.REGISTRY_REPO}}/refresh-token:${{ needs.prepare-variables.outputs.release_tag }}-arm64
- name: 'Pull the crd installer images from ACR'
# Note (chenyu1): must set the target platform explictly.
run: |
docker pull --platform linux/amd64 ${{ secrets.AZURE_REGISTRY }}/${{ env.REGISTRY_REPO}}/crd-installer:${{ needs.prepare-variables.outputs.release_tag }}-amd64
docker pull --platform linux/arm64 ${{ secrets.AZURE_REGISTRY }}/${{ env.REGISTRY_REPO}}/crd-installer:${{ needs.prepare-variables.outputs.release_tag }}-arm64
- name: 'Create and push multi-arch image manifests for the crd installer image'
# Note (chenyu1): use `docker buildx imagetools create`, otherwise attestations cannot be perserved.
run: |
docker buildx imagetools create \
-t ${{ secrets.AZURE_REGISTRY }}/${{ env.REGISTRY_REPO}}/crd-installer:${{ needs.prepare-variables.outputs.release_tag }} \
${{ secrets.AZURE_REGISTRY }}/${{ env.REGISTRY_REPO}}/crd-installer:${{ needs.prepare-variables.outputs.release_tag }}-amd64 \
${{ secrets.AZURE_REGISTRY }}/${{ env.REGISTRY_REPO}}/crd-installer:${{ needs.prepare-variables.outputs.release_tag }}-arm64
2 changes: 2 additions & 0 deletions charts/member-agent-arc/.helmignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@
.idea/
*.tmproj
.vscode/
# Tests
*_test.go
Loading
Loading