|
@@ -24,7 +24,9 @@ usage() {
|
|
dir="$1" # dir for building tar in
|
|
dir="$1" # dir for building tar in
|
|
shift || usage 1 >&2
|
|
shift || usage 1 >&2
|
|
|
|
|
|
-[ $# -gt 0 -a "$dir" ] || usage 2 >&2
|
|
|
|
|
|
+if ! [ $# -gt 0 ] && [ "$dir" ]; then
|
|
|
|
+ usage 2 >&2
|
|
|
|
+fi
|
|
mkdir -p "$dir"
|
|
mkdir -p "$dir"
|
|
|
|
|
|
# hacky workarounds for Bash 3 support (no associative arrays)
|
|
# hacky workarounds for Bash 3 support (no associative arrays)
|
|
@@ -37,7 +39,7 @@ doNotGenerateManifestJson=
|
|
# bash v4 on Windows CI requires CRLF separator
|
|
# bash v4 on Windows CI requires CRLF separator
|
|
newlineIFS=$'\n'
|
|
newlineIFS=$'\n'
|
|
if [ "$(go env GOHOSTOS)" = 'windows' ]; then
|
|
if [ "$(go env GOHOSTOS)" = 'windows' ]; then
|
|
- major=$(echo ${BASH_VERSION%%[^0.9]} | cut -d. -f1)
|
|
|
|
|
|
+ major=$(echo "${BASH_VERSION%%[^0.9]}" | cut -d. -f1)
|
|
if [ "$major" -ge 4 ]; then
|
|
if [ "$major" -ge 4 ]; then
|
|
newlineIFS=$'\r\n'
|
|
newlineIFS=$'\r\n'
|
|
fi
|
|
fi
|
|
@@ -59,7 +61,8 @@ fetch_blob() {
|
|
shift
|
|
shift
|
|
local curlArgs=("$@")
|
|
local curlArgs=("$@")
|
|
|
|
|
|
- local curlHeaders="$(
|
|
|
|
|
|
+ local curlHeaders
|
|
|
|
+ curlHeaders="$(
|
|
curl -S "${curlArgs[@]}" \
|
|
curl -S "${curlArgs[@]}" \
|
|
-H "Authorization: Bearer $token" \
|
|
-H "Authorization: Bearer $token" \
|
|
"$registryBase/v2/$image/blobs/$digest" \
|
|
"$registryBase/v2/$image/blobs/$digest" \
|
|
@@ -70,7 +73,8 @@ fetch_blob() {
|
|
if grep -qE "^HTTP/[0-9].[0-9] 3" <<< "$curlHeaders"; then
|
|
if grep -qE "^HTTP/[0-9].[0-9] 3" <<< "$curlHeaders"; then
|
|
rm -f "$targetFile"
|
|
rm -f "$targetFile"
|
|
|
|
|
|
- local blobRedirect="$(echo "$curlHeaders" | awk -F ': ' 'tolower($1) == "location" { print $2; exit }')"
|
|
|
|
|
|
+ local blobRedirect
|
|
|
|
+ blobRedirect="$(echo "$curlHeaders" | awk -F ': ' 'tolower($1) == "location" { print $2; exit }')"
|
|
if [ -z "$blobRedirect" ]; then
|
|
if [ -z "$blobRedirect" ]; then
|
|
echo >&2 "error: failed fetching '$image' blob '$digest'"
|
|
echo >&2 "error: failed fetching '$image' blob '$digest'"
|
|
echo "$curlHeaders" | head -1 >&2
|
|
echo "$curlHeaders" | head -1 >&2
|
|
@@ -88,15 +92,18 @@ handle_single_manifest_v2() {
|
|
local manifestJson="$1"
|
|
local manifestJson="$1"
|
|
shift
|
|
shift
|
|
|
|
|
|
- local configDigest="$(echo "$manifestJson" | jq --raw-output '.config.digest')"
|
|
|
|
|
|
+ local configDigest
|
|
|
|
+ configDigest="$(echo "$manifestJson" | jq --raw-output '.config.digest')"
|
|
local imageId="${configDigest#*:}" # strip off "sha256:"
|
|
local imageId="${configDigest#*:}" # strip off "sha256:"
|
|
|
|
|
|
local configFile="$imageId.json"
|
|
local configFile="$imageId.json"
|
|
fetch_blob "$token" "$image" "$configDigest" "$dir/$configFile" -s
|
|
fetch_blob "$token" "$image" "$configDigest" "$dir/$configFile" -s
|
|
|
|
|
|
- local layersFs="$(echo "$manifestJson" | jq --raw-output --compact-output '.layers[]')"
|
|
|
|
|
|
+ local layersFs
|
|
|
|
+ layersFs="$(echo "$manifestJson" | jq --raw-output --compact-output '.layers[]')"
|
|
local IFS="$newlineIFS"
|
|
local IFS="$newlineIFS"
|
|
- local layers=($layersFs)
|
|
|
|
|
|
+ local layers
|
|
|
|
+ mapfile -t layers <<< "$layersFs"
|
|
unset IFS
|
|
unset IFS
|
|
|
|
|
|
echo "Downloading '$imageIdentifier' (${#layers[@]} layers)..."
|
|
echo "Downloading '$imageIdentifier' (${#layers[@]} layers)..."
|
|
@@ -105,8 +112,10 @@ handle_single_manifest_v2() {
|
|
for i in "${!layers[@]}"; do
|
|
for i in "${!layers[@]}"; do
|
|
local layerMeta="${layers[$i]}"
|
|
local layerMeta="${layers[$i]}"
|
|
|
|
|
|
- local layerMediaType="$(echo "$layerMeta" | jq --raw-output '.mediaType')"
|
|
|
|
- local layerDigest="$(echo "$layerMeta" | jq --raw-output '.digest')"
|
|
|
|
|
|
+ local layerMediaType
|
|
|
|
+ layerMediaType="$(echo "$layerMeta" | jq --raw-output '.mediaType')"
|
|
|
|
+ local layerDigest
|
|
|
|
+ layerDigest="$(echo "$layerMeta" | jq --raw-output '.digest')"
|
|
|
|
|
|
# save the previous layer's ID
|
|
# save the previous layer's ID
|
|
local parentId="$layerId"
|
|
local parentId="$layerId"
|
|
@@ -118,8 +127,10 @@ handle_single_manifest_v2() {
|
|
echo '1.0' > "$dir/$layerId/VERSION"
|
|
echo '1.0' > "$dir/$layerId/VERSION"
|
|
|
|
|
|
if [ ! -s "$dir/$layerId/json" ]; then
|
|
if [ ! -s "$dir/$layerId/json" ]; then
|
|
- local parentJson="$(printf ', parent: "%s"' "$parentId")"
|
|
|
|
- local addJson="$(printf '{ id: "%s"%s }' "$layerId" "${parentId:+$parentJson}")"
|
|
|
|
|
|
+ local parentJson
|
|
|
|
+ parentJson="$(printf ', parent: "%s"' "$parentId")"
|
|
|
|
+ local addJson
|
|
|
|
+ addJson="$(printf '{ id: "%s"%s }' "$layerId" "${parentId:+$parentJson}")"
|
|
# this starter JSON is taken directly from Docker's own "docker save" output for unimportant layers
|
|
# this starter JSON is taken directly from Docker's own "docker save" output for unimportant layers
|
|
jq "$addJson + ." > "$dir/$layerId/json" <<- 'EOJSON'
|
|
jq "$addJson + ." > "$dir/$layerId/json" <<- 'EOJSON'
|
|
{
|
|
{
|
|
@@ -159,7 +170,8 @@ handle_single_manifest_v2() {
|
|
echo "skipping existing ${layerId:0:12}"
|
|
echo "skipping existing ${layerId:0:12}"
|
|
continue
|
|
continue
|
|
fi
|
|
fi
|
|
- local token="$(curl -fsSL "$authBase/token?service=$authService&scope=repository:$image:pull" | jq --raw-output '.token')"
|
|
|
|
|
|
+ local token
|
|
|
|
+ token="$(curl -fsSL "$authBase/token?service=$authService&scope=repository:$image:pull" | jq --raw-output '.token')"
|
|
fetch_blob "$token" "$image" "$layerDigest" "$dir/$layerTar" --progress-bar
|
|
fetch_blob "$token" "$image" "$layerDigest" "$dir/$layerTar" --progress-bar
|
|
;;
|
|
;;
|
|
|
|
|
|
@@ -174,10 +186,12 @@ handle_single_manifest_v2() {
|
|
imageId="$layerId"
|
|
imageId="$layerId"
|
|
|
|
|
|
# munge the top layer image manifest to have the appropriate image configuration for older daemons
|
|
# munge the top layer image manifest to have the appropriate image configuration for older daemons
|
|
- local imageOldConfig="$(jq --raw-output --compact-output '{ id: .id } + if .parent then { parent: .parent } else {} end' "$dir/$imageId/json")"
|
|
|
|
|
|
+ local imageOldConfig
|
|
|
|
+ imageOldConfig="$(jq --raw-output --compact-output '{ id: .id } + if .parent then { parent: .parent } else {} end' "$dir/$imageId/json")"
|
|
jq --raw-output "$imageOldConfig + del(.history, .rootfs)" "$dir/$configFile" > "$dir/$imageId/json"
|
|
jq --raw-output "$imageOldConfig + del(.history, .rootfs)" "$dir/$configFile" > "$dir/$imageId/json"
|
|
|
|
|
|
- local manifestJsonEntry="$(
|
|
|
|
|
|
+ local manifestJsonEntry
|
|
|
|
+ manifestJsonEntry="$(
|
|
echo '{}' | jq --raw-output '. + {
|
|
echo '{}' | jq --raw-output '. + {
|
|
Config: "'"$configFile"'",
|
|
Config: "'"$configFile"'",
|
|
RepoTags: ["'"${image#library\/}:$tag"'"],
|
|
RepoTags: ["'"${image#library\/}:$tag"'"],
|
|
@@ -232,7 +246,7 @@ while [ $# -gt 0 ]; do
|
|
application/vnd.docker.distribution.manifest.list.v2+json)
|
|
application/vnd.docker.distribution.manifest.list.v2+json)
|
|
layersFs="$(echo "$manifestJson" | jq --raw-output --compact-output '.manifests[]')"
|
|
layersFs="$(echo "$manifestJson" | jq --raw-output --compact-output '.manifests[]')"
|
|
IFS="$newlineIFS"
|
|
IFS="$newlineIFS"
|
|
- layers=($layersFs)
|
|
|
|
|
|
+ mapfile -t layers <<< "$layersFs"
|
|
unset IFS
|
|
unset IFS
|
|
|
|
|
|
found=""
|
|
found=""
|
|
@@ -278,7 +292,7 @@ while [ $# -gt 0 ]; do
|
|
|
|
|
|
layersFs="$(echo "$manifestJson" | jq --raw-output '.fsLayers | .[] | .blobSum')"
|
|
layersFs="$(echo "$manifestJson" | jq --raw-output '.fsLayers | .[] | .blobSum')"
|
|
IFS="$newlineIFS"
|
|
IFS="$newlineIFS"
|
|
- layers=($layersFs)
|
|
|
|
|
|
+ mapfile -t layers <<< "$layersFs"
|
|
unset IFS
|
|
unset IFS
|
|
|
|
|
|
history="$(echo "$manifestJson" | jq '.history | [.[] | .v1Compatibility]')"
|
|
history="$(echo "$manifestJson" | jq '.history | [.[] | .v1Compatibility]')"
|