From 7b86833518640a95c92ab6396d691093619af49a Mon Sep 17 00:00:00 2001 From: Valentin Lab Date: Sat, 8 Jan 2022 11:39:26 +0100 Subject: [PATCH] chg: [odoo-tecnativa] rewrite ``load`` and ``save`` to use stdin and stdout, add ``drop`` action. The previous implementation had several shortcoming about file due to ``compose`` being run in a docker container. By shifting to ``stdin`` and ``stdout`` usage, we make a more elegant proposition. See ``--help`` to get full doc with examples of usage. Signed-off-by: Valentin Lab --- odoo-tecnativa/actions/drop | 142 ++++++++++++++++++++++++++++++++++++ odoo-tecnativa/actions/load | 142 +++++++++++++++++++++++------------- odoo-tecnativa/actions/save | 131 ++++++++++++++++++++------------- 3 files changed, 312 insertions(+), 103 deletions(-) create mode 100755 odoo-tecnativa/actions/drop diff --git a/odoo-tecnativa/actions/drop b/odoo-tecnativa/actions/drop new file mode 100755 index 0000000..28c802c --- /dev/null +++ b/odoo-tecnativa/actions/drop @@ -0,0 +1,142 @@ +#!/bin/bash + +if [ -z "$SERVICE_DATASTORE" ]; then + echo "This script is meant to be run through 'compose' to work properly." >&2 + exit 1 +fi + + +version=0.1 +usage="$exname [-h|--help] [--force|-f] [DBNAME]" +help=" +USAGE: + +$usage + +DESCRIPTION: + +Drop odoo database DBNAME. If DBNAME is not provided, it'll take the +default odoo database from the ${DARKCYAN}postgres-database${NORMAL} relation of +current service. + +EXAMPLES: + + $exname + $exname odoo2 + +" + + +dbname= +output= +while [ "$1" ]; do + case "$1" in + "--help"|"-h") + print_help >&2 + exit 0 + ;; + "--force"|"-f") + force=yes + ;; + --*|-*) + err "Unexpected optional argument '$1'" + print_usage >&2 + exit 1 + ;; + *) + [ -z "$dbname" ] && { dbname=$1 ; shift ; continue ; } + err "Unexpected positional argument '$1'" + print_usage >&2 + exit 1 + ;; + esac + shift +done + + +if [ -z "$dbname" ]; then + + ## + ## Fetch default dbname in relation to postgres-database + ## + + ## XXXvlab: can't get real config here + if ! read-0 ts _ _ < <(get_service_relation "$SERVICE_NAME" "postgres-database"); then + err "Couldn't find relation ${DARKCYAN}postgres-database${NORMAL}." + exit 1 + fi + + relation_file=$(get_relation_data_dir "$SERVICE_NAME" "$ts" "postgres-database") || { + err "Failed to find relation file" + exit 1 + } + + postgres_config=$(cat "$relation_file"/data) || exit 2 + + dbname="$(e "$postgres_config" | shyaml get-value dbname)" || { + err "Couldn't retrieve information of ${DARKCYAN}mysql-database${NORMAL}'s relation." + exit 1 + } +fi + +set -e + +## Ensure odoo is launched +service_def=$(get_compose_service_def "$SERVICE_NAME") + +## XXXvlab: should be moved to lib +CONFIG=$SERVICE_CONFIGSTORE/etc/odoo-server.conf +ADMIN_PASSWORD=$(echo "$service_def" | shyaml -q get-value options.admin-password) || { + if [ -e "$CONFIG" ]; then + ADMIN_PASSWORD=$(grep ^admin_passwd "$CONFIG" | sed -r 's/^admin_passwd\s+=\s+(.+)$/\1/g') + fi + if [ -z "$ADMIN_PASSWORD" ]; then + err "Could not find 'admin-password' in $SERVICE_NAME service definition nor in config file." + exit 1 + fi +} + +containers="$(get_running_containers_for_service "$SERVICE_NAME")" + +if [ -z "$containers" ]; then + err "No containers running for service $DARKYELLOW$SERVICE_NAME$NORMAL." + die "Please ensure that $DARKYELLOW$SERVICE_NAME$NORMAL is running before using '$exname'." +fi + +## XXXvlab: taking first container is probably not a good idea +container="$(echo "$containers" | head -n 1)" + +## XXXvlab: taking first ip is probably not a good idea +read-0 container_network container_ip < <(get_container_network_ip "$container") + +DEFAULT_CURL_IMAGE=${DEFAULT_CURL_IMAGE:-docker.0k.io/curl} + + +cmd=( + docker run -i --rm --network "$container_network" + "$DEFAULT_CURL_IMAGE" + -sS + -X POST + -F "master_pwd=${ADMIN_PASSWORD}" + -F "name=${dbname}" + http://${container_ip}:8069/web/database/drop +) +## XXXvlab: contains password, left only for advanced debug +#debug "${cmd[@]}" + +out=$("${cmd[@]}") || { + die "Posting to odoo drop API through curl was unsuccessfull." +} + +if [[ "$out" == *""* ]]; then + errmsg=$(echo "$out" | grep "alert-danger") + errmsg=${errmsg#*>} + errmsg=${errmsg%%<*} + if [ "$errmsg" ]; then + errmsg=$(echo "$errmsg" | recode html..utf8) + die "$errmsg" + fi + die "Unexpected output. Drop probably failed." +fi >&2 + +info "Dropped odoo database '$dbname'." diff --git a/odoo-tecnativa/actions/load b/odoo-tecnativa/actions/load index 437ac7e..ef6120a 100755 --- a/odoo-tecnativa/actions/load +++ b/odoo-tecnativa/actions/load @@ -1,66 +1,84 @@ #!/bin/bash -## Load action gets a first argument a DIRECTORY holding the necessary files. -## -## - if [ -z "$SERVICE_DATASTORE" ]; then echo "This script is meant to be run through 'compose' to work properly." >&2 exit 1 fi -usage="$exname [-h|--help] SRC_FILE DBNAME" +version=0.1 +usage="$exname [-h|--help] [--force|-f] [DBNAME]" +help=" +USAGE: + +$usage + +DESCRIPTION: + +Read stdin and send it to the restore API of odoo service to restore +in the database DBNAME. If DBNAME is not provided, it'll take the +default odoo database from the ${DARKCYAN}postgres-database${NORMAL} relation of +current service. + +EXAMPLES: + + $exname < dump.zip + $exname odoo2 < odoo2.zip + +" + dbname= -source= +output= while [ "$1" ]; do case "$1" in "--help"|"-h") - print_usage + print_help >&2 exit 0 ;; + "--force"|"-f") + force=yes + ;; --*|-*) err "Unexpected optional argument '$1'" - print_usage + print_usage >&2 exit 1 ;; *) - [ -z "$source" ] && { source=$1 ; shift ; continue ; } [ -z "$dbname" ] && { dbname=$1 ; shift ; continue ; } err "Unexpected positional argument '$1'" - print_usage + print_usage >&2 exit 1 ;; esac shift done -if [ -z "$source" ]; then - err "You must provide a source filename name as first argument." - print_usage - exit 1 -fi if [ -z "$dbname" ]; then - err "You must provide a database name as second argument." - print_usage - exit 1 -fi -if ! [ -e "$source" ]; then - err "File '$source' not found. Please provide an existing file as first argument." - print_usage - exit 1 -fi + ## + ## Fetch default dbname in relation to postgres-database + ## -realpath=$(realpath "$source") || exit 1 -dirname="$(dirname "$realpath")" || exit 1 -basename=$(basename "$realpath") || exit 1 -host_path="$(get_host_path "$dirname")" || { - die "Failed to find host path for local directory: $dirname" -} + ## XXXvlab: can't get real config here + if ! read-0 ts _ _ < <(get_service_relation "$SERVICE_NAME" "postgres-database"); then + err "Couldn't find relation ${DARKCYAN}postgres-database${NORMAL}." + exit 1 + fi + + relation_file=$(get_relation_data_dir "$SERVICE_NAME" "$ts" "postgres-database") || { + err "Failed to find relation file" + exit 1 + } + + postgres_config=$(cat "$relation_file"/data) || exit 2 + dbname="$(e "$postgres_config" | shyaml get-value dbname)" || { + err "Couldn't retrieve information of ${DARKCYAN}mysql-database${NORMAL}'s relation." + exit 1 + } +fi set -e @@ -79,7 +97,6 @@ ADMIN_PASSWORD=$(echo "$service_def" | shyaml -q get-value options.admin-passwor fi } - containers="$(get_running_containers_for_service "$SERVICE_NAME")" if [ -z "$containers" ]; then @@ -95,25 +112,48 @@ read-0 container_network container_ip < <(get_container_network_ip "$container") DEFAULT_CURL_IMAGE=${DEFAULT_CURL_IMAGE:-docker.0k.io/curl} -debug docker run --rm --network "$container_network" \ - "-v" "$host_path:/tmp/work" \ - "$DEFAULT_CURL_IMAGE" \ - -sS \ - -X POST \ - -F "master_pwd=" \ - -F "backup_file=@/tmp/work/${source}" \ - -F "name=${dbname}" \ - http://${container_ip}:8069/web/database/restore -docker run --rm --network "$container_network" \ - "-v" "$host_path:/tmp/work" \ - "$DEFAULT_CURL_IMAGE" \ - -sS \ - -X POST \ - -F "master_pwd=${ADMIN_PASSWORD}" \ - -F "backup_file=@/tmp/work/${basename}" \ - -F "name=${dbname}" \ - http://${container_ip}:8069/web/database/restore >/dev/null || { - die "Querying odoo through curl was unsuccessfull." +check_input() { + local chars + read -n 2 -r chars + if [ "$chars" != "PK" ]; then + err "Unexpected input not matching ZIP signature. Invalid dump." + echo " First chars: '$chars'" + exit 1 + fi + { + echo -n "$chars" + cat + } +} + + +cmd=( + docker run -i --rm --network "$container_network" + "$DEFAULT_CURL_IMAGE" + -sS + -X POST + -F "master_pwd=${ADMIN_PASSWORD}" + -F "backup_file=@-" + -F "name=${dbname}" + http://${container_ip}:8069/web/database/restore +) + +## XXXvlab: contains password, left only for advanced debug +#debug "${cmd[@]}" + +out=$(set -o pipefail; check_input | "${cmd[@]}") || { + die "Posting to odoo restore API through curl was unsuccessfull." } -info "Restored '$source' odoo database and filestore to '$dbname'." \ No newline at end of file +if [[ "$out" == *""* ]]; then + errmsg=$(echo "$out" | grep "alert-danger") + errmsg=${errmsg#*>} + errmsg=${errmsg%%<*} + if [ "$errmsg" ]; then + errmsg=$(echo "$errmsg" | recode html..utf8) + die "$errmsg" + fi + die "Unexpected output. Restore probably failed." +fi >&2 + +info "Restored stdin dump to odoo '$dbname'." \ No newline at end of file diff --git a/odoo-tecnativa/actions/save b/odoo-tecnativa/actions/save index 0b2fc0c..6d2cd24 100755 --- a/odoo-tecnativa/actions/save +++ b/odoo-tecnativa/actions/save @@ -1,23 +1,38 @@ #!/bin/bash -## Load action gets a first argument a DIRECTORY holding the necessary files. -## -## - if [ -z "$SERVICE_DATASTORE" ]; then echo "This script is meant to be run through 'compose' to work properly." >&2 exit 1 fi -usage="$exname [-h|--help] [--force|-f] DBNAME DEST_FILENAME" +version=0.1 +usage="$exname [-h|--help] [--force|-f] [DBNAME]" +help=" +USAGE: + +$usage + +DESCRIPTION: + +Outputs to stdout the odoo zip dump of DBNAME. If DBNAME is not +provided, it'll take the default odoo database from the +${DARKCYAN}postgres-database${NORMAL} relation of current service. + +EXAMPLES: + + $exname > dump.zip + $exname odoo2 > odoo2.zip + +" + dbname= output= while [ "$1" ]; do case "$1" in "--help"|"-h") - print_usage + print_help >&2 exit 0 ;; "--force"|"-f") @@ -25,45 +40,44 @@ while [ "$1" ]; do ;; --*|-*) err "Unexpected optional argument '$1'" - print_usage + print_usage >&2 exit 1 ;; *) [ -z "$dbname" ] && { dbname=$1 ; shift ; continue ; } - [ -z "$output" ] && { output=$1 ; shift ; continue ; } err "Unexpected positional argument '$1'" - print_usage + print_usage >&2 exit 1 ;; esac shift done + if [ -z "$dbname" ]; then - err "You must provide a database name as first argument." - print_usage - exit 1 -fi -if [ -z "$output" ]; then - err "You must provide a destination filename name as second argument." - print_usage - exit 1 -fi + ## + ## Fetch default dbname in relation to postgres-database + ## + ## XXXvlab: can't get real config here + if ! read-0 ts _ _ < <(get_service_relation "$SERVICE_NAME" "postgres-database"); then + err "Couldn't find relation ${DARKCYAN}postgres-database${NORMAL}." + exit 1 + fi -if [ -e "$output" -a -z "$force" ]; then - err "File '$output' exists already. Force overwrite with -f or --force." - print_usage - exit 1 -fi + relation_file=$(get_relation_data_dir "$SERVICE_NAME" "$ts" "postgres-database") || { + err "Failed to find relation file" + exit 1 + } -realpath=$(realpath "$output") || exit 1 -dirname="$(dirname "$realpath")" || exit 1 -basename=$(basename "$realpath") || exit 1 -host_path="$(get_host_path "$dirname")" || { - die "Failed to find host path for local directory: $dirname" -} + postgres_config=$(cat "$relation_file"/data) || exit 2 + + dbname="$(e "$postgres_config" | shyaml get-value dbname)" || { + err "Couldn't retrieve information of ${DARKCYAN}mysql-database${NORMAL}'s relation." + exit 1 + } +fi set -e @@ -82,7 +96,6 @@ ADMIN_PASSWORD=$(echo "$service_def" | shyaml -q get-value options.admin-passwor fi } - containers="$(get_running_containers_for_service "$SERVICE_NAME")" if [ -z "$containers" ]; then @@ -99,28 +112,42 @@ read-0 container_network container_ip < <(get_container_network_ip "$container") DEFAULT_CURL_IMAGE=${DEFAULT_CURL_IMAGE:-docker.0k.io/curl} -debug docker run --rm --network "$container_network" \ - "-v" "$host_path:/tmp/work" \ - "$DEFAULT_CURL_IMAGE" \ - -X POST \ - -F "master_pwd=" \ - -F "name=${dbname}" \ - -F "backup_format=zip" \ - -o "/tmp/work/$basename" \ - http://${container_ip}:8069/web/database/backup - -docker run --rm --network "$container_network" \ - "-v" "$host_path:/tmp/work" \ - "$DEFAULT_CURL_IMAGE" \ - -sS \ - -X POST \ - -F "master_pwd=${ADMIN_PASSWORD}" \ - -F "name=${dbname}" \ - -F "backup_format=zip" \ - -o "/tmp/work/$basename" \ - http://${container_ip}:8069/web/database/backup || { - die "Querying odoo through curl was unsuccessfull." +check_output() { + local chars + read -n 2 -r chars + if [ "$chars" != "PK" ]; then + out=$(cat) + errmsg=$(echo "$out" | grep "alert-danger") + errmsg=${errmsg#*>} + errmsg=${errmsg%%<*} + if [ -n "$errmsg" ]; then + errmsg=$(echo "$errmsg" | recode html..utf8) + die "$errmsg" + fi + err "Unexpected output not matching ZIP signature. Dump probably failed." + exit 1 + fi + { + echo -n "$chars" + cat + } } -info "Saved '$dbname' odoo database and filestore to '$output'." +cmd=( + docker run --rm --network "$container_network" + "$DEFAULT_CURL_IMAGE" + -sS + -X POST + -F "master_pwd=${ADMIN_PASSWORD}" + -F "name=${dbname}" + -F "backup_format=zip" + http://${container_ip}:8069/web/database/backup +) + +## XXXvlab: contains password, left only for advanced debug +#debug "${cmd[@]}" + +set -o pipefail +"${cmd[@]}" | check_output && + info "Requested odoo '$dbname' dump and outputted result to stdout."