#!/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} } # Let -i option default to number of CPU cores. JOBS=$(getconf _NPROCESSORS_ONLN) 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 db_exec 'GRANT USAGE ON SCHEMA public TO funnel;' photowall db_exec 'GRANT USAGE ON SCHEMA public TO datastudio;' photowall 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_collections v_funnel_discounts v_funnel_order_rows v_funnel_orders v_funnel_pricelist EOF ) do db_exec "GRANT SELECT ON ${x} TO funnel;" photowall done for x in $(cat <<-EOF v_esales_categorytree_i18n v_esales_categorytree_i18n_v2 v_categorytree v_categorytree_v2 mv_category_locale_safexmlpath mv_category_locale_safexmlpath_v2 EOF ) do db_exec "ALTER MATERIALIZED VIEW ${x} OWNER TO photowall;" photowall done fi } 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"