96 lines
3.0 KiB
Bash
Executable File
96 lines
3.0 KiB
Bash
Executable File
#!/bin/sh
|
|
set -eu
|
|
|
|
usage() {
|
|
echo "usage: $0 --project-name NAME --input FILE --confirm-project NAME" >&2
|
|
exit 2
|
|
}
|
|
|
|
project_name=
|
|
input=
|
|
confirm_project=
|
|
while [ "$#" -gt 0 ]; do
|
|
case "$1" in
|
|
--project-name) project_name=${2-}; shift 2 ;;
|
|
--input) input=${2-}; shift 2 ;;
|
|
--confirm-project) confirm_project=${2-}; shift 2 ;;
|
|
*) usage ;;
|
|
esac
|
|
done
|
|
|
|
[ -n "$project_name" ] && [ -n "$input" ] && [ -n "$confirm_project" ] || usage
|
|
[ "$confirm_project" = "$project_name" ] || {
|
|
echo "restore confirmation must match --project-name exactly" >&2
|
|
exit 2
|
|
}
|
|
[ -r "$input" ] || { echo "backup input is not readable" >&2; exit 2; }
|
|
|
|
resolve_volume() {
|
|
names=$(docker volume ls \
|
|
--filter "label=com.docker.compose.project=$project_name" \
|
|
--filter "label=com.docker.compose.volume=qdrant-data" \
|
|
--format '{{.Name}}')
|
|
count=$(printf '%s\n' "$names" | sed '/^$/d' | wc -l | tr -d ' ')
|
|
[ "$count" -eq 1 ] || {
|
|
echo "expected exactly one qdrant-data volume for compose project $project_name" >&2
|
|
exit 2
|
|
}
|
|
printf '%s\n' "$names" | sed -n '/./{p;q;}'
|
|
}
|
|
|
|
resolve_mountpoint() {
|
|
mountpoint=$(docker volume inspect --format '{{ .Mountpoint }}' "$1")
|
|
[ -n "$mountpoint" ] || { echo "docker did not return a qdrant-data mountpoint" >&2; exit 2; }
|
|
case "$mountpoint" in
|
|
/*) ;;
|
|
*) echo "qdrant-data mountpoint is not absolute: $mountpoint" >&2; exit 2 ;;
|
|
esac
|
|
[ -d "$mountpoint" ] || { echo "qdrant-data mountpoint is not a directory: $mountpoint" >&2; exit 2; }
|
|
printf '%s\n' "$mountpoint"
|
|
}
|
|
|
|
volume_name=$(resolve_volume)
|
|
mountpoint=$(resolve_mountpoint "$volume_name")
|
|
running_container=$(docker compose --project-name "$project_name" ps --status running -q qdrant)
|
|
restart_qdrant=0
|
|
staging_dir=
|
|
extract_dir=
|
|
|
|
cleanup() {
|
|
status=$?
|
|
if [ "$status" -ne 0 ] && [ -n "${staging_dir:-}" ] && [ -d "${staging_dir:-}" ]; then
|
|
find "$mountpoint" -mindepth 1 -maxdepth 1 -exec rm -rf -- {} +
|
|
find "$staging_dir" -mindepth 1 -maxdepth 1 -exec mv {} "$mountpoint"/ \;
|
|
fi
|
|
if [ -n "${staging_dir:-}" ] && [ -d "${staging_dir:-}" ]; then
|
|
rm -rf "$staging_dir"
|
|
fi
|
|
if [ -n "${extract_dir:-}" ] && [ -d "${extract_dir:-}" ]; then
|
|
rm -rf "$extract_dir"
|
|
fi
|
|
if [ "$restart_qdrant" -eq 1 ]; then
|
|
docker compose --project-name "$project_name" start qdrant >/dev/null
|
|
fi
|
|
exit "$status"
|
|
}
|
|
trap cleanup EXIT HUP INT TERM
|
|
|
|
if [ -n "$running_container" ]; then
|
|
docker compose --project-name "$project_name" stop qdrant >/dev/null
|
|
restart_qdrant=1
|
|
fi
|
|
|
|
parent_dir=$(dirname "$mountpoint")
|
|
staging_dir=$(mktemp -d "$parent_dir/.qdrant-restore-staging.XXXXXX")
|
|
extract_dir=$(mktemp -d "${TMPDIR:-/tmp}/qdrant-restore.XXXXXX")
|
|
tar -C "$extract_dir" -xf "$input"
|
|
|
|
find "$mountpoint" -mindepth 1 -maxdepth 1 -exec mv {} "$staging_dir"/ \;
|
|
find "$extract_dir" -mindepth 1 -maxdepth 1 -exec mv {} "$mountpoint"/ \;
|
|
|
|
rm -rf "$staging_dir"
|
|
staging_dir=
|
|
rm -rf "$extract_dir"
|
|
extract_dir=
|
|
echo "Qdrant restore completed into $volume_name for compose project $project_name"
|