Skip to content

Instantly share code, notes, and snippets.

@vaab
Last active November 18, 2022 12:16
Show Gist options
  • Save vaab/89d710f452b1d4fc7912 to your computer and use it in GitHub Desktop.
Save vaab/89d710f452b1d4fc7912 to your computer and use it in GitHub Desktop.
docker-update
#!/bin/bash
## Updates an existing image with given script coming from stdin.
##
exname=$(basename $0)
usage="$exname DOCKER_IMAGE
Update image with stdin instruction.
"
if [ -z "$1" ]; then
echo "Error: you should specify a parent image as first argument." >&2
echo "usage: $usage"
exit 1
fi
image="$1"
shift
DOCKER_VERSION=$(docker -v | sed -r 's/^[^0-9.]+([0-9.]+).*$/\1/g')
is_cached_code_same() {
local cmd
cmd="$@"
code="$(cat -)"
[ "$1" == "-c" ] || return 1
[ "$2" == "$code" ] || return 1
if echo "$code" | egrep "#\s*docker:\s*ALWAYS\s*" >/dev/null 2>&1; then
return 1
fi
return 0
}
DOCKER_INSPECT_CACHE_FORMAT='{{.Id}}{{"\x00"}}
{{.Parent}}{{"\x00"}}
{{if .Config}}{{range $p := .Config.Cmd}}{{$p}}{{"\x00"}}
{{end}}{{end}}{{"\x00"}}'
DOCKER_INSPECT_CACHE_FORMAT="$(echo "$DOCKER_INSPECT_CACHE_FORMAT" | tr -d "\n")"
export DOCKER_INSPECT_CACHE_FORMAT
##
# check_cache PARENT_IMAGE
#
# Checks if no already stored image has PARENT_IMAGE as parent and
# same code. If it founds such an image, it'll output its id in stdout
# and return 0 errlvl. Otherwise it'll return 1 errlvl.
check_cache() {
local image
image="$1"
shift
code="$(cat -)"
image_id=$(get_image_id "$image")
while read-0 id parent; do
cmd=()
while true; do
read-0 elt
if [ "$elt" ]; then
cmd=("${cmd[@]}" "$elt")
else
break
fi
done
if [ "$parent" != "$image_id" ]; then
continue
fi
if echo "$code" | is_cached_code_same "${cmd[@]}"; then
## not used "$id" as there is a spurious \n that is inserted by --format
echo $id
return 0
fi
done < <(docker inspect --format "$DOCKER_INSPECT_CACHE_FORMAT" $(docker images -qa))
return 1
}
docker_update() {
local image
image="$1"
shift
code=$(cat -)
## Saving CMD and ENTRYPOINT
save_cmd=$(docker inspect --format='{{json .Config.Cmd}}' "$image")
save_entrypoint=$(docker inspect --format='{{json .Config.Entrypoint}}' "$image")
## Checking cache in case already built.
if cached=$(echo "$code" | check_cache "$image"); then
echo "cached"
docker tag "$cached" "$image"
else
## Running code in container
container_id=$(docker run -d \
--entrypoint /bin/bash \
"$@" "$image" -c \
"$code"
)
if [ "$(docker wait "$container_id")" != "0" ]; then
echo "Update of image "$image" failed ! (container: $container_id)"
echo "=== code:"
echo "$code"
echo "=== logs:"
docker logs "$container_id"
exit 1
fi
docker commit --author "$exname" --message "$exname" \
"$container_id" "$image" >/dev/null &&
docker rm $container_id >/dev/null
echo "updated"
fi
## Final commit to restore CMD and ENTRYPOINT
docker build -t "$image" - <<EOF >/dev/null 2>&1
FROM $image
CMD $save_cmd
ENTRYPOINT $save_entrypoint
EOF
}
read-0() {
while [ "$1" ]; do
IFS=$'\0' read -r -d '' "$1" || return 1
shift
done
}
get_image_id() {
docker inspect --format '{{.Id}}' "$1"
}
docker_update "$image" "$@"
@caioariede
Copy link

Hey, thanks for the scripts. It's very useful to me. :)

I forked it to make some little changes:

  1. replaces sed with grep
  2. make it work with images without entrypoint

https://gist.github.com/caioariede/99dc77101289726754a1

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