Skip to content

Instantly share code, notes, and snippets.

@willdurand
Created June 6, 2022 22:34
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save willdurand/7410cc5e4ca7297c904f9ed13f767ea4 to your computer and use it in GitHub Desktop.
Save willdurand/7410cc5e4ca7297c904f9ed13f767ea4 to your computer and use it in GitHub Desktop.
A script to download (pull) public OCI images from the Docker registry (v2)
#!/usr/bin/env bash
# Licensed under the MIT License - https://github.com/willdurand
set -euo pipefail
BASE_DIR="${BASE_DIR:-.}"
file_created() {
echo "[+] $1"
}
write_file() {
echo "$1" > "$2"
file_created "$2"
}
pull() {
image="$1"
version="latest"
if [[ "$#" -gt 1 ]] ; then
version="$2"
fi
image_dir="$BASE_DIR/oci-images/$image/$version"
if [[ -d "$image_dir" ]] ; then
echo "WARNING: the directory '$image_dir' already exists!"
echo " Do you want to (1) automatically delete it and continue OR (2) abort?"
select yn in "Yes" "No"; do
case $yn in
Yes) break;;
No) exit 2;;
esac
done
rm -rf "$image_dir/"{blobs,layers,manifest.json}
fi
mkdir -p "$image_dir"/{blobs,layers}
file_created "$image_dir"
# Get an auth token:
token=$(curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:$image:pull" | jq -r '.token')
authHeader=(-H "Authorization: Bearer $token")
# Get image manifest, see: https://github.com/opencontainers/image-spec/blob/main/manifest.md
manifest=$(curl -s -H "Accept: application/vnd.docker.distribution.manifest.v2+json" "${authHeader[@]}" "https://index.docker.io/v2/$image/manifests/$version" | jq -c)
write_file "$manifest" "$image_dir/manifest.json"
# Get image configuration:
mediaType=$(echo "$manifest" | jq -r '.config.mediaType')
digest=$(echo "$manifest" | jq -r '.config.digest')
config_blob=$(curl -L -s -H "$mediaType" "${authHeader[@]}" "https://index.docker.io/v2/$image/blobs/$digest" | jq -c)
write_file "$config_blob" "$image_dir/blobs/config.json"
# Get layers:
mapfile -t diff_ids < <(echo "$config_blob" | jq -r '.rootfs.diff_ids[]')
i=0
echo "$manifest" | jq -r -c '.layers[]' | while read -r layer; do
mediaType=$(echo "$layer" | jq -r '.mediaType')
digest=$(echo "$layer" | jq -r '.digest')
curl --output "$image_dir/blobs/$digest" -L -s -H "$mediaType" "${authHeader[@]}" "https://index.docker.io/v2/$image/blobs/$digest"
file_created "$image_dir/blobs/$digest"
# TODO: check media type to be sure that `tar xz` is the right tool for
# unpacking a layer below...
mkdir -p "$image_dir/layers/${diff_ids[$i]}"
tar xzf "$image_dir/blobs/$digest" -C "$image_dir/layers/${diff_ids[$i]}"
file_created "$image_dir/layers/${diff_ids[$i]}"
((i=i+1))
done
# Phew!
echo "OK"
}
if [[ "$#" == 0 ]]; then
echo "USAGE: $0 <image> [<version>]"
echo ""
echo "Examples:"
echo ""
echo " $0 library/alpine"
echo " $0 library/alpine latest"
echo ""
exit 1
fi
pull "$@"
@willdurand
Copy link
Author

$ BASE_DIR=/tmp ./pull.sh library/busybox
[+] /tmp/oci-images/library/busybox/latest
[+] /tmp/oci-images/library/busybox/latest/manifest.json
[+] /tmp/oci-images/library/busybox/latest/blobs/config.json
[+] /tmp/oci-images/library/busybox/latest/blobs/sha256:cecc78ee407586f0c9eec844d60b8d40698a35e8d3d59f11a45a6b19a21fb7f0
[+] /tmp/oci-images/library/busybox/latest/layers/sha256:7ffd7a2b9a8f5c3c0380c79e24059605cf353be164d3870658eca385ffc89535
OK

$ tree -L 6 /tmp/oci-images/ 
/tmp/oci-images/
└── library
    └── busybox
        └── latest
            ├── blobs
            │   ├── config.json
            │   └── sha256:cecc78ee407586f0c9eec844d60b8d40698a35e8d3d59f11a45a6b19a21fb7f0
            ├── layers
            │   └── sha256:7ffd7a2b9a8f5c3c0380c79e24059605cf353be164d3870658eca385ffc89535
            │       ├── bin
            │       ├── dev
            │       ├── etc
            │       ├── home
            │       ├── root
            │       ├── tmp
            │       ├── usr
            │       └── var
            └── manifest.json

14 directories, 3 files

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment