diff --git a/roles/base/tasks/apt.yml b/roles/base/tasks/apt.yml index 3c37b79cc280b814cd570601727e73957af3f2e3..777128fe924a9a81ece5aa268c7c701f73cabbc7 100644 --- a/roles/base/tasks/apt.yml +++ b/roles/base/tasks/apt.yml @@ -79,6 +79,7 @@ - acpid - lz4 - man-db + - jq - name: Install extra packages apt: diff --git a/roles/docker/tasks/start.yml b/roles/docker/tasks/start.yml index 5f266e593cd52016c77f676e29fe79666724bab8..a2f6019f0c96d459606a48ae5ecbd128a766ff32 100644 --- a/roles/docker/tasks/start.yml +++ b/roles/docker/tasks/start.yml @@ -58,11 +58,12 @@ block: - name: Pull the container images - command: "float-pull-image {{ item.container.image }}" + command: "float-pull-image{% if ansible_check_mode %} --check{% endif %} {{ item.container.image }}" register: container_image loop: "{{ float_enabled_containers }}" changed_when: "container_image.rc == 0" failed_when: "container_image.rc not in [0, 42]" + check_mode: no - name: Start the systemd units systemd: diff --git a/roles/docker/templates/float-pull-image.j2 b/roles/docker/templates/float-pull-image.j2 index 27f5f910af0beb932c0d39431445686eb0908009..6587a66ccf147fa35b88dcd680a3611cd486a7a6 100755 --- a/roles/docker/templates/float-pull-image.j2 +++ b/roles/docker/templates/float-pull-image.j2 @@ -6,30 +6,102 @@ set -u -image=${1:-} binary={{ container_runtime }} -if [ -z "$image" ]; then - echo "usage: <image url>" - exit 1 -fi +get_main_auth_token() { + jq -r .auths.\"${registry_hostname}\".auth ~/.docker/config.json 2>/dev/null +} + +get_auth_token() { + local url="$1" + local auth_hdr="$(curl -s -I -H "Accept: application/vnd.docker.distribution.manifest.v2+json" "$url" \ + | awk 'BEGIN{IGNORECASE=1} /^www-authenticate:/ {print $3}')" + local scope=$(printf "%s" "${auth_hdr}" | sed -e 's/^.*scope="\([^"]*\)".*$/\1/') + local service=$(printf "%s" "${auth_hdr}" | sed -e 's/^.*service="\([^"]*\)".*$/\1/') + local realm=$(printf "%s" "${auth_hdr}" | sed -e 's/^.*realm="\([^"]*\)".*$/\1/') + local main_auth_token="$(get_main_auth_token)" + local curl_opts="" + if [ -n "${main_auth_token}" ]; then + curl_opts="-H \"Authorization: Bearer ${main_auth_token}\"" + fi + curl ${curl_opts} -s "${realm}?service=${service}&scope=${scope}" | jq -r .token +} -get_image_version() { - $binary inspect --type image --format "{{ '{{' }}.Id{{ '}}' }}" $image +get_remote_image_version() { + local url="https://${registry_hostname}/v2/${image_path}/manifests/${image_tag}" + local token="$(get_auth_token "$url")" + curl -s -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \ + -H "Authorization: Bearer ${token}" \ + | jq -r .config.digest } -pre_version=$(get_image_version) +get_local_image_version() { + $binary inspect --type image --format "{{ '{{' }}.Id{{ '}}' }}" $image +} -$binary pull $image +check() { + local_version=$(get_local_image_version) + remote_version=$(get_remote_image_version) + if [ -z "${remote_version}" ]; then + echo "ERROR: Failed to obtain container version from the registry" >&2 + exit 1 + fi + if [ "${local_version}" != "${remote_version}" ]; then + exit 42 + fi +} -if [ $? -gt 0 ]; then - exit 1 -fi +pull() { + pre_version=$(get_local_image_version) -post_version=$(get_image_version) + $binary pull $image + if [ $? -gt 0 ]; then + exit 1 + fi -if [ "$pre_version" == "$post_version" ]; then - exit 42 + post_version=$(get_local_image_version) + + if [ "$pre_version" == "$post_version" ]; then + exit 42 + fi +} + +# Main. +op="pull" +image="" +while [ $# -gt 0 ]; do + case "$1" in + --check) + op="check" + ;; + -*) + echo "Unknown option $1" >&2 + exit 2 + ;; + *) + image="$1" + ;; + esac + shift +done + +if [ -z "$image" ]; then + echo "Usage: $0 [--check] <image url>" >&2 + exit 2 fi +registry_hostname="${image%%/*}" +image_tag="${image##*:}" +image_without_tag="${image%:*}" +image_path="${image_without_tag#*/}" + +case "$op" in + pull) + pull + ;; + check) + check + ;; +esac + exit 0