diff --git a/README.md b/README.md index 2616004..7b7fbe4 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,11 @@ sudo dokku plugin:install https://github.com/dokku/dokku-postgres.git postgres ## commands ``` +postgres:backup Create a backup of the postgres service to an existing s3 bucket +postgres:backup-auth Sets up authentication for backups on the postgres service +postgres:backup-deauth Removes backup authentication for the postgres service +postgres:backup-schedule Schedules a backup of the postgres service +postgres:backup-unschedule Unschedules the backup of the postgres service postgres:clone Create container then copy data from into postgres:connect Connect via psql to a postgres service postgres:create Create a postgres service with environment variables @@ -208,3 +213,24 @@ custom certificate by overwriting the `server.crt` and `server.key` files in `/var/lib/dokku/services/postgres//data`. The `server.key` must be chmoded to 600 and must be owned by the postgres user or root. + +## Backups + +Backups can be performed using the backup commands: + +``` +# setup s3 backup authentication +dokku postgres:backup-auth lolipop AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY + +# remove s3 authentication +dokku postgres:backup-deauth lolipop + +# backup the `lolipop` service to the `BUCKET_NAME` bucket on AWS +dokku postgres:backup lolipop BUCKET_NAME + +# schedule a backup +dokku postgres:backup-schedule lolipop CRON_SCHEDULE BUCKET_NAME + +# remove the scheduled backup from cron +dokku postgres:backup-unschedule lolipop +``` diff --git a/commands b/commands index 0a232d9..1a07da3 100755 --- a/commands +++ b/commands @@ -11,6 +11,26 @@ if [[ ! -d $PLUGIN_DATA_ROOT ]]; then fi case "$1" in + $PLUGIN_COMMAND_PREFIX:backup) + "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/subcommands/backup" "$@" + ;; + + $PLUGIN_COMMAND_PREFIX:backup-auth) + "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/subcommands/backup-auth" "$@" + ;; + + $PLUGIN_COMMAND_PREFIX:backup-deauth) + "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/subcommands/backup-deauth" "$@" + ;; + + $PLUGIN_COMMAND_PREFIX:backup-schedule) + "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/subcommands/backup-schedule" "$@" + ;; + + $PLUGIN_COMMAND_PREFIX:backup-unschedule) + "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/subcommands/backup-unschedule" "$@" + ;; + $PLUGIN_COMMAND_PREFIX:clone) "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/subcommands/clone" "$@" ;; @@ -84,6 +104,11 @@ case "$1" in # shellcheck disable=SC2034 declare desc="return $PLUGIN_COMMAND_PREFIX plugin help content" cat< , Create a backup of the $PLUGIN_COMMAND_PREFIX service to an existing s3 bucket + $PLUGIN_COMMAND_PREFIX:backup-auth , Sets up authentication for backups on the $PLUGIN_COMMAND_PREFIX service + $PLUGIN_COMMAND_PREFIX:backup-deauth , Removes backup authentication for the $PLUGIN_COMMAND_PREFIX service + $PLUGIN_COMMAND_PREFIX:backup-schedule , Schedules a backup of the $PLUGIN_COMMAND_PREFIX service + $PLUGIN_COMMAND_PREFIX:backup-unschedule , Unschedules the backup of the $PLUGIN_COMMAND_PREFIX service $PLUGIN_COMMAND_PREFIX:clone , Create container then copy data from into $PLUGIN_COMMAND_PREFIX:connect , Connect via psql to a $PLUGIN_SERVICE service $PLUGIN_COMMAND_PREFIX:create , Create a $PLUGIN_SERVICE service diff --git a/common-functions b/common-functions index 08a653f..c19fe76 100755 --- a/common-functions +++ b/common-functions @@ -102,6 +102,68 @@ service_alternative_alias() { echo "$ALIAS" } +service_backup() { + declare desc="Creates a backup of a service to an existing s3 bucket" + declare SERVICE="$1" BUCKET_NAME="$2" + local SERVICE_ROOT="$PLUGIN_DATA_ROOT/$SERVICE" + local AWS_ACCESS_KEY_ID_FILE="$SERVICE_ROOT/backup/AWS_ACCESS_KEY_ID" + local AWS_SECRET_ACCESS_KEY_FILE="$SERVICE_ROOT/backup/AWS_SECRET_ACCESS_KEY" + + [[ ! -f "$AWS_ACCESS_KEY_ID_FILE" ]] && dokku_log_fail "Missing AWS_ACCESS_KEY_ID file" + [[ ! -f "$AWS_SECRET_ACCESS_KEY_FILE" ]] && dokku_log_fail "Missing AWS_SECRET_ACCESS_KEY file" + + TMPDIR=$(mktemp -d) + trap 'rm -rf "$TMP_WORK_DIR" > /dev/null' RETURN INT TERM EXIT + + (service_export "$SERVICE" > "${TMPDIR}/export") + docker run \ + -e AWS_ACCESS_KEY_ID="$(cat "$AWS_ACCESS_KEY_ID_FILE")" \ + -e AWS_SECRET_ACCESS_KEY="$(cat "$AWS_SECRET_ACCESS_KEY_FILE")" \ + -e BUCKET_NAME="$BUCKET_NAME" \ + -e BACKUP_NAME="${PLUGIN_COMMAND_PREFIX}-${SERVICE}" \ + -v "${TMPDIR}:/backup" dokkupaas/s3backup:0.5.0-1 +} + +service_backup_auth() { + declare desc="Sets up authentication" + declare SERVICE="$1" AWS_ACCESS_KEY_ID="$2" AWS_SECRET_ACCESS_KEY="$3" + local SERVICE_ROOT="${PLUGIN_DATA_ROOT}/${SERVICE}" + local SERVICE_BACKUP_ROOT="${SERVICE_ROOT}/backup/" + + mkdir -p "$SERVICE_BACKUP_ROOT" + echo "$AWS_ACCESS_KEY_ID" > "${SERVICE_BACKUP_ROOT}/AWS_ACCESS_KEY_ID" + echo "$AWS_SECRET_ACCESS_KEY" > "${SERVICE_BACKUP_ROOT}/AWS_SECRET_ACCESS_KEY" +} + +service_backup_deauth() { + declare desc="Removes authentication" + declare SERVICE="$1" + local SERVICE_ROOT="${PLUGIN_DATA_ROOT}/${SERVICE}" + local SERVICE_BACKUP_ROOT="${SERVICE_ROOT}/backup/" + + rm -rf "$SERVICE_BACKUP_ROOT" +} + +service_backup_schedule() { + declare desc="schedules a backup of the service" + declare SERVICE="$1" SCHEDULE="$2" BUCKET_NAME="$3" + local DOKKU_BIN="$(which dokku)" + local CRON_FILE="/etc/cron.d/dokku-${PLUGIN_COMMAND_PREFIX}-${SERVICE}" + local TMP_CRON_FILE="${PLUGIN_DATA_ROOT}/.TMP_CRON_FILE" + + echo "${SCHEDULE} dokku ${DOKKU_BIN} ${PLUGIN_COMMAND_PREFIX}:backup ${SERVICE} ${BUCKET_NAME}" > "$TMP_CRON_FILE" + sudo /bin/mv "$TMP_CRON_FILE" "$CRON_FILE" + sudo /bin/chown root:root "$CRON_FILE" +} + +service_backup_unschedule() { + declare desc="unschedules the backup of the service" + declare SERVICE="$1" + local CRON_FILE="/etc/cron.d/dokku-${PLUGIN_COMMAND_PREFIX}-${SERVICE}" + + sudo /bin/rm -f "$CRON_FILE" +} + service_enter() { declare desc="enters running app container of specified proc type" declare SERVICE="$1" && shift 1 @@ -411,6 +473,15 @@ service_version() { docker inspect -f '{{.Config.Image}}' "$SERVICE_NAME" } +update_plugin_scheme_for_app() { + declare desc="Retrieves the updated plugin scheme" + declare APP="$1" + local DATABASE_SCHEME + + DATABASE_SCHEME=$(config_get "$APP" "${PLUGIN_VARIABLE}_DATABASE_SCHEME" || true) + PLUGIN_SCHEME=${DATABASE_SCHEME:-$PLUGIN_SCHEME} +} + verify_service_name() { declare desc="Verifies that a service exists" declare SERVICE="$1" diff --git a/config b/config index 4ff8872..edeb2ac 100644 --- a/config +++ b/config @@ -13,6 +13,7 @@ export PLUGIN_IMAGE=$POSTGRES_IMAGE export PLUGIN_IMAGE_VERSION=$POSTGRES_IMAGE_VERSION export PLUGIN_SCHEME="postgres" export PLUGIN_SERVICE="Postgres" +export PLUGIN_VARIABLE="POSTGRES" export PLUGIN_BASE_PATH="$PLUGIN_PATH" if [[ -n $DOKKU_API_VERSION ]]; then export PLUGIN_BASE_PATH="$PLUGIN_ENABLED_PATH" diff --git a/functions b/functions index 6d592cd..d4b4040 100755 --- a/functions +++ b/functions @@ -127,9 +127,3 @@ service_url() { local SERVICE_ALIAS="$(service_alias "$SERVICE")" echo "$PLUGIN_SCHEME://postgres:$PASSWORD@$SERVICE_ALIAS:${PLUGIN_DATASTORE_PORTS[0]}/$DATABASE_NAME" } - -update_plugin_scheme_for_app() { - local APP="$1" - local POSTGRES_DATABASE_SCHEME=$(config_get "$APP" POSTGRES_DATABASE_SCHEME) - PLUGIN_SCHEME=${POSTGRES_DATABASE_SCHEME:-$PLUGIN_SCHEME} -} diff --git a/install b/install index 7a38c52..b551c51 100755 --- a/install +++ b/install @@ -2,16 +2,36 @@ source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/config" set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x -pull-docker-image() { - declare IMAGE="$1" - if [[ "$(docker images -q "${IMAGE}" 2> /dev/null)" == "" ]]; then - docker pull "${IMAGE}" - fi +plugin-install() { + pull-docker-image() { + declare IMAGE="$1" + if [[ "$(docker images -q "${IMAGE}" 2> /dev/null)" == "" ]]; then + docker pull "${IMAGE}" + fi + } + + pull-docker-image "${PLUGIN_IMAGE}:${PLUGIN_IMAGE_VERSION}" + pull-docker-image "svendowideit/ambassador:latest" + pull-docker-image "dokkupaas/wait:0.2" + pull-docker-image "dokkupaas/s3backup:0.5.0-1" + pull-docker-image "busybox:latest" + + mkdir -p "$PLUGIN_DATA_ROOT" || echo "Failed to create $PLUGIN_SERVICE directory" + chown dokku:dokku "$PLUGIN_DATA_ROOT" + + rm -f "/etc/sudoers.d/dokku-${PLUGIN_COMMAND_PREFIX}*" + _SUDOERS_FILE="/etc/sudoers.d/dokku-${PLUGIN_COMMAND_PREFIX}" + + touch "$_SUDOERS_FILE" + cat > "$_SUDOERS_FILE" <&2 dokku --force "$PLUGIN_COMMAND_PREFIX:destroy" l >&2 - rm "$DOKKU_ROOT/my_app" -rf + rm -rf "$DOKKU_ROOT/my_app" } @test "($PLUGIN_COMMAND_PREFIX:hook:pre-delete) removes app from links file when destroying app" { diff --git a/tests/service_link.bats b/tests/service_link.bats index f0cbab9..131e6c4 100755 --- a/tests/service_link.bats +++ b/tests/service_link.bats @@ -8,7 +8,7 @@ setup() { teardown() { dokku --force "$PLUGIN_COMMAND_PREFIX:destroy" l >&2 - rm "$DOKKU_ROOT/my_app" -rf + rm -rf "$DOKKU_ROOT/my_app" } @test "($PLUGIN_COMMAND_PREFIX:link) error when there are no arguments" { diff --git a/tests/service_promote.bats b/tests/service_promote.bats index b2a01c5..a5ba44a 100755 --- a/tests/service_promote.bats +++ b/tests/service_promote.bats @@ -10,7 +10,7 @@ setup() { teardown() { dokku "$PLUGIN_COMMAND_PREFIX:unlink" l my_app dokku --force "$PLUGIN_COMMAND_PREFIX:destroy" l >&2 - rm "$DOKKU_ROOT/my_app" -rf + rm -rf "$DOKKU_ROOT/my_app" } @test "($PLUGIN_COMMAND_PREFIX:promote) error when there are no arguments" { diff --git a/tests/service_unlink.bats b/tests/service_unlink.bats index a0187b1..704c6e2 100755 --- a/tests/service_unlink.bats +++ b/tests/service_unlink.bats @@ -8,7 +8,7 @@ setup() { teardown() { dokku --force "$PLUGIN_COMMAND_PREFIX:destroy" l >&2 - rm "$DOKKU_ROOT/my_app" -rf + rm -rf "$DOKKU_ROOT/my_app" } @test "($PLUGIN_COMMAND_PREFIX:unlink) error when there are no arguments" { @@ -49,6 +49,6 @@ teardown() { @test "($PLUGIN_COMMAND_PREFIX:unlink) unsets config url from app" { dokku "$PLUGIN_COMMAND_PREFIX:link" l my_app >&2 dokku "$PLUGIN_COMMAND_PREFIX:unlink" l my_app - config=$(dokku config:get my_app DATABASE_URL) + config=$(dokku config:get my_app DATABASE_URL || true) assert_equal "$config" "" } diff --git a/update b/update new file mode 120000 index 0000000..f7ffc47 --- /dev/null +++ b/update @@ -0,0 +1 @@ +install \ No newline at end of file