diff --git a/README.md b/README.md index 83a4cdd..33939b2 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ postgres:backup-auth ( Removes backup authentication for the postgres service postgres:backup-schedule Schedules a backup of the postgres service postgres:backup-schedule-cat Show the backup schedule for the service -postgres:backup-set-encryption Sets up GPG encryption for future backups of the postgres service +postgres:backup-set-encryption Set a GPG passphrase for backups postgres:backup-unschedule Unschedules the backup of the postgres service postgres:backup-unset-encryption Removes backup encryption for future backups of the postgres service postgres:clone Create container then copy data from into @@ -46,6 +46,7 @@ postgres:start Start a previously stopped postgres service postgres:stop Stop a running postgres service postgres:unexpose Unexpose a previously exposed postgres service postgres:unlink Unlink the postgres service from the app +postgres:upgrade Upgrade service to the specified version ``` ## usage diff --git a/common-functions b/common-functions index 5ff4442..d9c0b58 100755 --- a/common-functions +++ b/common-functions @@ -183,7 +183,7 @@ service_backup() { fi # shellcheck disable=SC2086 - docker run $BACKUP_PARAMETERS dokkupaas/s3backup:0.8.0 + docker run --rm $BACKUP_PARAMETERS dokkupaas/s3backup:0.8.0 } service_backup_auth() { @@ -273,6 +273,22 @@ service_backup_unset_encryption() { rm -rf "$SERVICE_BACKUP_ENCRYPTION_ROOT" } +service_container_rm() { + declare desc="Stops a service and removes the running container" + declare SERVICE="$1" + local SERVICE_NAME="$(get_service_name "$SERVICE")" + + service_stop "$SERVICE" + local ID=$(docker inspect "$SERVICE_NAME" -f '{{ .ID }}' 2> /dev/null || true) + [[ -z "$ID" ]] && return 0 + + dokku_log_verbose_quiet "Removing container" + docker update --restart=no "$SERVICE_NAME" > /dev/null 2>&1 + if ! docker rm "$SERVICE_NAME" > /dev/null 2>&1; then + dokku_log_fail "Unable to remove container for service $SERVICE" + fi +} + service_enter() { declare desc="enters running app container of specified proc type" declare SERVICE="$1" && shift 1 @@ -300,6 +316,17 @@ service_exposed_ports() { done } +service_image_exists() { + declare desc="Checks if the current image exists" + local IMAGE="$PLUGIN_IMAGE:$PLUGIN_IMAGE_VERSION" + + if [[ "$(docker images -q "$IMAGE" 2> /dev/null)" == "" ]]; then + return 1 + fi + + return 0 +} + service_info() { declare desc="Retrieves information about a given service" declare SERVICE="$1" INFO_FLAG="$2" @@ -414,7 +441,11 @@ service_list() { if [[ -z $SERVICES ]]; then dokku_log_warn "There are no $PLUGIN_SERVICE services" else - LIST="NAME,VERSION,STATUS,EXPOSED PORTS,LINKS\n" + LIST="" + if [[ -z "$DOKKU_QUIET_OUTPUT" ]]; then + LIST="NAME,VERSION,STATUS,EXPOSED PORTS,LINKS\n" + fi + for SERVICE in $SERVICES; do LIST+="$SERVICE,$(service_version "$SERVICE"),$(service_status "$SERVICE"),$(service_exposed_ports "$SERVICE"),$(service_linked_apps "$SERVICE")\n" done @@ -448,24 +479,24 @@ service_parse_args() { for arg in "$@"; do shift case "$arg" in - "--config-options") set -- "$@" "-c" ;; - "--custom-env") set -- "$@" "-C" ;; - "--image") set -- "$@" "-i" ;; - "--image-version") set -- "$@" "-I" ;; - "--password") set -- "$@" "-p" ;; - "--root-password") set -- "$@" "-r" ;; - - "--alias") set -- "$@" "-a" ;; - "--database") set -- "$@" "-d" ;; - "--memory") set -- "$@" "-m" ;; - "--querystring") set -- "$@" "-q" ;; - "--user") set -- "$@" "-u" ;; - *) set -- "$@" "$arg" + "--alias") set -- "$@" "-a" ;; + "--config-options") set -- "$@" "-c" ;; + "--custom-env") set -- "$@" "-C" ;; + "--database") set -- "$@" "-d" ;; + "--image-version") set -- "$@" "-I" ;; + "--image") set -- "$@" "-i" ;; + "--memory") set -- "$@" "-m" ;; + "--password") set -- "$@" "-p" ;; + "--querystring") set -- "$@" "-q" ;; + "--restart-apps") set -- "$@" "-R" ;; + "--root-password") set -- "$@" "-r" ;; + "--user") set -- "$@" "-u" ;; + *) set -- "$@" "$arg" esac done OPTIND=1 - while getopts "a:c:C:d:i:I:m:p:q:r:u:" opt; do + while getopts "a:c:C:d:i:I:m:p:q:R:r:u:" opt; do case "$opt" in a) SERVICE_ALIAS="${OPTARG^^}"; export SERVICE_ALIAS="${SERVICE_ALIAS%_URL}" @@ -486,6 +517,8 @@ service_parse_args() { ;; q) export SERVICE_QUERYSTRING=${OPTARG#"?"} ;; + R) export SERVICE_RESTART_APPS=$OPTARG + ;; r) export SERVICE_ROOT_PASSWORD=$OPTARG ;; u) export SERVICE_USER=$OPTARG diff --git a/functions b/functions index 1a2e9ec..c7e3289 100755 --- a/functions +++ b/functions @@ -29,7 +29,7 @@ service_create() { service_parse_args "${@:2}" - if ! docker images | grep -e "^$PLUGIN_IMAGE " | grep -q " $PLUGIN_IMAGE_VERSION " ; then + if ! service_image_exists "$SERVICE"; then if [[ "$PLUGIN_DISABLE_PULL" == "true" ]]; then dokku_log_warn "${PLUGIN_DISABLE_PULL_VARIABLE} environment variable detected. Not running pull command." 1>&2 dokku_log_warn " docker pull ${IMAGE}" 1>&2 @@ -114,7 +114,7 @@ service_import() { if [[ -t 0 ]]; then dokku_log_fail "No data provided on stdin." fi - docker exec -i "$SERVICE_NAME" env PGPASSWORD="$PASSWORD" pg_restore -h localhost -cO -d "$DATABASE_NAME" -U postgres -w + docker exec -i "$SERVICE_NAME" env PGPASSWORD="$PASSWORD" pg_restore -h localhost -cO --if-exists -d "$DATABASE_NAME" -U postgres -w } service_start() { @@ -137,7 +137,7 @@ service_start() { docker start "$PREVIOUS_ID" > /dev/null service_port_unpause "$SERVICE" dokku_log_info2 "Container started" - elif $IMAGE_EXISTS && [[ -n "$PASSWORD" ]]; then + elif service_image_exists "$SERVICE" && [[ -n "$PASSWORD" ]]; then service_create_container "$SERVICE" else dokku_log_verbose_quiet "Neither container nor valid configuration exists for $SERVICE" diff --git a/help-functions b/help-functions index f87209c..51ae35b 100755 --- a/help-functions +++ b/help-functions @@ -28,14 +28,14 @@ fn-help() { fn-help-all() { declare CMD="$1" SUBCOMMAND="$2" - local CMD_OUTPUT BLUE BOLD FULL_OUTPUT NORMAL + local CMD_OUTPUT BLUE BOLD FULL_OUTPUT NORMAL FULL_OUTPUT=true if [[ "$CMD" = "$PLUGIN_COMMAND_PREFIX:help" ]] || [[ "$CMD" == "$PLUGIN_COMMAND_PREFIX" ]] || [[ "$CMD" == "$PLUGIN_COMMAND_PREFIX:default" ]] ; then - BOLD="$(tput bold)" - NORMAL="\033[m" - BLUE="\033[0;34m" - CYAN="\033[1;36m" + BOLD="$(fn-help-fancy-tput bold)" + NORMAL="$(fn-help-fancy-color "\033[m")" + BLUE="$(fn-help-fancy-color "\033[0;34m")" + CYAN="$(fn-help-fancy-color "\033[1;36m")" if [[ -n "$SUBCOMMAND" ]] && [[ "$SUBCOMMAND" != "--all" ]]; then fn-help-contents-subcommand "$SUBCOMMAND" "$FULL_OUTPUT" return "$?" @@ -76,7 +76,7 @@ fn-help-contents() { fn-help-contents-subcommand() { declare SUBCOMMAND="$1" FULL_OUTPUT="$2" local TMPDIR=$(mktemp -d) - local UNCLEAN_FILE="${TMPDIR}cmd-unclean" CLEAN_FILE="${TMPDIR}cmd-clean" + local UNCLEAN_FILE="${TMPDIR}/cmd-unclean" CLEAN_FILE="${TMPDIR}/cmd-clean" local BOLD CMD_OUTPUT CYAN EXAMPLE LIGHT_GRAY NORMAL trap 'rm -rf "$TMPDIR" > /dev/null' RETURN INT TERM EXIT @@ -95,12 +95,12 @@ fn-help-contents-subcommand() { desc="$(grep desc "$CLEAN_FILE" | head -1)" eval "$desc" - BLUE="\033[0;34m" - BOLD="$(tput bold)" - CYAN="\033[1;36m" - NORMAL="\033[m" - LIGHT_GRAY="\033[2;37m" - LIGHT_RED="\033[1;31m" + BLUE="$(fn-help-fancy-color "\033[0;34m")" + BOLD="$(fn-help-fancy-tput bold)" + CYAN="$(fn-help-fancy-color "\033[1;36m")" + NORMAL="$(fn-help-fancy-color "\033[m")" + LIGHT_GRAY="$(fn-help-fancy-color "\033[2;37m")" + LIGHT_RED="$(fn-help-fancy-color "\033[1;31m")" CMD_OUTPUT="$(echo -e " ${PLUGIN_COMMAND_PREFIX}${cmd_line}, ${LIGHT_GRAY}${desc}${NORMAL}")" if [[ "$FULL_OUTPUT" != "true" ]]; then echo "$CMD_OUTPUT" @@ -139,6 +139,26 @@ fn-help-contents-subcommand() { return 0 } +fn-help-fancy-tput() { + declare desc="A wrapper around tput" + + if [[ -n "$DOKKU_NO_COLOR" ]] || [[ "$TERM" = "unknown" ]] || [[ "$TERM" == "dumb" ]]; then + return + fi + + tput "$@" +} + +fn-help-fancy-color() { + declare desc="A wrapper around colors" + + if [[ -n "$DOKKU_NO_COLOR" ]] || [[ "$TERM" = "unknown" ]] || [[ "$TERM" == "dumb" ]]; then + return + fi + + echo "$@" +} + fn-help-list-example() { # shellcheck disable=SC2034 declare desc="return $PLUGIN_COMMAND_PREFIX plugin help content" @@ -153,8 +173,8 @@ fn-help-subcommand-args() { local argline arglist args argpos BLUE NORMAL if [[ "$FULL_OUTPUT" == "true" ]]; then - BLUE="\033[0;34m" - NORMAL="\033[m" + BLUE="$(fn-help-fancy-color "\033[0;34m")" + NORMAL="$(fn-help-fancy-color "\033[m")" fi argline=$(grep declare "$FUNC_FILE" | grep -v "declare desc" | head -1 || true) arglist=($(echo -e "${argline// /"\n"}" | awk -F= '/=/{print ""$1""}')) @@ -199,12 +219,12 @@ fn-help-subcommand-example() { return 0 fi - BOLD="$(tput bold)" + BOLD="$(fn-help-fancy-tput bold)" LAST_LINE="" - LIGHT_GRAY="\033[2;37m" - OTHER_GRAY="\033[7;37m" + LIGHT_GRAY="$(fn-help-fancy-color "\033[2;37m")" + OTHER_GRAY="$(fn-help-fancy-color "\033[7;37m")" NEWLINE="" - NORMAL="\033[m" + NORMAL="$(fn-help-fancy-color "\033[m")" _fn-help-apply-shell-expansion "$EXAMPLE" | while read -r line; do line="$(echo "$line" | cut -c 4-)" if [[ "$line" == export* ]] || [[ "$line" == dokku* ]]; then @@ -234,10 +254,10 @@ fn-help-subcommand-list-args() { return 0 fi - NORMAL="\033[m" - LIGHT_GRAY="\033[2;37m" + NORMAL="$(fn-help-fancy-color "\033[m")" + LIGHT_GRAY="$(fn-help-fancy-color "\033[2;37m")" - _fn-help-apply-shell-expansion "$FLAGS" | while read -r line; do + _fn-help-apply-shell-expansion "$FLAGS" | while read -r line; do echo -e "$(echo "$line" | cut -d',' -f1),${LIGHT_GRAY}$(echo "$line" | cut -d',' -f2-)${NORMAL}" done } @@ -251,10 +271,10 @@ fn-help-subcommand-list-flags() { return 0 fi - NORMAL="\033[m" - LIGHT_GRAY="\033[2;37m" + NORMAL="$(fn-help-fancy-color "\033[m")" + LIGHT_GRAY="$(fn-help-fancy-color "\033[2;37m")" - _fn-help-apply-shell-expansion "$FLAGS" | while read -r line; do + _fn-help-apply-shell-expansion "$FLAGS" | while read -r line; do echo -e "$(echo "$line" | cut -d',' -f1),${LIGHT_GRAY}$(echo "$line" | cut -d',' -f2-)${NORMAL}" done } diff --git a/plugin.toml b/plugin.toml index e3b17d6..58d14e7 100644 --- a/plugin.toml +++ b/plugin.toml @@ -1,4 +1,4 @@ [plugin] description = "dokku postgres service plugin" -version = "1.3.1" +version = "1.4.11" [plugin.config] diff --git a/subcommands/backup-set-encryption b/subcommands/backup-set-encryption index 175d716..dce3620 100755 --- a/subcommands/backup-set-encryption +++ b/subcommands/backup-set-encryption @@ -5,19 +5,19 @@ source "$PLUGIN_BASE_PATH/common/functions" source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/functions" service-backup-set-encryption-cmd() { - #E set a GPG encryption key for backups + #E set a GPG passphrase for backups #E dokku $PLUGIN_COMMAND_PREFIX:backup-set-encryption lolipop #A service, service to run command against - #A encryption-key, a GPG encryption key + #A passphrase, a GPG-compatible passphrase declare desc="sets encryption for all future backups of $PLUGIN_SERVICE service" local cmd="$PLUGIN_COMMAND_PREFIX:backup-set-encryption" argv=("$@"); [[ ${argv[0]} == "$cmd" ]] && shift 1 - declare SERVICE="$1" ENCRYPTION_KEY="$2" + declare SERVICE="$1" PASSPHRASE="$2" is_implemented_command "$cmd" || dokku_log_fail "Not yet implemented" [[ -z "$SERVICE" ]] && dokku_log_fail "Please specify a name for the service" - [[ -z "$ENCRYPTION_KEY" ]] && dokku_log_fail "Please specify a GPG encryption key" + [[ -z "$PASSPHRASE" ]] && dokku_log_fail "Please specify a GPG backup passphrase" verify_service_name "$SERVICE" - service_backup_set_encryption "$SERVICE" "$ENCRYPTION_KEY" + service_backup_set_encryption "$SERVICE" "$PASSPHRASE" } service-backup-set-encryption-cmd "$@" diff --git a/subcommands/clone b/subcommands/clone index 6c8beb3..ab0e073 100755 --- a/subcommands/clone +++ b/subcommands/clone @@ -9,9 +9,9 @@ service-clone-cmd() { #E dokku $PLUGIN_COMMAND_PREFIX:clone lolipop lolipop-2 #A service, service to run command against #A new-service, name of new service - #F -c|--custom-env "USER=alpha;HOST=beta", semi-colon delimited environment variables to start the service with + #F -C|--custom-env "USER=alpha;HOST=beta", semi-colon delimited environment variables to start the service with #F -i|--image IMAGE, the image name to start the service with - #F -i|--image-version IMAGE_VERSION, the image version to start the service with + #F -I|--image-version IMAGE_VERSION, the image version to start the service with #F -p|--password PASSWORD, override the user-level service password #F -r|--root-password PASSWORD, override the root-level service password declare desc="create container then copy data from into " diff --git a/subcommands/create b/subcommands/create index f8456e4..e5135b3 100755 --- a/subcommands/create +++ b/subcommands/create @@ -17,9 +17,9 @@ service-create-cmd() { #E export ${PLUGIN_DEFAULT_ALIAS}_CUSTOM_ENV="USER=alpha;HOST=beta" #E dokku $PLUGIN_COMMAND_PREFIX:create lolipop #A service, service to run command against - #F -c|--custom-env "USER=alpha;HOST=beta", semi-colon delimited environment variables to start the service with + #F -C|--custom-env "USER=alpha;HOST=beta", semi-colon delimited environment variables to start the service with #F -i|--image IMAGE, the image name to start the service with - #F -i|--image-version IMAGE_VERSION, the image version to start the service with + #F -I|--image-version IMAGE_VERSION, the image version to start the service with #F -p|--password PASSWORD, override the user-level service password #F -r|--root-password PASSWORD, override the root-level service password declare desc="create a $PLUGIN_SERVICE service" diff --git a/subcommands/upgrade b/subcommands/upgrade new file mode 100755 index 0000000..76f1a9b --- /dev/null +++ b/subcommands/upgrade @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_BASE_PATH/common/functions" +source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/functions" +source "$PLUGIN_AVAILABLE_PATH/ps/functions" + +service-upgrade-cmd() { + #E you can upgrade an existing service to a new image or image-version + #E dokku $PLUGIN_COMMAND_PREFIX:upgrade lolipop + #A service, service to run command against + #F -C|--custom-env "USER=alpha;HOST=beta", semi-colon delimited environment variables to start the service with + #F -i|--image IMAGE, the image name to start the service with + #F -I|--image-version IMAGE_VERSION, the image version to start the service with + #F -R|--restart-apps "true", whether to force an app restart + declare desc="upgrade service to the specified versions" + local cmd="$PLUGIN_COMMAND_PREFIX:upgrade" argv=("$@"); [[ ${argv[0]} == "$cmd" ]] && shift 1 + declare SERVICE="$1" UPGRADE_FLAGS_LIST="${@:2}" + + [[ -z "$SERVICE" ]] && dokku_log_fail "Please specify a name for the service" + verify_service_name "$SERVICE" + + local SERVICE_ROOT="$PLUGIN_DATA_ROOT/$SERVICE" + + service_parse_args "${@:2}" + + if ! service_image_exists "$SERVICE"; then + dokku_log_fail "Unable to proceed with upgrade, image ${PLUGIN_IMAGE}:${PLUGIN_IMAGE_VERSION} does not exist" + fi + + local NEW_PLUGIN_IMAGE_TAG="$PLUGIN_IMAGE:$PLUGIN_IMAGE_VERSION" + if [[ "$(service_version "$SERVICE")" == "$NEW_PLUGIN_IMAGE_TAG" ]]; then + dokku_log_info1 "Service $SERVICE already running $NEW_PLUGIN_IMAGE_TAG" + return + fi + + dokku_log_info2 "Upgrading $SERVICE to $NEW_PLUGIN_IMAGE_TAG" + if [[ "$SERVICE_RESTART_APPS" == "true" ]]; then + dokku_log_info2 "Stopping all linked services" + for app in $(service_linked_apps "$SERVICE"); do + [[ "$app" == "-" ]] && continue + ps_stop "$app" + done + fi + + dokku_log_info2 "Stopping $SERVICE" + service_container_rm "$SERVICE" + service_start "$SERVICE" "${@:2}" + + if [[ "$SERVICE_RESTART_APPS" == "true" ]]; then + dokku_log_info2 "Starting all linked services" + for app in $(service_linked_apps "$SERVICE"); do + [[ "$app" == "-" ]] && continue + ps_start "$app" + done + fi + + dokku_log_info2 "Done" +} + +service-upgrade-cmd "$@" diff --git a/tests/service_import.bats b/tests/service_import.bats index a9dfb1c..e6a83e5 100755 --- a/tests/service_import.bats +++ b/tests/service_import.bats @@ -32,6 +32,6 @@ teardown() { export ECHO_DOCKER_COMMAND="true" run dokku "$PLUGIN_COMMAND_PREFIX:import" l < "$PLUGIN_DATA_ROOT/fake.dump" password="$(cat "$PLUGIN_DATA_ROOT/l/PASSWORD")" - assert_output "docker exec -i dokku.postgres.l env PGPASSWORD=$password pg_restore -h localhost -cO -d l -U postgres -w" + assert_output "docker exec -i dokku.postgres.l env PGPASSWORD=$password pg_restore -h localhost -cO --if-exists -d l -U postgres -w" } diff --git a/tests/test_helper.bash b/tests/test_helper.bash old mode 100644 new mode 100755 index 9aa1026..39b6092 --- a/tests/test_helper.bash +++ b/tests/test_helper.bash @@ -1,5 +1,4 @@ #!/usr/bin/env bash -export DOKKU_QUIET_OUTPUT=1 export DOKKU_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/dokku" export DOKKU_VERSION=${DOKKU_VERSION:-"master"} export PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/bin:$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/dokku:$PATH"