Skip to content

Instantly share code, notes, and snippets.

@zxkane
Created July 4, 2024 16:28
Show Gist options
  • Save zxkane/4624d699ff664698d94e3b8bf51de3e2 to your computer and use it in GitHub Desktop.
Save zxkane/4624d699ff664698d94e3b8bf51de3e2 to your computer and use it in GitHub Desktop.

Revisions

  1. zxkane created this gist Jul 4, 2024.
    112 changes: 112 additions & 0 deletions mirror-multi-arch-repos.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,112 @@
    #!/bin/bash

    set -o errexit # exit on first error
    set -o nounset # exit on using unset variables
    set -o pipefail # exit on any error in a pipeline
    set -x # enable debugging

    # Script to mirror multi-arch images from Docker Hub to Amazon ECR Public.
    # Usage: ./mirror-multi-arch-repos.sh <source-image>:<tag> <ecr-repo-name>

    if [ $# -ne 2 ]; then
    echo "Usage: $0 <source-image>:<tag> <ecr-repo-name>"
    exit 1
    fi

    SOURCE_IMAGE_TAG_STR=$1
    ECR_REPO_NAME=$2

    # Get ECR registry URL
    ECR_REGISTRY_URL=$(aws ecr-public describe-registries --region us-east-1 --query 'registries[0].registryUri' --output text)

    # Function to extract platform from manifest as a list of json strings
    get_platforms() {
    docker manifest inspect $SOURCE_IMAGE_TAG_STR | jq -r '.manifests[].platform | select(.architecture!="unknown") | @json'
    }

    get_suffix() {
    local os=$1
    local arch=$2
    local variant=${3:-""}

    prefix="-"

    suffix=${os}-${arch}${variant:+$prefix$variant}

    echo $suffix
    }
    # Pull and push images to ECR
    pull_and_push_to_ecr() {
    # Create ECR repository if it doesn't exist
    aws ecr-public create-repository --repository-name $ECR_REPO_NAME --region us-east-1 || true

    platforms=( $(get_platforms) )

    for platform in "${platforms[@]}"; do

    arch=$(echo $platform | jq -r .architecture)
    os=$(echo $platform | jq -r .os)
    variant=$(echo $platform | jq -r '.variant? // empty')

    suffix=$(get_suffix $os $arch $variant)

    local source_image_with_platform="${SOURCE_IMAGE_TAG_STR}-${suffix}"
    local ecr_image_with_arch="${ECR_REGISTRY_URL}/${source_image_with_platform}"

    echo "Pulling $SOURCE_IMAGE_TAG_STR for $suffix"
    docker pull --platform $os/$arch${variant:+/$variant} $SOURCE_IMAGE_TAG_STR

    echo "Tagging $SOURCE_IMAGE_TAG_STR as $ecr_image_with_arch"
    docker tag $SOURCE_IMAGE_TAG_STR $ecr_image_with_arch

    echo "Pushing $ecr_image_with_arch to ECR"
    docker push $ecr_image_with_arch
    done
    }

    # Create and push manifest list
    create_manifest() {
    local manifest_list="${ECR_REGISTRY_URL}/${SOURCE_IMAGE_TAG_STR}"
    echo "Creating manifest list: $manifest_list"

    platforms=( $(get_platforms) )

    for platform in "${platforms[@]}"; do

    arch=$(echo $platform | jq -r .architecture)
    os=$(echo $platform | jq -r .os)
    variant=$(echo $platform | jq -r '.variant? // empty')

    suffix=$(get_suffix $os $arch $variant)

    # Construct the image tag with the suffix
    image_tag="${SOURCE_IMAGE_TAG}-${suffix}"

    # Add the image to the manifest list
    docker manifest create "$manifest_list" "${ECR_REGISTRY_URL}/${SOURCE_IMAGE}:${image_tag}" --amend

    docker manifest annotate $manifest_list \
    "${ECR_REGISTRY_URL}/${SOURCE_IMAGE}:${image_tag}" \
    --os $os --arch $arch ${variant:+--variant $variant}
    done

    echo "Pushing manifest list to ECR"
    docker manifest push $manifest_list
    }

    # a method to parse the image name and tag from SOURCE_IMAGE_TAG_STR
    # e.g. "nginx:latest" will be split into "nginx" and "latest"
    SOURCE_IMAGE=$(echo $SOURCE_IMAGE_TAG_STR | cut -d: -f1)
    SOURCE_IMAGE_TAG=$(echo $SOURCE_IMAGE_TAG_STR | cut -d: -f2)

    # Main execution
    echo "Authenticating with ECR"
    aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws

    echo "Pulling multi-architecture images for $SOURCE_IMAGE_TAG_STR, then pushing images to ECR"
    pull_and_push_to_ecr

    echo "Creating and pushing manifest list"
    create_manifest

    echo "Mirroring complete for $SOURCE_IMAGE_TAG_STR to ${ECR_REGISTRY_URL}/${ECR_REPO_NAME}"