This commit is contained in:
parent
f818ecc2bc
commit
044f63a10a
@ -2,62 +2,143 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
verbose=1
|
function docker_cli_plugin_metadata {
|
||||||
image="$1"
|
local vendor="infogulch"
|
||||||
export searchpath="$2"
|
local version="v0.0.1"
|
||||||
export tarfile="$(mktemp)-$image.tar"
|
local description="Manage Artifacts in Docker Images"
|
||||||
|
local url="https://github.com/infogulch/docker-artifact"
|
||||||
# check to see if image exists locally
|
printf '{"SchemaVersion":"0.1.0","Vendor":"%s","Version":"%s","ShortDescription":"%s","URL":"%s"}\n' \
|
||||||
if ! docker image inspect "$image" > /dev/null ; then
|
"$vendor" "$version" "$description" "$url"
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Save image tar to temp file
|
|
||||||
[ $verbose ] && >&2 echo "Exporting image '$image' to temp file '$tarfile'..."
|
|
||||||
docker image save "$image" -o "$tarfile"
|
|
||||||
|
|
||||||
# Set up cleanup to not leave image behind
|
|
||||||
function onexit() {
|
|
||||||
[ $verbose ] && >&2 echo "Cleaning up exported image '$tarfile'"
|
|
||||||
rm "$tarfile"
|
|
||||||
}
|
}
|
||||||
trap onexit EXIT
|
|
||||||
|
|
||||||
# Extract manifest and config file contents
|
__usage="
|
||||||
[ $verbose ] && >&2 echo "Collecting metadata from image..."
|
Usage: docker artifact [command] [options]
|
||||||
manifest=$(tar -xf "$tarfile" -x manifest.json -O | jq)
|
|
||||||
config_file=$(echo "$manifest" | jq -r '.[0].Config')
|
|
||||||
config=$(tar -f "$tarfile" -x "$config_file" -O | jq)
|
|
||||||
|
|
||||||
# Combine manifest.json and config json to build a map from tar layer directory to layer id sha
|
Commands:
|
||||||
export idmap=$(echo "$manifest" "$config" | jq -sr '[ [ .[0][0].Layers, .[1].rootfs.diff_ids ] | transpose[] | { (.[0]): .[1] } ] | reduce .[] as $x ({}; . * $x)')
|
|
||||||
|
|
||||||
# Search each layer for a file matching $searchpath
|
ls - List files available to download directly from an image
|
||||||
# TODO also search for related whiteout files that start with `.wh.`
|
download - Download files directly from an image that has been labeled
|
||||||
[ $verbose ] && >&2 echo "Searching layers for '$searchpath'..."
|
label - Label an image
|
||||||
found=$(echo "$manifest" | jq '.[0].Layers[]' | xargs -I {} sh -c 'digest=$(echo "$idmap" | jq -r ".[\"{}\"]"); tar -f "$tarfile" -x {} -O | tar -t | sed s_^_/_ | grep -wx "$searchpath" | xargs -I [] echo "[]=$digest"')
|
|
||||||
|
|
||||||
foundcount=$(echo "$found" | grep -c . || true)
|
Command usage:
|
||||||
|
|
||||||
# If more than one is found, then bail
|
docker artifact ls [options] image_name
|
||||||
if [ $foundcount -gt 1 ]; then
|
docker artifact download [options] image_name file...
|
||||||
>&2 echo "File was changed in multiple layers, aborting. Found files and layer ids:"
|
docker artifact label [options] image_name file...
|
||||||
>&2 echo "$found"
|
|
||||||
exit 2
|
|
||||||
elif [ $foundcount -eq 0 ]; then
|
|
||||||
>&2 echo "No files found matching '$searchpath'"
|
|
||||||
exit 3
|
|
||||||
fi
|
|
||||||
|
|
||||||
labels=$(echo "$found" | sed 's_^.*$_--label "\0"_' | paste -d' ')
|
Options:
|
||||||
|
-v - verbose output
|
||||||
|
-q - quiet output
|
||||||
|
|
||||||
# Add a layer to the existing image to add the labels and tag the new image with the same image name
|
Examples:
|
||||||
[ $verbose ] && >&2 echo "Rebuilding image and adding labels: $labels"
|
|
||||||
echo "FROM $image" | eval docker build $labels -t "$image" - &> /dev/null
|
|
||||||
|
|
||||||
echo "Rebuilt image '$image' with the following added labels:"
|
docker artifact ls infogulch/artifact-test
|
||||||
docker image inspect "$image" | jq '.[0].Config.Labels'
|
docker artifact download infogulch/artifact-test /app/othertestfile.txt
|
||||||
echo "Run 'docker push $image' to push it to docker hub"
|
docker artifact label infogulch/artifact-test /testfile.txt
|
||||||
|
"
|
||||||
|
|
||||||
exit
|
function main {
|
||||||
|
case "$1" in
|
||||||
|
docker-cli-plugin-metadata)
|
||||||
|
docker_cli_plugin_metadata
|
||||||
|
;;
|
||||||
|
artifact)
|
||||||
|
case "$2" in
|
||||||
|
ls)
|
||||||
|
list "${@:3}"
|
||||||
|
;;
|
||||||
|
label)
|
||||||
|
label "${@:3}"
|
||||||
|
;;
|
||||||
|
download)
|
||||||
|
download "${@:3}"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "$__usage"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
function _search_layer {
|
||||||
|
local idmap="$1"
|
||||||
|
local imagetar="$2"
|
||||||
|
local layertar="$3"
|
||||||
|
local search="$4"
|
||||||
|
# look up digest associated with layer path
|
||||||
|
local digest="$(jq --arg key "$layertar" -r '.[$key]' <<< "$idmap")"
|
||||||
|
# extract layer from image | list files in layer | add / prefix | search for file | append =$digest to each found file
|
||||||
|
tar -f "$imagetar" -x "$layertar" -O | tar -t | sed s_^_/_ | grep -wx "$search" | sed 's_.$_\0='"$digest"'_'
|
||||||
|
}
|
||||||
|
|
||||||
|
function label {
|
||||||
|
local image="$1"
|
||||||
|
local searchpath="$2"
|
||||||
|
local tarfile="$(mktemp).tar"
|
||||||
|
|
||||||
|
# check to see if image exists locally. If not, Docker already prints an error so just exit
|
||||||
|
if ! docker image inspect "$image" > /dev/null ; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Save image tar to temp file
|
||||||
|
LOG "Exporting image '$image' to temp file '$tarfile'..."
|
||||||
|
docker image save "$image" -o "$tarfile"
|
||||||
|
|
||||||
|
# Set up cleanup to not leave image behind
|
||||||
|
trap "delete_files '$tarfile'; trap - RETURN" RETURN
|
||||||
|
|
||||||
|
# Extract manifest and config file contents
|
||||||
|
LOG "Collecting metadata from image..."
|
||||||
|
local manifest="$(tar -xf "$tarfile" -x manifest.json -O | jq)"
|
||||||
|
local config_file="$(echo "$manifest" | jq -r '.[0].Config')"
|
||||||
|
local config="$(tar -f "$tarfile" -x "$config_file" -O | jq)"
|
||||||
|
|
||||||
|
# Combine manifest.json and config json to build a map from tar layer directory to layer id sha
|
||||||
|
local idmap="$(echo "$manifest" "$config" | jq -s '[ [ .[0][0].Layers, .[1].rootfs.diff_ids ] | transpose[] | { (.[0]): .[1] } ] | reduce .[] as $x ({}; . * $x)')"
|
||||||
|
|
||||||
|
# Search each layer for a file matching $searchpath
|
||||||
|
# TODO also search for related whiteout files that start with `.wh.`
|
||||||
|
LOG "Searching layers for '$searchpath'..."
|
||||||
|
local found="$(echo "$manifest" | jq -r '.[0].Layers[]' | xargs -L1 -I {} bash -c '_search_layer "$@"' _ "$idmap" "$tarfile" {} "$searchpath")"
|
||||||
|
local foundcount="$(echo "$found" | grep -c . || true)"
|
||||||
|
|
||||||
|
# If more than one is found, then bail
|
||||||
|
if [ $foundcount -gt 1 ]; then
|
||||||
|
echo "File was changed in multiple layers, aborting. Found files and layer ids:"
|
||||||
|
echo "$found"
|
||||||
|
exit 2
|
||||||
|
elif [ $foundcount -eq 0 ]; then
|
||||||
|
echo "No files found matching '$searchpath'"
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
|
||||||
|
local labels="$(echo "$found" | sed 's_^.*$_--label "\0"_' | paste -d' ')"
|
||||||
|
|
||||||
|
# Add a layer to the existing image to add the labels and tag the new image with the same image name
|
||||||
|
LOG "Rebuilding image and adding labels: $labels"
|
||||||
|
echo "FROM $image" | eval docker build $labels -t "$image" - &> /dev/null
|
||||||
|
LOG "All image labels:"$'\n'"$(docker image inspect "$image" | jq '.[0].Config.Labels' | sed 's_^_ _' )"
|
||||||
|
|
||||||
|
# Remind user to push image
|
||||||
|
echo " [*] Rebuilt image '$image' to add $foundcount labels"
|
||||||
|
echo " [*] Run 'docker push $image' to push it to your container repository"
|
||||||
|
}
|
||||||
|
|
||||||
|
function delete_files {
|
||||||
|
LOG "Cleaning up temp files $@"
|
||||||
|
rm "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
export verbose=1
|
||||||
|
function LOG {
|
||||||
|
[ $verbose ] && >&2 echo -e "$(tput setaf 4) ==> $@$(tput sgr0)"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Export functions used in subshells
|
||||||
|
export -f LOG
|
||||||
|
export -f _search_layer
|
||||||
|
|
||||||
|
main "$@"
|
||||||
|
|
||||||
|
@ -5,11 +5,12 @@ source=$(dirname "${BASH_SOURCE[0]}")
|
|||||||
pushd "$source" &> /dev/null
|
pushd "$source" &> /dev/null
|
||||||
|
|
||||||
# Build example image
|
# Build example image
|
||||||
docker build . -t test-image &> /dev/null
|
echo "Rebuilding docker image for example"
|
||||||
|
docker build . -t infogulch/test-image &> /dev/null
|
||||||
|
|
||||||
# Execute docker artifact to add a label to /app/othertestfile.txt
|
# Execute docker artifact to add a label to /app/othertestfile.txt
|
||||||
set +e
|
set +e
|
||||||
../docker-artifact.sh test-image /app/othertestfile.txt
|
../docker-artifact.sh artifact label infogulch/test-image /app/othertestfile.txt
|
||||||
|
|
||||||
popd &> /dev/null
|
popd &> /dev/null
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user