From c514a581b943ee013b4fc6f20c7f3b7247ff186a Mon Sep 17 00:00:00 2001 From: Rikard Bartholf Date: Mon, 15 May 2023 11:14:35 +0200 Subject: [PATCH] Add pwdb to /bin --- bin/pwdb | 257 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 257 insertions(+) create mode 100755 bin/pwdb diff --git a/bin/pwdb b/bin/pwdb new file mode 100755 index 0000000..e1e5929 --- /dev/null +++ b/bin/pwdb @@ -0,0 +1,257 @@ +#!/bin/bash +set -e + +DATABASE=photowall +DB_PROD=production-cluster.cluster-cfl0zi9wi0ze.eu-west-1.rds.amazonaws.com +DB_STAGE=staging-new-cluster.cluster-cfl0zi9wi0ze.eu-west-1.rds.amazonaws.com +DB_LOCAL=localhost +DUMP_FILE=~/photowall.sql +JOBS=1 + +throw_err() +{ + echo && echo " *** Error: $1" && usage 1 +} + +graceful_exit() +{ + echo && echo "$1" && exit 0 +} + +usage() +{ + echo + cat <<- EOF + Usage: $0 [OPTION]... [COMMAND] [SOURCE] + + Commands: + dump dump database photowall from SOURCE + sync sync database photowall from SOURCE database to TARGET database + + Options: + -d, --dry-run don't actually run any commands, just print them + -h, --help display this help message + -j, --jobs (DEFAULT: 1) number of processes to use when restoring the dump + -p, --production tells if the restored database is production like (stage or prod). + If set, additional grants will be handled +EOF + exit ${1:-0} +} + +db_exec() +{ + echo "psql -h $TARGET -xU root -d ${2:-postgres} -c \"$1\"" + + if [ -z "$DRY_RUN" ] + then + psql -h $TARGET -xU root -d ${2:-postgres} -c "$1" + fi +} + +confirm_sync() +{ + cat <<- EOF + $(tput blink) + !!! WARNING !!!$(tput sgr0)$(tput bold) + DATABASE ${DATABASE} + ON ${TARGET} + WILL BE DROPPED AND REPLACED WITH THE ONE + FROM ${SOURCE}$(tput sgr0)$(tput blink) + !!! WARNING !!!$(tput sgr0) + +EOF + + read -rep "Are you sure this is what you want? [y/N] " -n 1 -r + [[ ! $REPLY =~ ^[Yy]$ ]] && graceful_exit "Quitting..." || true +} + +create_dump() +{ + echo "* Dumping database ${DATABASE} on ${SOURCE} to ${DUMP_FILE}" + echo + echo "pg_dump -h ${SOURCE} -d ${DATABASE} -U root -Fc > ${DUMP_FILE}" + if [ -z "$DRY_RUN" ] + then + pg_dump -h $SOURCE -d $DATABASE -U root -Fc > ${DUMP_FILE} + fi +} + +dump() +{ + ## Check if dump file already exists + if [ -f "$DUMP_FILE" ] + then + FDATA=$(date -r $DUMP_FILE "+%Y-%m-%d %H:%M:%S") + echo + echo "File $DUMP_FILE ($FDATA) already exists." + read -rep "Use that instead? [y/N/q] " -n 1 -r + case "$REPLY" in + [yY]) + echo + echo "* Using existing dump file ($FDATA)" + ;; + [nN]) + echo + create_dump + ;; + *) + graceful_exit "Quitting..." + ;; + esac + else + echo '* Dump file not found' + echo + create_dump + fi +} + +provision() +{ + db_exec " + SELECT pg_terminate_backend(pg_stat_activity.pid) + FROM pg_stat_activity + WHERE pg_stat_activity.datname = 'photowall' + AND pid <> pg_backend_pid();" + + echo "dropdb -h $TARGET -U root --if-exists photowall" + echo "createdb -h $TARGET -U root --encoding=UTF8 photowall" + + if [ -z "$DRY_RUN" ] + then + dropdb -h $TARGET -U root --if-exists photowall + createdb -h $TARGET -U root --encoding=UTF8 photowall + fi + + db_exec 'CREATE EXTENSION IF NOT EXISTS unaccent SCHEMA pg_catalog;' photowall +} + +restore() +{ + provision + echo "pg_restore -h $TARGET -U root --no-owner --no-acl -j $JOBS -d photowall $DUMP_FILE" + if [ -z "$DRY_RUN" ] + then + pg_restore -h $TARGET -U root --no-owner --no-acl -d photowall $DUMP_FILE + fi +} + +set_grants() +{ + db_exec 'REVOKE TEMPORARY ON DATABASE photowall FROM public'; + SQL=$(cat <<- 'EOF' + REVOKE ALL ON SCHEMA public FROM public; + GRANT USAGE ON SCHEMA public TO photowall; + GRANT SELECT, UPDATE, INSERT, DELETE ON ALL TABLES IN SCHEMA public TO photowall; + GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO photowall; + ALTER DEFAULT PRIVILEGES IN SCHEMA public + GRANT SELECT, UPDATE, INSERT, DELETE ON TABLES TO photowall; + ALTER DEFAULT PRIVILEGES IN SCHEMA public + GRANT USAGE, SELECT ON SEQUENCES TO photowall; +EOF +) + db_exec "$SQL" photowall + + if [ ! -z $PRODUCTION ] + then + for x in $(cat <<-EOF + v_analytics_products + v_analytics_inquiries + v_analytics_discounts + v_analytics_product_categories + v_analytics_orders_not_delivered + v_analytics_orders_rows_not_delivered + v_analytics_order_rows + v_analytics_orders2 + v_analytics_contract_customer_discounts +EOF + ) + do + db_exec "GRANT SELECT ON ${x} TO datastudio;" photowall + done + + for x in $(cat <<-EOF + v_funnel_discounts + v_funnel_order_rows + v_funnel_orders +EOF + ) + do + db_exec "GRANT SELECT ON ${x} TO funnel;" photowall + done + fi + + for x in $(cat <<-EOF + v_categorytree + v_esales_category_count + v_esales_categorytree_i18n + v_esales_product_categories +EOF + ) + do + db_exec "ALTER MATERIALIZED VIEW ${x} OWNER TO photowall;" photowall + done +} + +set_source() +{ + SOURCE="$1" + SOURCE_HASH=$(echo "${SOURCE}${DATABASE}" | md5sum | cut -d' ' -f1) + DUMP_FILE="/tmp/${DATABASE}-${SOURCE_HASH:0:10}.sql" +} + +# Handle arguments +PARAMS="" +while (( "$#" )); do + case "$1" in + -d|--dry-run) + DRY_RUN=1 + shift + ;; + -h|--help) + usage + ;; + -j|--jobs) + if [ -n "$2" ] && [ ${2:0:1} != "-" ]; then + JOBS="$2" + shift 2 + else + throw_err "Argument for ${1} is missing" + fi + ;; + -p|--production) + PRODUCTION=1 + shift 1 + ;; + -*|--*=) + throw_err "Unsupported flag ${1}" + ;; + *) + PARAMS="${PARAMS} ${1}" + shift + ;; + esac +done + +# reset positional arguments +eval set -- "${PARAMS}" + +[ -z "$2" ] && throw_err "Source server URL is missing" || set_source "$2" + +case "$1" in + dump) + dump + ;; + sync) + [ -z $3 ] && throw_err "Target server URL is missing" + TARGET="$3" + confirm_sync + dump + restore + set_grants + ;; + *) + throw_err "${1} is not a valid command" + ;; +esac + +[ -z "$1" ] && throw_err "No command given"