├── update ├── Dockerfile ├── .gitignore ├── plugin.toml ├── docs └── README.md ├── .devcontainer ├── bin │ └── copy-file ├── devcontainer.json ├── 20_init_plugin └── Dockerfile ├── .editorconfig ├── .github ├── dependabot.yml ├── workflows │ ├── tagged-release.yml │ └── ci.yml └── labels.yml ├── commands ├── pre-delete ├── subcommands ├── list ├── start ├── pause ├── stop ├── exists ├── unexpose ├── app-links ├── import ├── restart ├── backup-deauth ├── backup-schedule-cat ├── links ├── backup-unschedule ├── backup-unset-encryption ├── export ├── connect ├── backup-unset-public-key-encryption ├── linked ├── enter ├── unlink ├── expose ├── backup-set-encryption ├── logs ├── backup ├── backup-set-public-key-encryption ├── backup-schedule ├── set ├── promote ├── backup-auth ├── create ├── info ├── destroy ├── clone ├── upgrade └── link ├── post-app-clone-setup ├── post-app-rename-setup ├── service-list ├── tests ├── hook_pre_delete.bats ├── service_list.bats ├── service_stop.bats ├── service_pause.bats ├── service_start.bats ├── service_restart.bats ├── service_connect.bats ├── service_unexpose.bats ├── shellcheck-exclude ├── service_create.bats ├── service_export.bats ├── service_destroy.bats ├── service_logs.bats ├── service_import.bats ├── service_clone.bats ├── setup.sh ├── test_helper.bash ├── service_expose.bats ├── service_info.bats ├── service_unlink.bats ├── service_promote.bats ├── service_link.bats ├── shellcheck-to-junit └── link_networks.bats ├── Vagrantfile ├── pre-start ├── LICENSE.txt ├── pre-restore ├── config ├── install ├── Makefile ├── help-functions ├── functions ├── bin └── generate └── README.md /update: -------------------------------------------------------------------------------- 1 | install -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mysql:9.5.0 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /tmp 2 | .vagrant 3 | bootstrap.sh 4 | -------------------------------------------------------------------------------- /plugin.toml: -------------------------------------------------------------------------------- 1 | [plugin] 2 | description = "dokku mysql service plugin" 3 | version = "1.44.1" 4 | [plugin.config] 5 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Extra Documentation 2 | 3 | The documentation in this folder is supplemental to using this plugin. It is injected automatically into the plugin's readme during documentation generation. 4 | 5 | -------------------------------------------------------------------------------- /.devcontainer/bin/copy-file: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | main() { 4 | PLUGIN_NAME="$(source /tmp/.env && echo "$PLUGIN_NAME")" 5 | cp "$1" "/var/lib/dokku/plugins/enabled/$PLUGIN_NAME/$1" 6 | } 7 | 8 | main "$@" 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | insert_final_newline = true 5 | indent_style = space 6 | indent_size = 2 7 | 8 | [Makefile] 9 | insert_final_newline = true 10 | indent_style = tab 11 | indent_size = 4 12 | 13 | [*.mk] 14 | insert_final_newline = true 15 | indent_style = tab 16 | indent_size = 4 17 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | updates: 4 | - package-ecosystem: "docker" 5 | directory: "/" 6 | schedule: 7 | interval: "daily" 8 | - package-ecosystem: "github-actions" 9 | directory: "/" 10 | schedule: 11 | interval: daily 12 | open-pull-requests-limit: 10 13 | -------------------------------------------------------------------------------- /.github/workflows/tagged-release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "tagged-release" 3 | 4 | # yamllint disable-line rule:truthy 5 | on: 6 | push: 7 | tags: 8 | - "*" 9 | 10 | jobs: 11 | tagged-release: 12 | name: tagged-release 13 | runs-on: ubuntu-24.04 14 | 15 | steps: 16 | - name: Release 17 | uses: softprops/action-gh-release@v2.5.0 18 | with: 19 | generate_release_notes: true 20 | make_latest: "true" 21 | -------------------------------------------------------------------------------- /commands: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/config" 3 | [[ " help $PLUGIN_COMMAND_PREFIX:help $PLUGIN_COMMAND_PREFIX $PLUGIN_COMMAND_PREFIX:default " == *" $1 "* ]] || [[ "$1" == "$PLUGIN_COMMAND_PREFIX:"* ]] || exit "$DOKKU_NOT_IMPLEMENTED_EXIT" 4 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 5 | 6 | set -eo pipefail 7 | [[ $DOKKU_TRACE ]] && set -x 8 | 9 | source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/help-functions" 10 | 11 | fn-help "$@" 12 | -------------------------------------------------------------------------------- /pre-delete: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 3 | source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/config" 4 | source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/functions" 5 | set -eo pipefail 6 | [[ $DOKKU_TRACE ]] && set -x 7 | 8 | APP="$1" 9 | for SERVICE in $(fn-services-list false); do 10 | [[ -n "$SERVICE" ]] || continue 11 | dokku_log_verbose_quiet "Unlinking from $SERVICE" 12 | remove_from_links_file "$(basename "$SERVICE")" "$APP" 13 | done 14 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "build": { 3 | "dockerfile": "Dockerfile", 4 | "context": ".." 5 | }, 6 | "containerEnv": { 7 | "SERVICE_HOST_ROOT": "${localWorkspaceFolder}/tmp/data" 8 | }, 9 | "initializeCommand": ["mkdir", "-p", "tmp/data"], 10 | "mounts": [ 11 | "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind", 12 | "source=${localWorkspaceFolder}/tmp/data/,target=/var/lib/dokku/services/,type=bind" 13 | ], 14 | "overrideCommand": false, 15 | "runArgs": ["--init"] 16 | } 17 | -------------------------------------------------------------------------------- /.devcontainer/20_init_plugin: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -eo pipefail 3 | 4 | log-info() { 5 | declare desc="Log info formatter" 6 | echo " $*" 1>&2 7 | } 8 | 9 | log-fail() { 10 | declare desc="Log fail formatter" 11 | echo "! $*" 1>&2 12 | exit 1 13 | } 14 | 15 | main() { 16 | dokku plugin:install 17 | 18 | # built in the Dockerfile 19 | PLUGIN_NAME="$(source /tmp/.env && echo "$PLUGIN_NAME")" 20 | PLUGIN_VARIABLE="$(source /tmp/.env && echo "$PLUGIN_VARIABLE")" 21 | echo "export ${PLUGIN_VARIABLE}_HOST_ROOT=${SERVICE_HOST_ROOT}/$PLUGIN_NAME" >/etc/default/dokku 22 | } 23 | 24 | main "$@" 25 | -------------------------------------------------------------------------------- /subcommands/list: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 6 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/functions" 7 | 8 | service-list-cmd() { 9 | #E list all services 10 | #E dokku $PLUGIN_COMMAND_PREFIX:list 11 | declare desc="list all $PLUGIN_SERVICE services" 12 | local cmd="$PLUGIN_COMMAND_PREFIX:list" argv=("$@") 13 | [[ ${argv[0]} == "$cmd" ]] && shift 1 14 | 15 | service_list 16 | } 17 | 18 | service-list-cmd "$@" 19 | -------------------------------------------------------------------------------- /post-app-clone-setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/config" 3 | source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/common-functions" 4 | source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/functions" 5 | set -eo pipefail 6 | [[ $DOKKU_TRACE ]] && set -x 7 | 8 | plugin-post-app-clone-setup() { 9 | declare OLD_APP_NAME="$1" NEW_APP_NAME="$2" 10 | 11 | for SERVICE in $(fn-services-list false); do 12 | if in_links_file "$SERVICE" "$OLD_APP_NAME"; then 13 | add_to_links_file "$SERVICE" "$NEW_APP_NAME" 14 | fi 15 | done 16 | } 17 | 18 | plugin-post-app-clone-setup "$@" 19 | -------------------------------------------------------------------------------- /post-app-rename-setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/config" 3 | source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/common-functions" 4 | source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/functions" 5 | set -eo pipefail 6 | [[ $DOKKU_TRACE ]] && set -x 7 | 8 | plugin-post-app-rename-setup() { 9 | declare OLD_APP_NAME="$1" NEW_APP_NAME="$2" 10 | 11 | for SERVICE in $(fn-services-list false); do 12 | if in_links_file "$SERVICE" "$OLD_APP_NAME"; then 13 | add_to_links_file "$SERVICE" "$NEW_APP_NAME" 14 | fi 15 | done 16 | } 17 | 18 | plugin-post-app-rename-setup "$@" 19 | -------------------------------------------------------------------------------- /service-list: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/config" 3 | source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/common-functions" 4 | set -eo pipefail 5 | [[ $DOKKU_TRACE ]] && set -x 6 | 7 | plugin-service-list() { 8 | declare desc="allows listing all services for use by other dokku plugins" 9 | declare SERVICE_TYPE="$1" 10 | 11 | if [[ -n "$SERVICE_TYPE" ]] && [[ "$SERVICE_TYPE" != "$PLUGIN_COMMAND_PREFIX" ]]; then 12 | return 13 | fi 14 | 15 | for service in $(fn-services-list false); do 16 | echo "$PLUGIN_COMMAND_PREFIX:$service" 17 | done 18 | } 19 | 20 | plugin-service-list "$@" 21 | -------------------------------------------------------------------------------- /tests/hook_pre_delete.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | load test_helper 3 | 4 | setup() { 5 | dokku apps:create my-app 6 | dokku "$PLUGIN_COMMAND_PREFIX:create" ls 7 | dokku "$PLUGIN_COMMAND_PREFIX:link" ls my-app >&2 8 | } 9 | 10 | teardown() { 11 | dokku "$PLUGIN_COMMAND_PREFIX:unlink" ls my-app >&2 12 | dokku --force "$PLUGIN_COMMAND_PREFIX:destroy" ls 13 | dokku --force apps:destroy my-app || true 14 | } 15 | 16 | @test "($PLUGIN_COMMAND_PREFIX:hook:pre-delete) removes app from links file when destroying app" { 17 | [[ -n $(<"$PLUGIN_DATA_ROOT/ls/LINKS") ]] 18 | dokku --force apps:destroy my-app 19 | [[ -z $(<"$PLUGIN_DATA_ROOT/ls/LINKS") ]] 20 | } 21 | -------------------------------------------------------------------------------- /tests/service_list.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | load test_helper 3 | 4 | setup() { 5 | dokku "$PLUGIN_COMMAND_PREFIX:create" l 6 | } 7 | 8 | teardown() { 9 | dokku --force "$PLUGIN_COMMAND_PREFIX:destroy" l 10 | } 11 | 12 | @test "($PLUGIN_COMMAND_PREFIX:list) with no exposed ports, no linked apps" { 13 | run dokku --quiet "$PLUGIN_COMMAND_PREFIX:list" 14 | assert_output "l" 15 | } 16 | 17 | @test "($PLUGIN_COMMAND_PREFIX:list) when there are no services" { 18 | dokku --force "$PLUGIN_COMMAND_PREFIX:destroy" l 19 | run dokku "$PLUGIN_COMMAND_PREFIX:list" 20 | assert_output "${lines[*]}" "There are no $PLUGIN_SERVICE services" 21 | dokku "$PLUGIN_COMMAND_PREFIX:create" l 22 | } 23 | -------------------------------------------------------------------------------- /tests/service_stop.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | load test_helper 3 | 4 | setup() { 5 | dokku "$PLUGIN_COMMAND_PREFIX:create" l 6 | } 7 | 8 | teardown() { 9 | dokku --force "$PLUGIN_COMMAND_PREFIX:destroy" l 10 | } 11 | 12 | @test "($PLUGIN_COMMAND_PREFIX:stop) error when there are no arguments" { 13 | run dokku "$PLUGIN_COMMAND_PREFIX:stop" 14 | assert_contains "${lines[*]}" "Please specify a valid name for the service" 15 | } 16 | 17 | @test "($PLUGIN_COMMAND_PREFIX:stop) error when service does not exist" { 18 | run dokku "$PLUGIN_COMMAND_PREFIX:stop" not_existing_service 19 | assert_contains "${lines[*]}" "service not_existing_service does not exist" 20 | } 21 | 22 | @test "($PLUGIN_COMMAND_PREFIX:stop) success" { 23 | run dokku "$PLUGIN_COMMAND_PREFIX:stop" l 24 | assert_success 25 | } 26 | -------------------------------------------------------------------------------- /tests/service_pause.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | load test_helper 3 | 4 | setup() { 5 | dokku "$PLUGIN_COMMAND_PREFIX:create" l 6 | } 7 | 8 | teardown() { 9 | dokku --force "$PLUGIN_COMMAND_PREFIX:destroy" l 10 | } 11 | 12 | @test "($PLUGIN_COMMAND_PREFIX:pause) error when there are no arguments" { 13 | run dokku "$PLUGIN_COMMAND_PREFIX:pause" 14 | assert_contains "${lines[*]}" "Please specify a valid name for the service" 15 | } 16 | 17 | @test "($PLUGIN_COMMAND_PREFIX:pause) error when service does not exist" { 18 | run dokku "$PLUGIN_COMMAND_PREFIX:pause" not_existing_service 19 | assert_contains "${lines[*]}" "service not_existing_service does not exist" 20 | } 21 | 22 | @test "($PLUGIN_COMMAND_PREFIX:pause) success" { 23 | run dokku "$PLUGIN_COMMAND_PREFIX:pause" l 24 | assert_success 25 | } 26 | -------------------------------------------------------------------------------- /tests/service_start.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | load test_helper 3 | 4 | setup() { 5 | dokku "$PLUGIN_COMMAND_PREFIX:create" l 6 | } 7 | 8 | teardown() { 9 | dokku --force "$PLUGIN_COMMAND_PREFIX:destroy" l 10 | } 11 | 12 | @test "($PLUGIN_COMMAND_PREFIX:start) error when there are no arguments" { 13 | run dokku "$PLUGIN_COMMAND_PREFIX:start" 14 | assert_contains "${lines[*]}" "Please specify a valid name for the service" 15 | } 16 | 17 | @test "($PLUGIN_COMMAND_PREFIX:start) error when service does not exist" { 18 | run dokku "$PLUGIN_COMMAND_PREFIX:start" not_existing_service 19 | assert_contains "${lines[*]}" "service not_existing_service does not exist" 20 | } 21 | 22 | @test "($PLUGIN_COMMAND_PREFIX:start) success" { 23 | run dokku "$PLUGIN_COMMAND_PREFIX:start" l 24 | assert_success 25 | } 26 | -------------------------------------------------------------------------------- /tests/service_restart.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | load test_helper 3 | 4 | setup() { 5 | dokku "$PLUGIN_COMMAND_PREFIX:create" l 6 | } 7 | 8 | teardown() { 9 | dokku --force "$PLUGIN_COMMAND_PREFIX:destroy" l 10 | } 11 | 12 | @test "($PLUGIN_COMMAND_PREFIX:restart) error when there are no arguments" { 13 | run dokku "$PLUGIN_COMMAND_PREFIX:restart" 14 | assert_contains "${lines[*]}" "Please specify a valid name for the service" 15 | } 16 | 17 | @test "($PLUGIN_COMMAND_PREFIX:restart) error when service does not exist" { 18 | run dokku "$PLUGIN_COMMAND_PREFIX:restart" not_existing_service 19 | assert_contains "${lines[*]}" "service not_existing_service does not exist" 20 | } 21 | 22 | @test "($PLUGIN_COMMAND_PREFIX:restart) success" { 23 | run dokku "$PLUGIN_COMMAND_PREFIX:restart" l 24 | assert_success 25 | } 26 | -------------------------------------------------------------------------------- /subcommands/start: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 6 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/functions" 7 | 8 | service-start-cmd() { 9 | #E start the service 10 | #E dokku $PLUGIN_COMMAND_PREFIX:start lollipop 11 | #A service, service to run command against 12 | declare desc="start a previously stopped $PLUGIN_SERVICE service" 13 | local cmd="$PLUGIN_COMMAND_PREFIX:start" argv=("$@") 14 | [[ ${argv[0]} == "$cmd" ]] && shift 1 15 | declare SERVICE="$1" 16 | 17 | [[ -z "$SERVICE" ]] && dokku_log_fail "Please specify a valid name for the service" 18 | verify_service_name "$SERVICE" 19 | service_start "$SERVICE" 20 | } 21 | 22 | service-start-cmd "$@" 23 | -------------------------------------------------------------------------------- /tests/service_connect.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | load test_helper 3 | 4 | setup() { 5 | dokku "$PLUGIN_COMMAND_PREFIX:create" l 6 | } 7 | 8 | teardown() { 9 | dokku --force "$PLUGIN_COMMAND_PREFIX:destroy" l 10 | } 11 | 12 | @test "($PLUGIN_COMMAND_PREFIX:connect) error when there are no arguments" { 13 | run dokku "$PLUGIN_COMMAND_PREFIX:connect" 14 | assert_contains "${lines[*]}" "Please specify a valid name for the service" 15 | } 16 | 17 | @test "($PLUGIN_COMMAND_PREFIX:connect) error when service does not exist" { 18 | run dokku "$PLUGIN_COMMAND_PREFIX:connect" not_existing_service 19 | assert_contains "${lines[*]}" "service not_existing_service does not exist" 20 | } 21 | 22 | @test "($PLUGIN_COMMAND_PREFIX:connect) success" { 23 | skip "Connect hangs indefinitely without input" 24 | run dokku "$PLUGIN_COMMAND_PREFIX:connect" l 25 | assert_success 26 | } 27 | -------------------------------------------------------------------------------- /subcommands/pause: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 6 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/functions" 7 | 8 | service-pause-cmd() { 9 | #E pause the running container for the service 10 | #E dokku $PLUGIN_COMMAND_PREFIX:pause lollipop 11 | #A service, service to run command against 12 | declare desc="pause a running $PLUGIN_SERVICE service" 13 | local cmd="$PLUGIN_COMMAND_PREFIX:pause" argv=("$@") 14 | [[ ${argv[0]} == "$cmd" ]] && shift 1 15 | declare SERVICE="$1" 16 | 17 | [[ -z "$SERVICE" ]] && dokku_log_fail "Please specify a valid name for the service" 18 | verify_service_name "$SERVICE" 19 | service_pause "$SERVICE" 20 | } 21 | 22 | service-pause-cmd "$@" 23 | -------------------------------------------------------------------------------- /subcommands/stop: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 6 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/functions" 7 | 8 | service-stop-cmd() { 9 | #E stop the service and removes the running container 10 | #E dokku $PLUGIN_COMMAND_PREFIX:stop lollipop 11 | #A service, service to run command against 12 | declare desc="stop a running $PLUGIN_SERVICE service" 13 | local cmd="$PLUGIN_COMMAND_PREFIX:stop" argv=("$@") 14 | [[ ${argv[0]} == "$cmd" ]] && shift 1 15 | declare SERVICE="$1" 16 | 17 | [[ -z "$SERVICE" ]] && dokku_log_fail "Please specify a valid name for the service" 18 | verify_service_name "$SERVICE" 19 | service_container_rm "$SERVICE" 20 | } 21 | 22 | service-stop-cmd "$@" 23 | -------------------------------------------------------------------------------- /subcommands/exists: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 6 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/functions" 7 | 8 | service-exists-cmd() { 9 | #E here we check if the lollipop $PLUGIN_COMMAND_PREFIX service exists. 10 | #E dokku $PLUGIN_COMMAND_PREFIX:exists lollipop 11 | #A service, service to run command against 12 | declare desc="check if the $PLUGIN_SERVICE service exists" 13 | local cmd="$PLUGIN_COMMAND_PREFIX:exists" argv=("$@") 14 | [[ ${argv[0]} == "$cmd" ]] && shift 1 15 | declare SERVICE="$1" 16 | 17 | [[ -z "$SERVICE" ]] && dokku_log_fail "Please specify a valid name for the service" 18 | verify_service_name "$SERVICE" 19 | dokku_log_info1 "Service $SERVICE exists" 20 | } 21 | 22 | service-exists-cmd "$@" 23 | -------------------------------------------------------------------------------- /subcommands/unexpose: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 6 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/functions" 7 | 8 | service-unexpose-cmd() { 9 | #E unexpose the service, removing access to it from the public interface (0.0.0.0) 10 | #E dokku $PLUGIN_COMMAND_PREFIX:unexpose lollipop 11 | #A service, service to run command against 12 | declare desc="unexpose a previously exposed $PLUGIN_SERVICE service" 13 | local cmd="$PLUGIN_COMMAND_PREFIX:unexpose" argv=("$@") 14 | [[ ${argv[0]} == "$cmd" ]] && shift 1 15 | declare SERVICE="$1" 16 | 17 | [[ -z "$SERVICE" ]] && dokku_log_fail "Please specify a valid name for the service" 18 | verify_service_name "$SERVICE" 19 | service_port_unexpose "$SERVICE" 20 | } 21 | 22 | service-unexpose-cmd "$@" 23 | -------------------------------------------------------------------------------- /tests/service_unexpose.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | load test_helper 3 | 4 | setup() { 5 | dokku "$PLUGIN_COMMAND_PREFIX:create" l 6 | } 7 | 8 | teardown() { 9 | dokku --force "$PLUGIN_COMMAND_PREFIX:destroy" l 10 | } 11 | 12 | @test "($PLUGIN_COMMAND_PREFIX:unexpose) error when there are no arguments" { 13 | run dokku "$PLUGIN_COMMAND_PREFIX:unexpose" 14 | assert_contains "${lines[*]}" "Please specify a valid name for the service" 15 | } 16 | 17 | @test "($PLUGIN_COMMAND_PREFIX:unexpose) error when service does not exist" { 18 | run dokku "$PLUGIN_COMMAND_PREFIX:unexpose" not_existing_service 19 | assert_contains "${lines[*]}" "service not_existing_service does not exist" 20 | } 21 | 22 | @test "($PLUGIN_COMMAND_PREFIX:unexpose) success" { 23 | dokku "$PLUGIN_COMMAND_PREFIX:expose" l 24 | run dokku "$PLUGIN_COMMAND_PREFIX:unexpose" l 25 | [[ ! -f $PLUGIN_DATA_ROOT/PORT ]] 26 | assert_contains "${lines[*]}" "Service l unexposed" 27 | } 28 | -------------------------------------------------------------------------------- /subcommands/app-links: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 6 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/functions" 7 | 8 | service-app-links-cmd() { 9 | #E list all $PLUGIN_COMMAND_PREFIX services that are linked to the 'playground' app. 10 | #E dokku $PLUGIN_COMMAND_PREFIX:app-links playground 11 | #A app, app to run command against 12 | declare desc="list all $PLUGIN_SERVICE service links for a given app" 13 | local cmd="$PLUGIN_COMMAND_PREFIX:app-links" argv=("$@") 14 | [[ ${argv[0]} == "$cmd" ]] && shift 1 15 | declare APP="$1" 16 | APP=${APP:="$DOKKU_APP_NAME"} 17 | 18 | [[ -z "$APP" ]] && dokku_log_fail "Please specify an app to run the command on" 19 | verify_app_name "$APP" 20 | service_app_links "$APP" 21 | } 22 | 23 | service-app-links-cmd "$@" 24 | -------------------------------------------------------------------------------- /subcommands/import: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 6 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/functions" 7 | 8 | service-import-cmd() { 9 | #E import a datastore dump 10 | #E dokku $PLUGIN_COMMAND_PREFIX:import lollipop < data.dump 11 | #A service, service to run command against 12 | declare desc="import a dump into the $PLUGIN_SERVICE service database" 13 | local cmd="$PLUGIN_COMMAND_PREFIX:import" argv=("$@") 14 | [[ ${argv[0]} == "$cmd" ]] && shift 1 15 | declare SERVICE="$1" 16 | is_implemented_command "$cmd" || dokku_log_fail "Not yet implemented" 17 | 18 | [[ -z "$SERVICE" ]] && dokku_log_fail "Please specify a valid name for the service" 19 | verify_service_name "$SERVICE" 20 | service_import "$SERVICE" 21 | } 22 | 23 | service-import-cmd "$@" 24 | -------------------------------------------------------------------------------- /tests/shellcheck-exclude: -------------------------------------------------------------------------------- 1 | # SC1090 - Can't follow non-constant source. Use a directive to specify location - https://github.com/koalaman/shellcheck/wiki/SC1090 2 | # SC1091 - Not following - 3 | # SC2034 - Variable appears unused. Verify it or export it - https://github.com/koalaman/shellcheck/wiki/SC2034 4 | # SC2155 - Declare and assign separately to avoid masking return values - https://github.com/koalaman/shellcheck/wiki/SC2155 5 | # SC2206 - Quote to prevent word splitting/globbing, or split robustly with mapfile or read -a - https://github.com/koalaman/shellcheck/wiki/SC2206 6 | # SC2207 - Prefer mapfile or read -a to split command output (or quote to avoid splitting) - https://github.com/koalaman/shellcheck/wiki/SC2207 7 | # SC2220 - Invalid flags are not handled. Add a *) case - https://github.com/koalaman/shellcheck/wiki/SC2220 8 | # SC2230 - which is non-standard. Use builtin 'command -v' instead - https://github.com/koalaman/shellcheck/wiki/SC2230 9 | -------------------------------------------------------------------------------- /subcommands/restart: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 6 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/functions" 7 | 8 | service-restart-cmd() { 9 | #E restart the service 10 | #E dokku $PLUGIN_COMMAND_PREFIX:restart lollipop 11 | #A service, service to run command against 12 | declare desc="graceful shutdown and restart of the $PLUGIN_SERVICE service container" 13 | local cmd="$PLUGIN_COMMAND_PREFIX:restart" argv=("$@") 14 | [[ ${argv[0]} == "$cmd" ]] && shift 1 15 | declare SERVICE="$1" 16 | 17 | [[ -z "$SERVICE" ]] && dokku_log_fail "Please specify a valid name for the service" 18 | verify_service_name "$SERVICE" 19 | service_pause "$SERVICE" 20 | service_start "$SERVICE" 21 | dokku_log_info1 "Please call dokku ps:restart on all linked apps" 22 | } 23 | 24 | service-restart-cmd "$@" 25 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | BOX_NAME = ENV["BOX_NAME"] || "bento/ubuntu-24.04" 5 | BOX_MEMORY = ENV["BOX_MEMORY"] || "2048" 6 | DOKKU_VERSION = "master" 7 | 8 | Vagrant.configure(2) do |config| 9 | config.vm.box = BOX_NAME 10 | config.ssh.forward_agent = true 11 | 12 | config.vm.provider :virtualbox do |vb| 13 | vb.customize ["modifyvm", :id, "--memory", BOX_MEMORY] 14 | end 15 | 16 | config.vm.provider :vmware_fusion do |v, override| 17 | v.vmx["memsize"] = BOX_MEMORY 18 | end 19 | 20 | config.vm.define "default", primary: true do |vm| 21 | vm.vm.synced_folder File.dirname(__FILE__), "/vagrant" 22 | 23 | vm.vm.provision :shell, :inline => "apt -q update && apt -y -qq install git software-properties-common" 24 | vm.vm.provision :shell, :inline => "cd /vagrant && DOKKU_VERSION=#{DOKKU_VERSION} make setup" 25 | vm.vm.provision :shell, :inline => "cd /vagrant && DOKKU_TRACE=1 DOKKU_VERSION=#{DOKKU_VERSION} make test" 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /subcommands/backup-deauth: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 6 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/functions" 7 | 8 | service-backup-deauth-cmd() { 9 | #E remove s3 authentication 10 | #E dokku $PLUGIN_COMMAND_PREFIX:backup-deauth lollipop 11 | #A service, service to run command against 12 | declare desc="remove backup authentication for the $PLUGIN_SERVICE service" 13 | local cmd="$PLUGIN_COMMAND_PREFIX:backup-deauth" argv=("$@") 14 | [[ ${argv[0]} == "$cmd" ]] && shift 1 15 | declare SERVICE="$1" 16 | is_implemented_command "$cmd" || dokku_log_fail "Not yet implemented" 17 | 18 | [[ -z "$SERVICE" ]] && dokku_log_fail "Please specify a valid name for the service" 19 | verify_service_name "$SERVICE" 20 | service_backup_deauth "$SERVICE" 21 | } 22 | 23 | service-backup-deauth-cmd "$@" 24 | -------------------------------------------------------------------------------- /pre-start: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/config" 3 | source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/common-functions" 4 | source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/functions" 5 | set -eo pipefail 6 | [[ $DOKKU_TRACE ]] && set -x 7 | 8 | plugin-pre-start() { 9 | declare APP="$1" 10 | local status 11 | 12 | for SERVICE in $(fn-services-list false); do 13 | if ! in_links_file "$SERVICE" "$APP"; then 14 | continue 15 | fi 16 | 17 | status="$(service_status "$SERVICE")" 18 | if [[ "$status" == "running" ]]; then 19 | continue 20 | fi 21 | 22 | if [[ "$status" == "restarting" ]]; then 23 | dokku_log_warn "$PLUGIN_SERVICE service $SERVICE is restarting and may cause issues with linked app $APP" 24 | continue 25 | fi 26 | 27 | dokku_log_warn "$PLUGIN_SERVICE service $SERVICE is not running, issuing service start" 28 | service_start "$SERVICE" 29 | done 30 | } 31 | 32 | plugin-pre-start "$@" 33 | -------------------------------------------------------------------------------- /subcommands/backup-schedule-cat: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 6 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/functions" 7 | 8 | service-backup-schedule-cat-cmd() { 9 | #E cat the contents of the configured backup cronfile for the service 10 | #E dokku $PLUGIN_COMMAND_PREFIX:backup-schedule-cat lollipop 11 | #A service, service to run command against 12 | declare desc="cat the contents of the configured backup cronfile for the service" 13 | local cmd="$PLUGIN_COMMAND_PREFIX:backup-schedule-cat" argv=("$@") 14 | [[ ${argv[0]} == "$cmd" ]] && shift 1 15 | declare SERVICE="$1" 16 | 17 | [[ -z "$SERVICE" ]] && dokku_log_fail "Please specify a valid name for the service" 18 | verify_service_name "$SERVICE" 19 | service_backup_schedule_cat "$SERVICE" 20 | } 21 | 22 | service-backup-schedule-cat-cmd "$@" 23 | -------------------------------------------------------------------------------- /subcommands/links: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 6 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/functions" 7 | 8 | service-links-cmd() { 9 | #E list all apps linked to the 'lollipop' $PLUGIN_COMMAND_PREFIX service. 10 | #E dokku $PLUGIN_COMMAND_PREFIX:links lollipop 11 | #A service, service to run command against 12 | declare desc="list all apps linked to the $PLUGIN_SERVICE service" 13 | local cmd="$PLUGIN_COMMAND_PREFIX:links" argv=("$@") 14 | [[ ${argv[0]} == "$cmd" ]] && shift 1 15 | declare SERVICE="$1" 16 | local SERVICE_ROOT="$PLUGIN_DATA_ROOT/$SERVICE" 17 | local LINKS_FILE="$SERVICE_ROOT/LINKS" 18 | 19 | [[ -z "$SERVICE" ]] && dokku_log_fail "Please specify a valid name for the service" 20 | verify_service_name "$SERVICE" 21 | service_links "$SERVICE" 22 | } 23 | 24 | service-links-cmd "$@" 25 | -------------------------------------------------------------------------------- /subcommands/backup-unschedule: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 6 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/functions" 7 | 8 | service-backup-unschedule-cmd() { 9 | #E remove the scheduled backup from cron 10 | #E dokku $PLUGIN_COMMAND_PREFIX:backup-unschedule lollipop 11 | #A service, service to run command against 12 | declare desc="unschedule the backup of the $PLUGIN_SERVICE service" 13 | local cmd="$PLUGIN_COMMAND_PREFIX:backup-unschedule" argv=("$@") 14 | [[ ${argv[0]} == "$cmd" ]] && shift 1 15 | declare SERVICE="$1" 16 | is_implemented_command "$cmd" || dokku_log_fail "Not yet implemented" 17 | 18 | [[ -z "$SERVICE" ]] && dokku_log_fail "Please specify a valid name for the service" 19 | verify_service_name "$SERVICE" 20 | service_backup_unschedule "$SERVICE" 21 | } 22 | 23 | service-backup-unschedule-cmd "$@" 24 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2020 Jose Diaz-Gonzalez 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /subcommands/backup-unset-encryption: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 6 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/functions" 7 | 8 | service-backup-unset-encryption-cmd() { 9 | #E unset the GPG encryption passphrase for backups 10 | #E dokku $PLUGIN_COMMAND_PREFIX:backup-unset-encryption lollipop 11 | #A service, service to run command against 12 | declare desc="unset encryption for future backups of the $PLUGIN_SERVICE service" 13 | local cmd="$PLUGIN_COMMAND_PREFIX:backup-unset-encryption" argv=("$@") 14 | [[ ${argv[0]} == "$cmd" ]] && shift 1 15 | declare SERVICE="$1" 16 | is_implemented_command "$cmd" || dokku_log_fail "Not yet implemented" 17 | 18 | [[ -z "$SERVICE" ]] && dokku_log_fail "Please specify a valid name for the service" 19 | verify_service_name "$SERVICE" 20 | service_backup_unset_encryption "$SERVICE" 21 | } 22 | 23 | service-backup-unset-encryption-cmd "$@" 24 | -------------------------------------------------------------------------------- /subcommands/export: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 6 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/functions" 7 | 8 | service-export-cmd() { 9 | #E by default, datastore output is exported to stdout 10 | #E dokku $PLUGIN_COMMAND_PREFIX:export lollipop 11 | #E you can redirect this output to a file 12 | #E dokku $PLUGIN_COMMAND_PREFIX:export lollipop > data.dump 13 | #A service, service to run command against 14 | declare desc="export a dump of the $PLUGIN_SERVICE service database" 15 | local cmd="$PLUGIN_COMMAND_PREFIX:export" argv=("$@") 16 | [[ ${argv[0]} == "$cmd" ]] && shift 1 17 | declare SERVICE="$1" 18 | is_implemented_command "$cmd" || dokku_log_fail "Not yet implemented" 19 | 20 | [[ -z "$SERVICE" ]] && dokku_log_fail "Please specify a valid name for the service" 21 | verify_service_name "$SERVICE" 22 | service_export "$SERVICE" 23 | } 24 | 25 | service-export-cmd "$@" 26 | -------------------------------------------------------------------------------- /pre-restore: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/config" 3 | source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/common-functions" 4 | source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/functions" 5 | set -eo pipefail 6 | [[ $DOKKU_TRACE ]] && set -x 7 | 8 | plugin-pre-restore() { 9 | declare SCHEDULER="$1" APP="$2" 10 | local status 11 | 12 | if [[ "$SCHEDULER" != "docker-local" ]]; then 13 | return 14 | fi 15 | 16 | for SERVICE in $(fn-services-list false); do 17 | if ! in_links_file "$SERVICE" "$APP"; then 18 | continue 19 | fi 20 | 21 | status="$(service_status "$SERVICE")" 22 | if [[ "$status" == "running" ]]; then 23 | continue 24 | fi 25 | 26 | if [[ "$status" == "restarting" ]]; then 27 | dokku_log_warn "$PLUGIN_SERVICE service $SERVICE is restarting and may cause issues with linked app $APP" 28 | continue 29 | fi 30 | 31 | dokku_log_warn "$PLUGIN_SERVICE service $SERVICE is not running, issuing service start" 32 | service_start "$SERVICE" 33 | done 34 | } 35 | 36 | plugin-pre-restore "$@" 37 | -------------------------------------------------------------------------------- /tests/service_create.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | load test_helper 3 | 4 | @test "($PLUGIN_COMMAND_PREFIX:create) success" { 5 | run dokku "$PLUGIN_COMMAND_PREFIX:create" l 6 | assert_contains "${lines[*]}" "container created: l" 7 | dokku --force "$PLUGIN_COMMAND_PREFIX:destroy" l 8 | } 9 | 10 | @test "($PLUGIN_COMMAND_PREFIX:create) service with dashes" { 11 | run dokku "$PLUGIN_COMMAND_PREFIX:create" service-with-dashes 12 | assert_contains "${lines[*]}" "container created: service-with-dashes" 13 | assert_contains "${lines[*]}" "dokku-$PLUGIN_COMMAND_PREFIX-service-with-dashes" 14 | assert_contains "${lines[*]}" "service_with_dashes" 15 | 16 | dokku --force "$PLUGIN_COMMAND_PREFIX:destroy" service-with-dashes 17 | } 18 | 19 | @test "($PLUGIN_COMMAND_PREFIX:create) error when there are no arguments" { 20 | run dokku "$PLUGIN_COMMAND_PREFIX:create" 21 | assert_contains "${lines[*]}" "Please specify a valid name for the service" 22 | } 23 | 24 | @test "($PLUGIN_COMMAND_PREFIX:create) error when there is an invalid name specified" { 25 | run dokku "$PLUGIN_COMMAND_PREFIX:create" d.erp 26 | assert_failure 27 | } 28 | -------------------------------------------------------------------------------- /subcommands/connect: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 6 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/functions" 7 | 8 | service-connect-cmd() { 9 | #E connect to the service via the $PLUGIN_COMMAND_PREFIX connection tool 10 | #E > NOTE: disconnecting from ssh while running this command may leave zombie processes due to moby/moby#9098 11 | #E dokku $PLUGIN_COMMAND_PREFIX:connect lollipop 12 | #A service, service to run command against 13 | declare desc="connect to the service via the $PLUGIN_COMMAND_PREFIX connection tool" 14 | local cmd="$PLUGIN_COMMAND_PREFIX:connect" argv=("$@") 15 | [[ ${argv[0]} == "$cmd" ]] && shift 1 16 | declare SERVICE="$1" 17 | is_implemented_command "$cmd" || dokku_log_fail "Not yet implemented" 18 | 19 | [[ -z "$SERVICE" ]] && dokku_log_fail "Please specify a valid name for the service" 20 | verify_service_name "$SERVICE" 21 | service_connect "$SERVICE" 22 | } 23 | 24 | service-connect-cmd "$@" 25 | -------------------------------------------------------------------------------- /subcommands/backup-unset-public-key-encryption: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 6 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/functions" 7 | 8 | service-backup-unset-public-key-encryption-cmd() { 9 | #E unset the GPG Public Key encryption for backups 10 | #E dokku $PLUGIN_COMMAND_PREFIX:backup-unset-public-key-encryption lollipop 11 | #A service, service to run command against 12 | declare desc="unset GPG Public Key encryption for future backups of the $PLUGIN_SERVICE service" 13 | local cmd="$PLUGIN_COMMAND_PREFIX:backup-unset-public-key-encryption" argv=("$@") 14 | [[ ${argv[0]} == "$cmd" ]] && shift 1 15 | declare SERVICE="$1" 16 | is_implemented_command "$cmd" || dokku_log_fail "Not yet implemented" 17 | 18 | [[ -z "$SERVICE" ]] && dokku_log_fail "Please specify a valid name for the service" 19 | verify_service_name "$SERVICE" 20 | service_backup_unset_public_key_encryption "$SERVICE" 21 | } 22 | 23 | service-backup-unset-public-key-encryption-cmd "$@" 24 | -------------------------------------------------------------------------------- /subcommands/linked: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 6 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/functions" 7 | 8 | service-linked-cmd() { 9 | #E here we check if the lollipop $PLUGIN_COMMAND_PREFIX service is linked to the 'playground' app. 10 | #E dokku $PLUGIN_COMMAND_PREFIX:linked lollipop playground 11 | #A service, service to run command against 12 | #A app, app to run command against 13 | declare desc="check if the $PLUGIN_SERVICE service is linked to an app" 14 | local cmd="$PLUGIN_COMMAND_PREFIX:linked" argv=("$@") 15 | [[ ${argv[0]} == "$cmd" ]] && shift 1 16 | declare SERVICE="$1" APP="$2" 17 | APP=${APP:="$DOKKU_APP_NAME"} 18 | 19 | [[ -z "$SERVICE" ]] && dokku_log_fail "Please specify a valid name for the service" 20 | [[ -z "$APP" ]] && dokku_log_fail "Please specify an app to run the command on" 21 | verify_app_name "$APP" 22 | verify_service_name "$SERVICE" 23 | service_is_linked "$SERVICE" "$APP" 24 | } 25 | 26 | service-linked-cmd "$@" 27 | -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM dokku/dokku:latest 2 | 3 | RUN apt-get update 4 | RUN apt-get install --no-install-recommends -y build-essential file nano && \ 5 | apt-get clean autoclean && \ 6 | apt-get autoremove --yes && \ 7 | rm -rf /var/lib/apt/lists/* && \ 8 | mkdir -p /mnt/dokku/home/dokku /mnt/dokku/var/lib/dokku/config /mnt/dokku/var/lib/dokku/data /mnt/dokku/var/lib/dokku/services && \ 9 | chown -R dokku:dokku /mnt/dokku/home/dokku /mnt/dokku/var/lib/dokku/config /mnt/dokku/var/lib/dokku/data /mnt/dokku/var/lib/dokku/services && \ 10 | echo "dokku.me" > /home/dokku/VHOST 11 | 12 | ADD https://raw.githubusercontent.com/dokku/dokku/master/tests/dhparam.pem /mnt/dokku/etc/nginx/dhparam.pem 13 | 14 | COPY .devcontainer/20_init_plugin /etc/my_init.d/20_init_plugin 15 | COPY .devcontainer/bin/ /usr/local/bin/ 16 | 17 | COPY . . 18 | 19 | RUN source /tmp/config && \ 20 | echo "export ${PLUGIN_DISABLE_PULL_VARIABLE}=true" > /tmp/.env && \ 21 | echo "export PLUGIN_NAME=${PLUGIN_COMMAND_PREFIX}" >> /tmp/.env && \ 22 | echo "export PLUGIN_VARIABLE=${PLUGIN_VARIABLE}" >> /tmp/.env 23 | 24 | RUN source /tmp/.env && \ 25 | dokku plugin:install file:///tmp --name $PLUGIN_NAME && \ 26 | make ci-dependencies 27 | -------------------------------------------------------------------------------- /tests/service_export.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | load test_helper 3 | 4 | setup() { 5 | dokku "$PLUGIN_COMMAND_PREFIX:create" l 6 | } 7 | 8 | teardown() { 9 | dokku --force "$PLUGIN_COMMAND_PREFIX:destroy" l 10 | } 11 | 12 | @test "($PLUGIN_COMMAND_PREFIX:export) error when there are no arguments" { 13 | run dokku "$PLUGIN_COMMAND_PREFIX:export" 14 | assert_contains "${lines[*]}" "Please specify a valid name for the service" 15 | } 16 | 17 | @test "($PLUGIN_COMMAND_PREFIX:export) error when service does not exist" { 18 | run dokku "$PLUGIN_COMMAND_PREFIX:export" not_existing_service 19 | assert_contains "${lines[*]}" "service not_existing_service does not exist" 20 | } 21 | 22 | @test "($PLUGIN_COMMAND_PREFIX:export) success with SSH_TTY" { 23 | if [[ -n "$GITHUB_WORKFLOW" ]]; then 24 | skip "No tty is available on Github Actions" 25 | fi 26 | export SSH_TTY=$(tty) 27 | run dokku "$PLUGIN_COMMAND_PREFIX:export" l 28 | echo "output: $output" 29 | echo "status: $status" 30 | assert_exit_status 0 31 | } 32 | 33 | @test "($PLUGIN_COMMAND_PREFIX:export) success without SSH_TTY" { 34 | unset SSH_TTY 35 | run dokku "$PLUGIN_COMMAND_PREFIX:export" l 36 | echo "output: $output" 37 | echo "status: $status" 38 | assert_exit_status 0 39 | } 40 | -------------------------------------------------------------------------------- /subcommands/enter: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 6 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/functions" 7 | 8 | service-enter-cmd() { 9 | #E a bash prompt can be opened against a running service. 10 | #E filesystem changes will not be saved to disk. 11 | #E > NOTE: disconnecting from ssh while running this command may leave zombie processes due to moby/moby#9098 12 | #E dokku $PLUGIN_COMMAND_PREFIX:enter lollipop 13 | #E you may also run a command directly against the service. 14 | #E filesystem changes will not be saved to disk. 15 | #E dokku $PLUGIN_COMMAND_PREFIX:enter lollipop touch /tmp/test 16 | #A service, service to run command against 17 | declare desc="enter or run a command in a running $PLUGIN_SERVICE service container" 18 | local cmd="$PLUGIN_COMMAND_PREFIX:enter" argv=("$@") 19 | [[ ${argv[0]} == "$cmd" ]] && shift 1 20 | declare SERVICE="$1" 21 | 22 | verify_service_name "$SERVICE" 23 | dokku_log_info1_quiet "Filesystem changes may not persist after container restarts" 24 | service_enter "$SERVICE" "${@:2}" 25 | } 26 | 27 | service-enter-cmd "$@" 28 | -------------------------------------------------------------------------------- /subcommands/unlink: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 6 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/functions" 7 | 8 | service-unlink-cmd() { 9 | #E you can unlink a $PLUGIN_COMMAND_PREFIX service 10 | #E > NOTE: this will restart your app and unset related environment variables 11 | #E dokku $PLUGIN_COMMAND_PREFIX:unlink lollipop playground 12 | #A service, service to run command against 13 | #A app, app to run command against 14 | #F -n|--no-restart "false", whether or not to restart the app on unlink (default: true) 15 | declare desc="unlink the $PLUGIN_SERVICE service from the app" 16 | local cmd="$PLUGIN_COMMAND_PREFIX:unlink" argv=("$@") 17 | [[ ${argv[0]} == "$cmd" ]] && shift 1 18 | declare SERVICE="$1" APP="$2" 19 | APP=${APP:="$DOKKU_APP_NAME"} 20 | 21 | [[ -z "$SERVICE" ]] && dokku_log_fail "Please specify a valid name for the service" 22 | [[ -z "$APP" ]] && dokku_log_fail "Please specify an app to run the command on" 23 | verify_service_name "$SERVICE" 24 | verify_app_name "$APP" 25 | service_unlink "$SERVICE" "$APP" 26 | } 27 | 28 | service-unlink-cmd "$@" 29 | -------------------------------------------------------------------------------- /tests/service_destroy.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | load test_helper 3 | 4 | @test "($PLUGIN_COMMAND_PREFIX:destroy) success with --force" { 5 | dokku "$PLUGIN_COMMAND_PREFIX:create" l 6 | run dokku --force "$PLUGIN_COMMAND_PREFIX:destroy" l 7 | assert_contains "${lines[*]}" "container deleted: l" 8 | } 9 | 10 | @test "($PLUGIN_COMMAND_PREFIX:destroy) error when there are no arguments" { 11 | run dokku "$PLUGIN_COMMAND_PREFIX:destroy" 12 | assert_contains "${lines[*]}" "Please specify a valid name for the service" 13 | } 14 | 15 | @test "($PLUGIN_COMMAND_PREFIX:destroy) error when container does not exist" { 16 | run dokku "$PLUGIN_COMMAND_PREFIX:destroy" non_existing_container 17 | assert_contains "${lines[*]}" "service non_existing_container does not exist" 18 | } 19 | 20 | @test "($PLUGIN_COMMAND_PREFIX:destroy) error when container is linked to an app" { 21 | dokku "$PLUGIN_COMMAND_PREFIX:create" l 22 | dokku apps:create app 23 | dokku "$PLUGIN_COMMAND_PREFIX:link" l app 24 | run dokku --force "$PLUGIN_COMMAND_PREFIX:destroy" l 25 | assert_contains "${lines[*]}" "Cannot delete linked service" 26 | 27 | dokku "$PLUGIN_COMMAND_PREFIX:unlink" l app 28 | run dokku --force "$PLUGIN_COMMAND_PREFIX:destroy" l 29 | assert_contains "${lines[*]}" "container deleted: l" 30 | } 31 | -------------------------------------------------------------------------------- /subcommands/expose: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 6 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/functions" 7 | 8 | service-expose-cmd() { 9 | #E expose the service on the service's normal ports, allowing access to it from the public interface (0.0.0.0) 10 | #E dokku $PLUGIN_COMMAND_PREFIX:expose lollipop ${PLUGIN_DATASTORE_PORTS[@]} 11 | #E expose the service on the service's normal ports, with the first on a specified ip address (127.0.0.1) 12 | #E dokku $PLUGIN_COMMAND_PREFIX:expose lollipop 127.0.0.1:${PLUGIN_DATASTORE_PORTS[@]} 13 | #A service, service to run command against 14 | #A ports, a list of ports to run against 15 | declare desc="expose a $PLUGIN_SERVICE service on custom host:port if provided (random port on the 0.0.0.0 interface if otherwise unspecified)" 16 | local cmd="$PLUGIN_COMMAND_PREFIX:expose" argv=("$@") 17 | [[ ${argv[0]} == "$cmd" ]] && shift 1 18 | declare SERVICE="$1" PORTS_LIST=("${@:2}") 19 | 20 | [[ -z "$SERVICE" ]] && dokku_log_fail "Please specify a valid name for the service" 21 | verify_service_name "$SERVICE" 22 | service_port_expose "$SERVICE" "${@:2}" 23 | } 24 | 25 | service-expose-cmd "$@" 26 | -------------------------------------------------------------------------------- /tests/service_logs.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | load test_helper 3 | 4 | setup() { 5 | dokku "$PLUGIN_COMMAND_PREFIX:create" l 6 | } 7 | 8 | teardown() { 9 | dokku --force "$PLUGIN_COMMAND_PREFIX:destroy" l 10 | } 11 | 12 | @test "($PLUGIN_COMMAND_PREFIX:logs) error when there are no arguments" { 13 | run dokku "$PLUGIN_COMMAND_PREFIX:logs" 14 | echo "output: $output" 15 | echo "status: $status" 16 | assert_contains "${lines[*]}" "Please specify a valid name for the service" 17 | assert_failure 18 | } 19 | 20 | @test "($PLUGIN_COMMAND_PREFIX:logs) error when service does not exist" { 21 | run dokku "$PLUGIN_COMMAND_PREFIX:logs" not_existing_service 22 | echo "output: $output" 23 | echo "status: $status" 24 | assert_contains "${lines[*]}" "service not_existing_service does not exist" 25 | assert_failure 26 | } 27 | 28 | @test "($PLUGIN_COMMAND_PREFIX:logs) success when not tailing" { 29 | skip "This may fail if there is no log output" 30 | run dokku "$PLUGIN_COMMAND_PREFIX:logs" l 31 | echo "output: $output" 32 | echo "status: $status" 33 | assert_success 34 | } 35 | 36 | @test "($PLUGIN_COMMAND_PREFIX:logs) success when tailing" { 37 | skip "This will hang as it waits for log output" 38 | run dokku "$PLUGIN_COMMAND_PREFIX:logs" l -t 39 | echo "output: $output" 40 | echo "status: $status" 41 | assert_success 42 | } 43 | -------------------------------------------------------------------------------- /tests/service_import.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | load test_helper 3 | 4 | setup() { 5 | dokku "$PLUGIN_COMMAND_PREFIX:create" l 6 | echo "CREATE DATABASE IF NOT EXISTS l;" | tee "/tmp/fake.sql" 7 | } 8 | 9 | teardown() { 10 | dokku --force "$PLUGIN_COMMAND_PREFIX:destroy" l 11 | rm -f "/tmp/fake.sql" 12 | } 13 | 14 | @test "($PLUGIN_COMMAND_PREFIX:import) error when there are no arguments" { 15 | run dokku "$PLUGIN_COMMAND_PREFIX:import" 16 | assert_contains "${lines[*]}" "Please specify a valid name for the service" 17 | assert_failure 18 | } 19 | 20 | @test "($PLUGIN_COMMAND_PREFIX:import) error when service does not exist" { 21 | run dokku "$PLUGIN_COMMAND_PREFIX:import" not_existing_service 22 | assert_contains "${lines[*]}" "service not_existing_service does not exist" 23 | assert_failure 24 | } 25 | 26 | @test "($PLUGIN_COMMAND_PREFIX:import) error when data is not provided" { 27 | if [[ -n "$GITHUB_WORKFLOW" ]]; then 28 | skip "No tty is available on Github Actions" 29 | fi 30 | run dokku "$PLUGIN_COMMAND_PREFIX:import" l 31 | assert_contains "${lines[*]}" "No data provided on stdin" 32 | assert_failure 33 | } 34 | 35 | @test "($PLUGIN_COMMAND_PREFIX:import) success" { 36 | run dokku "$PLUGIN_COMMAND_PREFIX:import" l <"/tmp/fake.sql" 37 | echo "output: $output" 38 | echo "status: $status" 39 | assert_success 40 | } 41 | -------------------------------------------------------------------------------- /subcommands/backup-set-encryption: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 6 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/functions" 7 | 8 | service-backup-set-encryption-cmd() { 9 | #E set the GPG-compatible passphrase for encrypting backups for backups 10 | #E dokku $PLUGIN_COMMAND_PREFIX:backup-set-encryption lollipop 11 | #E public key encryption will take precendence over the passphrase encryption if both types are set. 12 | #A service, service to run command against 13 | #A passphrase, a GPG-compatible passphrase 14 | declare desc="set encryption for all future backups of $PLUGIN_SERVICE service" 15 | local cmd="$PLUGIN_COMMAND_PREFIX:backup-set-encryption" argv=("$@") 16 | [[ ${argv[0]} == "$cmd" ]] && shift 1 17 | declare SERVICE="$1" PASSPHRASE="$2" 18 | is_implemented_command "$cmd" || dokku_log_fail "Not yet implemented" 19 | 20 | [[ -z "$SERVICE" ]] && dokku_log_fail "Please specify a valid name for the service" 21 | [[ -z "$PASSPHRASE" ]] && dokku_log_fail "Please specify a GPG backup passphrase" 22 | verify_service_name "$SERVICE" 23 | service_backup_set_encryption "$SERVICE" "$PASSPHRASE" 24 | } 25 | 26 | service-backup-set-encryption-cmd "$@" 27 | -------------------------------------------------------------------------------- /subcommands/logs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 6 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/functions" 7 | 8 | service-logs-cmd() { 9 | #E you can tail logs for a particular service: 10 | #E dokku $PLUGIN_COMMAND_PREFIX:logs lollipop 11 | #E by default, logs will not be tailed, but you can do this with the --tail flag: 12 | #E dokku $PLUGIN_COMMAND_PREFIX:logs lollipop --tail 13 | #E the default tail setting is to show all logs, but an initial count can also be specified 14 | #E dokku $PLUGIN_COMMAND_PREFIX:logs lollipop --tail 5 15 | #A service, service to run command against 16 | #F -t|--tail [], do not stop when end of the logs are reached and wait for additional output 17 | declare desc="print the most recent log(s) for this service" 18 | local cmd="$PLUGIN_COMMAND_PREFIX:logs" argv=("$@") 19 | [[ ${argv[0]} == "$cmd" ]] && shift 1 20 | declare SERVICE="$1" TAIL_FLAG="$2" TAIL_NUM_OPTIONAL="${3:-all}" 21 | 22 | [[ -z "$SERVICE" ]] && dokku_log_fail "Please specify a valid name for the service" 23 | verify_service_name "$SERVICE" 24 | service_logs "$SERVICE" "$TAIL_FLAG" "$TAIL_NUM_OPTIONAL" 25 | } 26 | 27 | service-logs-cmd "$@" 28 | -------------------------------------------------------------------------------- /.github/labels.yml: -------------------------------------------------------------------------------- 1 | labels: 2 | - name: bc-break 3 | color: eb6420 4 | - name: blocks release 5 | color: "000000" 6 | - name: 'difficulty: easy' 7 | color: c5def5 8 | - name: 'difficulty: hard' 9 | color: e99695 10 | - name: 'difficulty: medium' 11 | color: fef2c0 12 | - name: hacktoberfest 13 | color: b0581d 14 | - name: 'needs: documentation' 15 | color: c2e0c6 16 | - name: 'needs: more info' 17 | color: c2e0c6 18 | - name: 'needs: rebase' 19 | color: c2e0c6 20 | - name: 'needs: tests' 21 | color: c2e0c6 22 | - name: 'status: duplicate' 23 | color: cccccc 24 | - name: 'status: fix-provided' 25 | color: c5def5 26 | - name: 'status: future' 27 | color: c5def5 28 | - name: 'status: has plan' 29 | color: c5def5 30 | - name: 'status: invalid' 31 | color: cccccc 32 | - name: 'status: merge for next minor' 33 | color: c5def5 34 | - name: 'status: merge for next patch' 35 | color: c5def5 36 | - name: 'status: wontfix' 37 | color: cccccc 38 | - name: 'type: bug' 39 | color: e01b1b 40 | - name: 'type: documentation' 41 | color: 0052cc 42 | - name: 'type: enhancement' 43 | color: 09ab3c 44 | - name: 'type: question' 45 | color: cc317c 46 | - name: 'type: refactor' 47 | color: 0052cc 48 | - name: 'type: rfc' 49 | color: 0052cc 50 | - name: 'type: roadmap' 51 | color: 0052cc 52 | - name: 'type: service' 53 | color: "5319e7" 54 | - name: 'type: support' 55 | color: cc317c 56 | - name: 'type: tests' 57 | color: 0052cc 58 | -------------------------------------------------------------------------------- /subcommands/backup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 6 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/functions" 7 | 8 | service-backup-cmd() { 9 | #E backup the 'lollipop' service to the 'my-s3-bucket' bucket on AWS 10 | #E dokku $PLUGIN_COMMAND_PREFIX:backup lollipop my-s3-bucket --use-iam 11 | #E restore a backup file (assuming it was extracted via 'tar -xf backup.tgz') 12 | #E dokku $PLUGIN_COMMAND_PREFIX:import lollipop < backup-folder/export 13 | #F -u|--use-iam, use the IAM profile associated with the current server 14 | #A service, service to run command against 15 | #A bucket-name, name of the s3 bucket to upload backups to 16 | declare desc="create a backup of the $PLUGIN_SERVICE service to an existing s3 bucket" 17 | local cmd="$PLUGIN_COMMAND_PREFIX:backup" argv=("$@") 18 | [[ ${argv[0]} == "$cmd" ]] && shift 1 19 | declare SERVICE="$1" BUCKET_NAME="$2" USE_IAM_OPTIONAL_FLAG="$3" 20 | is_implemented_command "$cmd" || dokku_log_fail "Not yet implemented" 21 | 22 | [[ -z "$SERVICE" ]] && dokku_log_fail "Please specify a valid name for the service" 23 | [[ -z "$BUCKET_NAME" ]] && dokku_log_fail "Please specify an aws bucket for the backup" 24 | verify_service_name "$SERVICE" 25 | service_backup "$SERVICE" "$BUCKET_NAME" "$USE_IAM_OPTIONAL_FLAG" 26 | } 27 | 28 | service-backup-cmd "$@" 29 | -------------------------------------------------------------------------------- /subcommands/backup-set-public-key-encryption: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 6 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/functions" 7 | 8 | service-backup-set-public-key-encryption-cmd() { 9 | #E set the GPG Public Key for encrypting backups 10 | #E dokku $PLUGIN_COMMAND_PREFIX:backup-set-public-key-encryption lollipop 11 | #E this method currently requires the to be present on the keyserver 'keyserver.ubuntu.com' 12 | #A service, service to run command against 13 | #A public-key-id, a GPG Public Key ID (or fingerprint) to use for encryption. Must be uploaded to the GPG keyserver beforehand. 14 | declare desc="set GPG Public Key encryption for all future backups of $PLUGIN_SERVICE service" 15 | local cmd="$PLUGIN_COMMAND_PREFIX:backup-set-public-key-encryption" argv=("$@") 16 | [[ ${argv[0]} == "$cmd" ]] && shift 1 17 | declare SERVICE="$1" PUBLIC_KEY_ID="$2" 18 | is_implemented_command "$cmd" || dokku_log_fail "Not yet implemented" 19 | 20 | [[ -z "$SERVICE" ]] && dokku_log_fail "Please specify a valid name for the service" 21 | [[ -z "$PUBLIC_KEY_ID" ]] && dokku_log_fail "Please specify a valid GPG Public Key ID (or fingerprint)" 22 | verify_service_name "$SERVICE" 23 | service_backup_set_public_key_encryption "$SERVICE" "$PUBLIC_KEY_ID" 24 | } 25 | 26 | service-backup-set-public-key-encryption-cmd "$@" 27 | -------------------------------------------------------------------------------- /tests/service_clone.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | load test_helper 3 | 4 | setup() { 5 | dokku "$PLUGIN_COMMAND_PREFIX:create" l 6 | } 7 | 8 | teardown() { 9 | dokku --force "$PLUGIN_COMMAND_PREFIX:destroy" l 10 | } 11 | 12 | @test "($PLUGIN_COMMAND_PREFIX:clone) error when there are no arguments" { 13 | run dokku "$PLUGIN_COMMAND_PREFIX:clone" 14 | assert_contains "${lines[*]}" "Please specify a valid name for the service" 15 | assert_failure 16 | } 17 | 18 | @test "($PLUGIN_COMMAND_PREFIX:clone) error when service does not exist" { 19 | run dokku "$PLUGIN_COMMAND_PREFIX:clone" not_existing_service new_service 20 | assert_contains "${lines[*]}" "service not_existing_service does not exist" 21 | assert_failure 22 | } 23 | 24 | @test "($PLUGIN_COMMAND_PREFIX:clone) error when new service isn't provided" { 25 | run dokku "$PLUGIN_COMMAND_PREFIX:clone" l 26 | assert_contains "${lines[*]}" "Please specify a name for the new service" 27 | assert_failure 28 | } 29 | 30 | @test "($PLUGIN_COMMAND_PREFIX:clone) error when new service already exists" { 31 | dokku "$PLUGIN_COMMAND_PREFIX:create" new_service 32 | run dokku "$PLUGIN_COMMAND_PREFIX:clone" l new_service 33 | assert_contains "${lines[*]}" "Invalid service name new_service" 34 | assert_failure 35 | 36 | dokku --force "$PLUGIN_COMMAND_PREFIX:destroy" new_service 37 | } 38 | 39 | @test "($PLUGIN_COMMAND_PREFIX:clone) success" { 40 | run dokku "$PLUGIN_COMMAND_PREFIX:clone" l new_service 41 | [[ -f $PLUGIN_DATA_ROOT/new_service/ID ]] 42 | assert_contains "${lines[*]}" "Copying data from l to new_service" 43 | assert_contains "${lines[*]}" "Done" 44 | assert_success 45 | 46 | dokku --force "$PLUGIN_COMMAND_PREFIX:destroy" new_service 47 | } 48 | -------------------------------------------------------------------------------- /subcommands/backup-schedule: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 6 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/functions" 7 | 8 | service-backup-schedule-cmd() { 9 | #E schedule a backup 10 | #E > 'schedule' is a crontab expression, eg. "0 3 * * *" for each day at 3am 11 | #E dokku $PLUGIN_COMMAND_PREFIX:backup-schedule lollipop "0 3 * * *" my-s3-bucket 12 | #E schedule a backup and authenticate via iam 13 | #E dokku $PLUGIN_COMMAND_PREFIX:backup-schedule lollipop "0 3 * * *" my-s3-bucket --use-iam 14 | #F -u|--use-iam, use the IAM profile associated with the current server 15 | #A service, service to run command against 16 | #A schedule, a cron schedule to run backups on 17 | #A bucket-name, name of the s3 bucket to upload backups to 18 | declare desc="schedule a backup of the $PLUGIN_SERVICE service" 19 | local cmd="$PLUGIN_COMMAND_PREFIX:backup-schedule" argv=("$@") 20 | [[ ${argv[0]} == "$cmd" ]] && shift 1 21 | declare SERVICE="$1" SCHEDULE="$2" BUCKET_NAME="$3" USE_IAM_OPTIONAL_FLAG="$4" 22 | is_implemented_command "$cmd" || dokku_log_fail "Not yet implemented" 23 | 24 | [[ -z "$SERVICE" ]] && dokku_log_fail "Please specify a valid name for the service" 25 | [[ -z "$SCHEDULE" ]] && dokku_log_fail "Please specify a schedule for the backup" 26 | [[ -z "$BUCKET_NAME" ]] && dokku_log_fail "Please specify an aws bucket for the backup" 27 | verify_service_name "$SERVICE" 28 | service_backup_schedule "$SERVICE" "$SCHEDULE" "$BUCKET_NAME" "$USE_IAM_OPTIONAL_FLAG" 29 | } 30 | 31 | service-backup-schedule-cmd "$@" 32 | -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | _DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 3 | export MYSQL_IMAGE=${MYSQL_IMAGE:="$(awk -F '[ :]' '{print $2}' "${_DIR}/Dockerfile")"} 4 | export MYSQL_IMAGE_VERSION=${MYSQL_IMAGE_VERSION:="$(awk -F '[ :]' '{print $3}' "${_DIR}/Dockerfile")"} 5 | export MYSQL_ROOT=${MYSQL_ROOT:="$DOKKU_LIB_ROOT/services/mysql"} 6 | export DOKKU_LIB_HOST_ROOT=${DOKKU_LIB_HOST_ROOT:=$DOKKU_LIB_ROOT} 7 | export MYSQL_HOST_ROOT=${MYSQL_HOST_ROOT:="$DOKKU_LIB_HOST_ROOT/services/mysql"} 8 | 9 | export PLUGIN_UNIMPLEMENTED_SUBCOMMANDS=() 10 | export PLUGIN_COMMAND_PREFIX="mysql" 11 | export PLUGIN_CONFIG_ROOT=${PLUGIN_CONFIG_ROOT:="$DOKKU_LIB_ROOT/config/$PLUGIN_COMMAND_PREFIX"} 12 | export PLUGIN_DATA_ROOT=$MYSQL_ROOT 13 | export PLUGIN_DATA_HOST_ROOT=$MYSQL_HOST_ROOT 14 | export PLUGIN_DATASTORE_PORTS=(3306) 15 | export PLUGIN_DATASTORE_WAIT_PORT=3306 16 | export PLUGIN_DEFAULT_ALIAS="DATABASE" 17 | export PLUGIN_DISABLE_PULL=${MYSQL_DISABLE_PULL:=} 18 | export PLUGIN_DISABLE_PULL_VARIABLE="MYSQL_DISABLE_PULL" 19 | export PLUGIN_ALT_ALIAS="DOKKU_MYSQL" 20 | export PLUGIN_IMAGE=$MYSQL_IMAGE 21 | export PLUGIN_IMAGE_VERSION=$MYSQL_IMAGE_VERSION 22 | export PLUGIN_SCHEME="mysql" 23 | export PLUGIN_SERVICE="MySQL" 24 | export PLUGIN_VARIABLE="MYSQL" 25 | export PLUGIN_BASE_PATH="$PLUGIN_PATH" 26 | export PLUGIN_CONFIG_SUFFIX="config" 27 | if [[ -n $DOKKU_API_VERSION ]]; then 28 | export PLUGIN_BASE_PATH="$PLUGIN_ENABLED_PATH" 29 | fi 30 | 31 | export PLUGIN_BUSYBOX_IMAGE=${PLUGIN_BUSYBOX_IMAGE:=busybox:1.37.0-uclibc} 32 | export PLUGIN_AMBASSADOR_IMAGE=${PLUGIN_AMBASSADOR_IMAGE:=dokku/ambassador:0.8.2} 33 | export PLUGIN_S3BACKUP_IMAGE=${PLUGIN_S3BACKUP_IMAGE:=dokku/s3backup:0.18.0} 34 | export PLUGIN_WAIT_IMAGE=${PLUGIN_WAIT_IMAGE:=dokku/wait:0.9.3} 35 | 36 | export MYSQL_CONFIG_OPTIONS=${MYSQL_CONFIG_OPTIONS:=""} 37 | -------------------------------------------------------------------------------- /tests/setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eo pipefail 3 | [[ $TRACE ]] && set -x 4 | sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 762E3157 5 | echo "deb http://nginx.org/packages/ubuntu $(lsb_release -cs) nginx" | sudo tee /etc/apt/sources.list.d/nginx.list 6 | curl -fsSL https://nginx.org/keys/nginx_signing.key | sudo apt-key add - 7 | 8 | sudo mkdir -p /etc/nginx 9 | sudo curl https://raw.githubusercontent.com/dokku/dokku/master/tests/dhparam.pem -o /etc/nginx/dhparam.pem 10 | 11 | echo "dokku dokku/skip_key_file boolean true" | sudo debconf-set-selections 12 | wget https://raw.githubusercontent.com/dokku/dokku/master/bootstrap.sh 13 | if [[ "$DOKKU_VERSION" == "master" ]]; then 14 | sudo bash bootstrap.sh 15 | else 16 | sudo DOKKU_TAG="$DOKKU_VERSION" bash bootstrap.sh 17 | fi 18 | echo "Dokku version $DOKKU_VERSION" 19 | 20 | export DOKKU_LIB_ROOT="/var/lib/dokku" 21 | export DOKKU_PLUGINS_ROOT="$DOKKU_LIB_ROOT/plugins/available" 22 | pushd "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")" >/dev/null 23 | source "config" 24 | popd >/dev/null 25 | sudo rm -rf "$DOKKU_PLUGINS_ROOT/$PLUGIN_COMMAND_PREFIX" 26 | sudo mkdir -p "$DOKKU_PLUGINS_ROOT/$PLUGIN_COMMAND_PREFIX" "$DOKKU_PLUGINS_ROOT/$PLUGIN_COMMAND_PREFIX/subcommands" "$DOKKU_PLUGINS_ROOT/$PLUGIN_COMMAND_PREFIX/scripts" "$DOKKU_PLUGINS_ROOT/$PLUGIN_COMMAND_PREFIX/templates" 27 | sudo find ./ -maxdepth 1 -type f -exec cp '{}' "$DOKKU_PLUGINS_ROOT/$PLUGIN_COMMAND_PREFIX" \; 28 | [[ -d "./scripts" ]] && sudo find ./scripts -maxdepth 1 -type f -exec cp '{}' "$DOKKU_PLUGINS_ROOT/$PLUGIN_COMMAND_PREFIX/scripts" \; 29 | [[ -d "./subcommands" ]] && sudo find ./subcommands -maxdepth 1 -type f -exec cp '{}' "$DOKKU_PLUGINS_ROOT/$PLUGIN_COMMAND_PREFIX/subcommands" \; 30 | [[ -d "./templates" ]] && sudo find ./templates -maxdepth 1 -type f -exec cp '{}' "$DOKKU_PLUGINS_ROOT/$PLUGIN_COMMAND_PREFIX/templates" \; 31 | sudo mkdir -p "$PLUGIN_CONFIG_ROOT" "$PLUGIN_DATA_ROOT" 32 | sudo dokku plugin:enable "$PLUGIN_COMMAND_PREFIX" 33 | sudo dokku plugin:install 34 | -------------------------------------------------------------------------------- /tests/test_helper.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | export DOKKU_LIB_ROOT="/var/lib/dokku" 3 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" 4 | 5 | flunk() { 6 | { 7 | if [ "$#" -eq 0 ]; then 8 | cat - 9 | else 10 | echo "$*" 11 | fi 12 | } 13 | return 1 14 | } 15 | 16 | assert_equal() { 17 | if [ "$1" != "$2" ]; then 18 | { 19 | echo "expected: $1" 20 | echo "actual: $2" 21 | } | flunk 22 | fi 23 | } 24 | 25 | # ShellCheck doesn't know about $status from Bats 26 | # shellcheck disable=SC2154 27 | assert_exit_status() { 28 | assert_equal "$1" "$status" 29 | } 30 | 31 | # ShellCheck doesn't know about $status from Bats 32 | # shellcheck disable=SC2154 33 | # shellcheck disable=SC2120 34 | assert_success() { 35 | if [ "$status" -ne 0 ]; then 36 | flunk "command failed with exit status $status" 37 | elif [ "$#" -gt 0 ]; then 38 | assert_output "$1" 39 | fi 40 | } 41 | 42 | assert_failure() { 43 | if [[ "$status" -eq 0 ]]; then 44 | flunk "expected failed exit status" 45 | elif [[ "$#" -gt 0 ]]; then 46 | assert_output "$1" 47 | fi 48 | } 49 | 50 | assert_exists() { 51 | if [ ! -f "$1" ]; then 52 | flunk "expected file to exist: $1" 53 | fi 54 | } 55 | 56 | assert_contains() { 57 | if [[ "$1" != *"$2"* ]]; then 58 | flunk "expected $2 to be in: $1" 59 | fi 60 | } 61 | 62 | # ShellCheck doesn't know about $output from Bats 63 | # shellcheck disable=SC2154 64 | assert_output() { 65 | local expected 66 | if [ $# -eq 0 ]; then 67 | expected="$(cat -)" 68 | else 69 | expected="$1" 70 | fi 71 | assert_equal "$expected" "$output" 72 | } 73 | 74 | # ShellCheck doesn't know about $output from Bats 75 | # shellcheck disable=SC2154 76 | assert_output_contains() { 77 | local input="$output" 78 | local expected="$1" 79 | local count="${2:-1}" 80 | local found=0 81 | until [ "${input/$expected/}" = "$input" ]; do 82 | input="${input/$expected/}" 83 | found=$((found + 1)) 84 | done 85 | assert_equal "$count" "$found" 86 | } 87 | -------------------------------------------------------------------------------- /subcommands/set: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 6 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions" 7 | source "$(cd "$(dirname "$(dirname "${BASH_SOURCE[0]}")")" && pwd)/common-functions" 8 | 9 | service-set-cmd() { 10 | #E set the network to attach after the service container is started 11 | #E dokku $PLUGIN_COMMAND_PREFIX:set lollipop post-create-network custom-network 12 | #E set multiple networks 13 | #E dokku $PLUGIN_COMMAND_PREFIX:set lollipop post-create-network custom-network,other-network 14 | #E unset the post-create-network value 15 | #E dokku $PLUGIN_COMMAND_PREFIX:set lollipop post-create-network 16 | #A service, service to run command against 17 | #A key, property name to set 18 | #A value, optional property value to set or empty to unset key 19 | declare desc="set or clear a property for a service" 20 | local cmd="$PLUGIN_COMMAND_PREFIX:set" argv=("$@") 21 | [[ ${argv[0]} == "$cmd" ]] && shift 1 22 | declare SERVICE="$1" KEY="$2" VALUE="$3" 23 | local VALID_KEYS=("initial-network" "post-create-network" "post-start-network") 24 | verify_service_name "$SERVICE" 25 | 26 | [[ -z "$KEY" ]] && dokku_log_fail "No key specified" 27 | 28 | if ! fn-in-array "$KEY" "${VALID_KEYS[@]}"; then 29 | dokku_log_fail "Invalid key specified, valid keys include: initial-network, post-create-network, post-start-network" 30 | fi 31 | 32 | if [[ -n "$VALUE" ]]; then 33 | dokku_log_info2_quiet "Setting ${KEY} to ${VALUE}" 34 | fn-plugin-property-write "$PLUGIN_COMMAND_PREFIX" "$SERVICE" "$KEY" "$VALUE" 35 | else 36 | dokku_log_info2_quiet "Unsetting ${KEY}" 37 | if [[ "$KEY" == "rev-env-var" ]]; then 38 | fn-plugin-property-write "$PLUGIN_COMMAND_PREFIX" "$SERVICE" "$KEY" "$VALUE" 39 | else 40 | fn-plugin-property-delete "$PLUGIN_COMMAND_PREFIX" "$SERVICE" "$KEY" 41 | fi 42 | fi 43 | } 44 | 45 | service-set-cmd "$@" 46 | -------------------------------------------------------------------------------- /tests/service_expose.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | load test_helper 3 | 4 | setup() { 5 | dokku "$PLUGIN_COMMAND_PREFIX:create" ls 6 | } 7 | 8 | teardown() { 9 | dokku --force "$PLUGIN_COMMAND_PREFIX:destroy" ls 10 | } 11 | 12 | @test "($PLUGIN_COMMAND_PREFIX:expose) error when there are no arguments" { 13 | run dokku "$PLUGIN_COMMAND_PREFIX:expose" 14 | echo "output: $output" 15 | echo "status: $status" 16 | assert_failure 17 | assert_contains "${lines[*]}" "Please specify a valid name for the service" 18 | } 19 | 20 | @test "($PLUGIN_COMMAND_PREFIX:expose) error when service does not exist" { 21 | run dokku "$PLUGIN_COMMAND_PREFIX:expose" not_existing_service 22 | echo "output: $output" 23 | echo "status: $status" 24 | assert_failure 25 | assert_contains "${lines[*]}" "service not_existing_service does not exist" 26 | } 27 | 28 | @test "($PLUGIN_COMMAND_PREFIX:expose) error when already exposed" { 29 | run dokku "$PLUGIN_COMMAND_PREFIX:expose" ls 30 | echo "output: $output" 31 | echo "status: $status" 32 | assert_success 33 | 34 | run dokku "$PLUGIN_COMMAND_PREFIX:expose" ls 35 | echo "output: $output" 36 | echo "status: $status" 37 | assert_failure 38 | assert_contains "${lines[*]}" "Service ls already exposed on port(s)" 39 | 40 | run sudo rm "$PLUGIN_DATA_ROOT/ls/PORT" 41 | echo "output: $output" 42 | echo "status: $status" 43 | assert_success 44 | 45 | run dokku "$PLUGIN_COMMAND_PREFIX:expose" ls 46 | echo "output: $output" 47 | echo "status: $status" 48 | assert_success 49 | assert_contains "${lines[*]}" "Service ls has an untracked expose container, removing" 50 | } 51 | 52 | @test "($PLUGIN_COMMAND_PREFIX:expose) success when not providing custom ports" { 53 | run dokku "$PLUGIN_COMMAND_PREFIX:expose" ls 54 | echo "output: $output" 55 | echo "status: $status" 56 | assert_success 57 | [[ "${lines[*]}" =~ exposed\ on\ port\(s\)\ \[container\-\>host\]\:\ [[:digit:]]+ ]] 58 | } 59 | 60 | @test "($PLUGIN_COMMAND_PREFIX:expose) success when providing custom ports" { 61 | run dokku "$PLUGIN_COMMAND_PREFIX:expose" ls 4242 62 | echo "output: $output" 63 | echo "status: $status" 64 | assert_success 65 | assert_contains "${lines[*]}" "exposed on port(s) [container->host]: 3306->4242" 66 | } 67 | -------------------------------------------------------------------------------- /subcommands/promote: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 6 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/functions" 7 | 8 | service-promote-cmd() { 9 | #E if you have a $PLUGIN_COMMAND_PREFIX service linked to an app and try to link another $PLUGIN_COMMAND_PREFIX service 10 | #E another link environment variable will be generated automatically: 11 | #E 12 | #E DOKKU_${PLUGIN_DEFAULT_ALIAS}_BLUE_URL=${PLUGIN_SCHEME}://other_service:ANOTHER_PASSWORD@dokku-${PLUGIN_COMMAND_PREFIX}-other-service:${PLUGIN_DATASTORE_PORTS[0]}/other_service 13 | #E 14 | #E you can promote the new service to be the primary one 15 | #E > NOTE: this will restart your app 16 | #E dokku $PLUGIN_COMMAND_PREFIX:promote other_service playground 17 | #E this will replace ${PLUGIN_DEFAULT_ALIAS}_URL with the url from other_service and generate 18 | #E another environment variable to hold the previous value if necessary. 19 | #E you could end up with the following for example: 20 | #E 21 | #E ${PLUGIN_DEFAULT_ALIAS}_URL=${PLUGIN_SCHEME}://other_service:ANOTHER_PASSWORD@dokku-${PLUGIN_COMMAND_PREFIX}-other-service:${PLUGIN_DATASTORE_PORTS[0]}/other_service 22 | #E DOKKU_${PLUGIN_DEFAULT_ALIAS}_BLUE_URL=${PLUGIN_SCHEME}://other_service:ANOTHER_PASSWORD@dokku-${PLUGIN_COMMAND_PREFIX}-other-service:${PLUGIN_DATASTORE_PORTS[0]}/other_service 23 | #E DOKKU_${PLUGIN_DEFAULT_ALIAS}_SILVER_URL=${PLUGIN_SCHEME}://lollipop:SOME_PASSWORD@dokku-${PLUGIN_COMMAND_PREFIX}-lollipop:${PLUGIN_DATASTORE_PORTS[0]}/lollipop 24 | #A service, service to run command against 25 | #A app, app to run command against 26 | declare desc="promote service as ${PLUGIN_DEFAULT_ALIAS}_URL in " 27 | local cmd="$PLUGIN_COMMAND_PREFIX:promote" argv=("$@") 28 | [[ ${argv[0]} == "$cmd" ]] && shift 1 29 | declare SERVICE="$1" APP="$2" 30 | APP=${APP:="$DOKKU_APP_NAME"} 31 | 32 | [[ -z "$SERVICE" ]] && dokku_log_fail "Please specify a valid name for the service" 33 | [[ -z "$APP" ]] && dokku_log_fail "Please specify an app to run the command on" 34 | verify_service_name "$SERVICE" 35 | verify_app_name "$APP" 36 | service_promote "$SERVICE" "$APP" 37 | } 38 | 39 | service-promote-cmd "$@" 40 | -------------------------------------------------------------------------------- /subcommands/backup-auth: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 6 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/functions" 7 | 8 | service-backup-auth-cmd() { 9 | #E setup s3 backup authentication 10 | #E dokku $PLUGIN_COMMAND_PREFIX:backup-auth lollipop AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY 11 | #E setup s3 backup authentication with different region 12 | #E dokku $PLUGIN_COMMAND_PREFIX:backup-auth lollipop AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_REGION 13 | #E setup s3 backup authentication with different signature version and endpoint 14 | #E dokku $PLUGIN_COMMAND_PREFIX:backup-auth lollipop AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_REGION AWS_SIGNATURE_VERSION ENDPOINT_URL 15 | #E more specific example for minio auth 16 | #E dokku $PLUGIN_COMMAND_PREFIX:backup-auth lollipop MINIO_ACCESS_KEY_ID MINIO_SECRET_ACCESS_KEY us-east-1 s3v4 https://YOURMINIOSERVICE 17 | #A service, service to run command against 18 | #A access-key-id, an amazon AWS_ACCESS_KEY_ID 19 | #A aws-secret-access-key, an amazon AWS_SECRET_ACCESS_KEY 20 | #A aws-default-region, (optional) a valid amazon S3 region 21 | #A aws-signature-version, (optional) the AWS signature version to use when signing S3 requests 22 | #A endpoint-url, (optional) an aws endpoint to upload to 23 | declare desc="set up authentication for backups on the $PLUGIN_SERVICE service" 24 | local cmd="$PLUGIN_COMMAND_PREFIX:backup-auth" argv=("$@") 25 | [[ ${argv[0]} == "$cmd" ]] && shift 1 26 | declare SERVICE="$1" AWS_ACCESS_KEY_ID="$2" AWS_SECRET_ACCESS_KEY="$3" AWS_DEFAULT_REGION="$4" AWS_SIGNATURE_VERSION="$5" ENDPOINT_URL="$6" 27 | is_implemented_command "$cmd" || dokku_log_fail "Not yet implemented" 28 | 29 | [[ -z "$SERVICE" ]] && dokku_log_fail "Please specify a valid name for the service" 30 | [[ -z "$AWS_ACCESS_KEY_ID" ]] && dokku_log_fail "Please specify an aws access key id" 31 | [[ -z "$AWS_SECRET_ACCESS_KEY" ]] && dokku_log_fail "Please specify an aws secret access key" 32 | verify_service_name "$SERVICE" 33 | service_backup_auth "$SERVICE" "$AWS_ACCESS_KEY_ID" "$AWS_SECRET_ACCESS_KEY" "$AWS_DEFAULT_REGION" "$AWS_SIGNATURE_VERSION" "$ENDPOINT_URL" 34 | } 35 | 36 | service-backup-auth-cmd "$@" 37 | -------------------------------------------------------------------------------- /subcommands/create: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 6 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/functions" 7 | 8 | service-create-cmd() { 9 | #E create a $PLUGIN_COMMAND_PREFIX service named lollipop 10 | #E dokku $PLUGIN_COMMAND_PREFIX:create lollipop 11 | #E you can also specify the image and image version to use for the service. 12 | #E it *must* be compatible with the ${PLUGIN_IMAGE} image. 13 | #E export ${PLUGIN_VARIABLE}_IMAGE="${PLUGIN_IMAGE}" 14 | #E export ${PLUGIN_VARIABLE}_IMAGE_VERSION="${PLUGIN_IMAGE_VERSION}" 15 | #E dokku $PLUGIN_COMMAND_PREFIX:create lollipop 16 | #E you can also specify custom environment variables to start 17 | #E the ${PLUGIN_COMMAND_PREFIX} service in semicolon-separated form. 18 | #E export ${PLUGIN_VARIABLE}_CUSTOM_ENV="USER=alpha;HOST=beta" 19 | #E dokku $PLUGIN_COMMAND_PREFIX:create lollipop 20 | #A service, service to run command against 21 | #F -c|--config-options "--args --go=here", extra arguments to pass to the container create command 22 | #F -C|--custom-env "USER=alpha;HOST=beta", semi-colon delimited environment variables to start the service with 23 | #F -i|--image IMAGE, the image name to start the service with 24 | #F -I|--image-version IMAGE_VERSION, the image version to start the service with 25 | #F -m|--memory MEMORY, container memory limit in megabytes (default: unlimited) 26 | #F -N|--initial-network INITIAL_NETWORK, the initial network to attach the service to 27 | #F -p|--password PASSWORD, override the user-level service password 28 | #F -P|--post-create-network NETWORKS, a comma-separated list of networks to attach the service container to after service creation 29 | #F -r|--root-password PASSWORD, override the root-level service password 30 | #F -S|--post-start-network NETWORKS, a comma-separated list of networks to attach the service container to after service start 31 | #F -s|--shm-size SHM_SIZE, override shared memory size for $PLUGIN_COMMAND_PREFIX docker container 32 | declare desc="create a $PLUGIN_SERVICE service" 33 | local cmd="$PLUGIN_COMMAND_PREFIX:create" argv=("$@") 34 | [[ ${argv[0]} == "$cmd" ]] && shift 1 35 | declare SERVICE="$1" CREATE_FLAGS_LIST=("${@:2}") 36 | 37 | service_create "$SERVICE" "${@:2}" 38 | } 39 | 40 | service-create-cmd "$@" 41 | -------------------------------------------------------------------------------- /subcommands/info: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 6 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/functions" 7 | 8 | service-info-cmd() { 9 | #E get connection information as follows: 10 | #E dokku $PLUGIN_COMMAND_PREFIX:info lollipop 11 | #E you can also retrieve a specific piece of service info via flags: 12 | #E dokku $PLUGIN_COMMAND_PREFIX:info lollipop --config-dir 13 | #E dokku $PLUGIN_COMMAND_PREFIX:info lollipop --data-dir 14 | #E dokku $PLUGIN_COMMAND_PREFIX:info lollipop --dsn 15 | #E dokku $PLUGIN_COMMAND_PREFIX:info lollipop --exposed-ports 16 | #E dokku $PLUGIN_COMMAND_PREFIX:info lollipop --id 17 | #E dokku $PLUGIN_COMMAND_PREFIX:info lollipop --internal-ip 18 | #E dokku $PLUGIN_COMMAND_PREFIX:info lollipop --initial-network 19 | #E dokku $PLUGIN_COMMAND_PREFIX:info lollipop --links 20 | #E dokku $PLUGIN_COMMAND_PREFIX:info lollipop --post-create-network 21 | #E dokku $PLUGIN_COMMAND_PREFIX:info lollipop --post-start-network 22 | #E dokku $PLUGIN_COMMAND_PREFIX:info lollipop --service-root 23 | #E dokku $PLUGIN_COMMAND_PREFIX:info lollipop --status 24 | #E dokku $PLUGIN_COMMAND_PREFIX:info lollipop --version 25 | #A service, service to run command against 26 | #F --config-dir, show the service configuration directory 27 | #F --data-dir, show the service data directory 28 | #F --dsn, show the service DSN 29 | #F --exposed-ports, show service exposed ports 30 | #F --id, show the service container id 31 | #F --internal-ip, show the service internal ip 32 | #F --initial-network, show the initial network being connected to 33 | #F --links, show the service app links 34 | #F --post-create-network, show the networks to attach to after service container creation 35 | #F --post-start-network, show the networks to attach to after service container start 36 | #F --service-root, show the service root directory 37 | #F --status, show the service running status 38 | #F --version, show the service image version 39 | declare desc="print the service information" 40 | local cmd="$PLUGIN_COMMAND_PREFIX:info" argv=("$@") 41 | [[ ${argv[0]} == "$cmd" ]] && shift 1 42 | declare SERVICE="$1" INFO_FLAG="$2" 43 | 44 | [[ -z "$SERVICE" ]] && dokku_log_fail "Please specify a valid name for the service" 45 | verify_service_name "$SERVICE" 46 | service_info "$SERVICE" "$INFO_FLAG" 47 | } 48 | 49 | service-info-cmd "$@" 50 | -------------------------------------------------------------------------------- /subcommands/destroy: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 6 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/functions" 7 | 8 | service-destroy-cmd() { 9 | #E destroy the service, it's data, and the running container 10 | #E dokku $PLUGIN_COMMAND_PREFIX:destroy lollipop 11 | #A service, service to run command against 12 | #F -f|--force, force destroy without asking for confirmation 13 | declare desc="delete the $PLUGIN_SERVICE service/data/container if there are no links left" 14 | local cmd="$PLUGIN_COMMAND_PREFIX:destroy" argv=("$@") 15 | [[ ${argv[0]} == "$cmd" ]] && shift 1 16 | declare SERVICE="$1" FORCE_FLAG="$2" 17 | 18 | [[ -z "$SERVICE" ]] && dokku_log_fail "Please specify a valid name for the service" 19 | verify_service_name "$SERVICE" 20 | SERVICE_ROOT="$PLUGIN_DATA_ROOT/$SERVICE" 21 | LINKS_FILE="$SERVICE_ROOT/LINKS" 22 | SERVICE_HOST_ROOT="$PLUGIN_DATA_HOST_ROOT/$SERVICE" 23 | SERVICE_NAME="$(get_service_name "$SERVICE")" 24 | 25 | [[ -s "$LINKS_FILE" ]] && dokku_log_fail "Cannot delete linked service" 26 | 27 | if [[ "$FORCE_FLAG" == "force" ]] || [[ "$FORCE_FLAG" == "-f" ]] || [[ "$FORCE_FLAG" == "--force" ]]; then 28 | DOKKU_APPS_FORCE_DELETE=1 29 | fi 30 | if [[ -z "$DOKKU_APPS_FORCE_DELETE" ]]; then 31 | dokku_log_warn "WARNING: Potentially Destructive Action" 32 | dokku_log_warn "This command will destroy $SERVICE $PLUGIN_SERVICE service." 33 | dokku_log_warn "To proceed, type \"$SERVICE\"" 34 | echo "" 35 | 36 | read -rp "> " service_name 37 | if [[ "$service_name" != "$SERVICE" ]]; then 38 | dokku_log_warn "Confirmation did not match $SERVICE. Aborted." 39 | exit 1 40 | fi 41 | fi 42 | 43 | dokku_log_info2_quiet "Deleting $SERVICE" 44 | plugn trigger service-action pre-delete "$PLUGIN_COMMAND_PREFIX" "$SERVICE" 45 | service_backup_unschedule "$SERVICE" 46 | service_container_rm "$SERVICE" 47 | 48 | dokku_log_verbose_quiet "Removing data" 49 | "$DOCKER_BIN" container run --rm -v "$SERVICE_HOST_ROOT/data:/data" -v "$SERVICE_HOST_ROOT/$PLUGIN_CONFIG_SUFFIX:/config" "$PLUGIN_BUSYBOX_IMAGE" chmod 777 -R /config /data 50 | rm -rf "$SERVICE_ROOT" 51 | 52 | fn-plugin-property-destroy "$PLUGIN_COMMAND_PREFIX" "$SERVICE" 53 | 54 | plugn trigger service-action post-delete "$PLUGIN_COMMAND_PREFIX" "$SERVICE" 55 | dokku_log_info2 "$PLUGIN_SERVICE container deleted: $SERVICE" 56 | } 57 | 58 | service-destroy-cmd "$@" 59 | -------------------------------------------------------------------------------- /tests/service_info.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | load test_helper 3 | 4 | setup() { 5 | dokku "$PLUGIN_COMMAND_PREFIX:create" l 6 | } 7 | 8 | teardown() { 9 | dokku --force "$PLUGIN_COMMAND_PREFIX:destroy" l 10 | } 11 | 12 | @test "($PLUGIN_COMMAND_PREFIX:info) error when there are no arguments" { 13 | run dokku "$PLUGIN_COMMAND_PREFIX:info" 14 | assert_contains "${lines[*]}" "Please specify a valid name for the service" 15 | } 16 | 17 | @test "($PLUGIN_COMMAND_PREFIX:info) error when service does not exist" { 18 | run dokku "$PLUGIN_COMMAND_PREFIX:info" not_existing_service 19 | assert_contains "${lines[*]}" "service not_existing_service does not exist" 20 | } 21 | 22 | @test "($PLUGIN_COMMAND_PREFIX:info) success" { 23 | run dokku "$PLUGIN_COMMAND_PREFIX:info" l 24 | local password="$(sudo cat "$PLUGIN_DATA_ROOT/l/PASSWORD")" 25 | assert_contains "${lines[*]}" "mysql://mysql:$password@dokku-mysql-l:3306/l" 26 | } 27 | 28 | @test "($PLUGIN_COMMAND_PREFIX:info) replaces underscores by dash in hostname" { 29 | dokku "$PLUGIN_COMMAND_PREFIX:create" test_with_underscores 30 | run dokku "$PLUGIN_COMMAND_PREFIX:info" test_with_underscores 31 | local password="$(sudo cat "$PLUGIN_DATA_ROOT/test_with_underscores/PASSWORD")" 32 | assert_contains "${lines[*]}" "mysql://mysql:$password@dokku-mysql-test-with-underscores:3306/test_with_underscores" 33 | dokku --force "$PLUGIN_COMMAND_PREFIX:destroy" test_with_underscores 34 | } 35 | 36 | @test "($PLUGIN_COMMAND_PREFIX:info) success with flag" { 37 | run dokku "$PLUGIN_COMMAND_PREFIX:info" l --dsn 38 | local password="$(sudo cat "$PLUGIN_DATA_ROOT/l/PASSWORD")" 39 | assert_output "mysql://mysql:$password@dokku-mysql-l:3306/l" 40 | 41 | run dokku "$PLUGIN_COMMAND_PREFIX:info" l --config-dir 42 | assert_success 43 | 44 | run dokku "$PLUGIN_COMMAND_PREFIX:info" l --data-dir 45 | assert_success 46 | 47 | run dokku "$PLUGIN_COMMAND_PREFIX:info" l --dsn 48 | assert_success 49 | 50 | run dokku "$PLUGIN_COMMAND_PREFIX:info" l --exposed-ports 51 | assert_success 52 | 53 | run dokku "$PLUGIN_COMMAND_PREFIX:info" l --id 54 | assert_success 55 | 56 | run dokku "$PLUGIN_COMMAND_PREFIX:info" l --internal-ip 57 | assert_success 58 | 59 | run dokku "$PLUGIN_COMMAND_PREFIX:info" l --links 60 | assert_success 61 | 62 | run dokku "$PLUGIN_COMMAND_PREFIX:info" l --service-root 63 | assert_success 64 | 65 | run dokku "$PLUGIN_COMMAND_PREFIX:info" l --status 66 | assert_success 67 | 68 | run dokku "$PLUGIN_COMMAND_PREFIX:info" l --version 69 | assert_success 70 | } 71 | 72 | @test "($PLUGIN_COMMAND_PREFIX:info) error when invalid flag" { 73 | run dokku "$PLUGIN_COMMAND_PREFIX:info" l --invalid-flag 74 | assert_failure 75 | } 76 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: CI 3 | 4 | # yamllint disable-line rule:truthy 5 | on: 6 | pull_request: 7 | branches: 8 | - "*" 9 | push: 10 | branches: 11 | - master 12 | 13 | concurrency: 14 | group: build-${{ github.event.pull_request.number || github.ref }} 15 | cancel-in-progress: true 16 | 17 | jobs: 18 | unit-tests-master: 19 | name: unit-tests 20 | runs-on: ubuntu-24.04 21 | env: 22 | DOKKU_VERSION: master 23 | 24 | steps: 25 | - uses: actions/checkout@v6 26 | with: 27 | fetch-depth: 0 28 | 29 | - uses: actions/setup-python@v6 30 | with: 31 | python-version: "3.13" 32 | 33 | - run: make setup 34 | 35 | - run: sudo sysctl -w vm.max_map_count=262144 36 | 37 | - run: | 38 | git fetch -q origin master 39 | changed=$(git --no-pager diff --name-only $GITHUB_SHA..origin/master) 40 | if [ $changed = "Dockerfile" ]; then 41 | echo "Please run 'make generate' to update the image version in the README.md" 42 | else 43 | make generate 44 | if ! git diff --quiet README.md; then 45 | echo "Please run 'make generate'" 46 | git status --short 47 | git --no-pager diff README.md 48 | exit 1 49 | fi 50 | fi 51 | 52 | - run: make test 53 | 54 | - uses: actions/upload-artifact@v6 55 | if: failure() 56 | with: 57 | name: tmp/test-results 58 | path: test-results 59 | 60 | unit-tests-0_19_0: 61 | name: unit-tests-0.19.0 62 | runs-on: ubuntu-24.04 63 | env: 64 | DOKKU_TAG: v0.19.0 65 | 66 | steps: 67 | - uses: actions/checkout@v6 68 | with: 69 | fetch-depth: 0 70 | 71 | - uses: actions/setup-python@v6 72 | with: 73 | python-version: "3.13" 74 | 75 | - run: make setup 76 | 77 | - run: sudo sysctl -w vm.max_map_count=262144 78 | 79 | - run: | 80 | git fetch -q origin master 81 | changed=$(git --no-pager diff --name-only $GITHUB_SHA..origin/master) 82 | if [ $changed = "Dockerfile" ]; then 83 | echo "Please run 'make generate' to update the image version in the README.md" 84 | else 85 | make generate 86 | if ! git diff --quiet README.md; then 87 | echo "Please run 'make generate'" 88 | git status --short 89 | git --no-pager diff README.md 90 | exit 1 91 | fi 92 | fi 93 | 94 | - run: make test 95 | 96 | - uses: actions/upload-artifact@v6 97 | if: failure() 98 | with: 99 | name: tmp/test-results 100 | path: test-results 101 | -------------------------------------------------------------------------------- /tests/service_unlink.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | load test_helper 3 | 4 | setup() { 5 | dokku apps:create my-app 6 | dokku "$PLUGIN_COMMAND_PREFIX:create" ls 7 | } 8 | 9 | teardown() { 10 | dokku --force "$PLUGIN_COMMAND_PREFIX:destroy" ls 11 | dokku --force apps:destroy my-app 12 | } 13 | 14 | @test "($PLUGIN_COMMAND_PREFIX:unlink) error when there are no arguments" { 15 | run dokku "$PLUGIN_COMMAND_PREFIX:unlink" 16 | assert_contains "${lines[*]}" "Please specify a valid name for the service" 17 | } 18 | 19 | @test "($PLUGIN_COMMAND_PREFIX:unlink) error when the app argument is missing" { 20 | run dokku "$PLUGIN_COMMAND_PREFIX:unlink" ls 21 | assert_contains "${lines[*]}" "Please specify an app to run the command on" 22 | } 23 | 24 | @test "($PLUGIN_COMMAND_PREFIX:unlink) error when the app does not exist" { 25 | run dokku "$PLUGIN_COMMAND_PREFIX:unlink" ls not_existing_app 26 | assert_contains "${lines[*]}" "App not_existing_app does not exist" 27 | } 28 | 29 | @test "($PLUGIN_COMMAND_PREFIX:unlink) error when the service does not exist" { 30 | run dokku "$PLUGIN_COMMAND_PREFIX:unlink" not_existing_service my-app 31 | assert_contains "${lines[*]}" "service not_existing_service does not exist" 32 | } 33 | 34 | @test "($PLUGIN_COMMAND_PREFIX:unlink) error when service not linked to app" { 35 | run dokku "$PLUGIN_COMMAND_PREFIX:unlink" ls my-app 36 | assert_contains "${lines[*]}" "Not linked to app my-app" 37 | } 38 | 39 | @test "($PLUGIN_COMMAND_PREFIX:unlink) removes link from docker-options" { 40 | dokku "$PLUGIN_COMMAND_PREFIX:link" ls my-app >&2 41 | dokku "$PLUGIN_COMMAND_PREFIX:unlink" ls my-app 42 | 43 | check_value="Docker options build: Docker options deploy: --restart=on-failure:10 Docker options run:" 44 | options=$(dokku --quiet docker-options:report my-app | xargs) 45 | assert_equal "$options" "$check_value" 46 | } 47 | 48 | @test "($PLUGIN_COMMAND_PREFIX:unlink) unsets config url from app" { 49 | dokku "$PLUGIN_COMMAND_PREFIX:link" ls my-app >&2 50 | dokku "$PLUGIN_COMMAND_PREFIX:unlink" ls my-app 51 | config=$(dokku config:get my-app DATABASE_URL || true) 52 | assert_equal "$config" "" 53 | } 54 | 55 | @test "($PLUGIN_COMMAND_PREFIX:unlink) respects --no-restart" { 56 | run dokku "$PLUGIN_COMMAND_PREFIX:link" ls my-app 57 | echo "output: $output" 58 | echo "status: $status" 59 | assert_success 60 | 61 | run dokku "$PLUGIN_COMMAND_PREFIX:unlink" ls my-app 62 | echo "output: $output" 63 | echo "status: $status" 64 | assert_output_contains "Skipping restart of linked app" 0 65 | assert_success 66 | 67 | run dokku "$PLUGIN_COMMAND_PREFIX:link" ls my-app 68 | echo "output: $output" 69 | echo "status: $status" 70 | assert_success 71 | 72 | run dokku "$PLUGIN_COMMAND_PREFIX:unlink" ls my-app --no-restart 73 | echo "output: $output" 74 | echo "status: $status" 75 | assert_output_contains "Skipping restart of linked app" 76 | assert_success 77 | } 78 | -------------------------------------------------------------------------------- /tests/service_promote.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | load test_helper 3 | 4 | setup() { 5 | dokku "$PLUGIN_COMMAND_PREFIX:create" l 6 | dokku apps:create my-app 7 | dokku "$PLUGIN_COMMAND_PREFIX:link" l my-app 8 | } 9 | 10 | teardown() { 11 | dokku "$PLUGIN_COMMAND_PREFIX:unlink" l my-app 12 | dokku --force "$PLUGIN_COMMAND_PREFIX:destroy" l 13 | dokku --force apps:destroy my-app 14 | } 15 | 16 | @test "($PLUGIN_COMMAND_PREFIX:promote) error when there are no arguments" { 17 | run dokku "$PLUGIN_COMMAND_PREFIX:promote" 18 | assert_contains "${lines[*]}" "Please specify a valid name for the service" 19 | } 20 | 21 | @test "($PLUGIN_COMMAND_PREFIX:promote) error when the app argument is missing" { 22 | run dokku "$PLUGIN_COMMAND_PREFIX:promote" l 23 | assert_contains "${lines[*]}" "Please specify an app to run the command on" 24 | } 25 | 26 | @test "($PLUGIN_COMMAND_PREFIX:promote) error when the app does not exist" { 27 | run dokku "$PLUGIN_COMMAND_PREFIX:promote" l not_existing_app 28 | assert_contains "${lines[*]}" "App not_existing_app does not exist" 29 | } 30 | 31 | @test "($PLUGIN_COMMAND_PREFIX:promote) error when the service does not exist" { 32 | run dokku "$PLUGIN_COMMAND_PREFIX:promote" not_existing_service my-app 33 | assert_contains "${lines[*]}" "service not_existing_service does not exist" 34 | } 35 | 36 | @test "($PLUGIN_COMMAND_PREFIX:promote) error when the service is already promoted" { 37 | run dokku "$PLUGIN_COMMAND_PREFIX:promote" l my-app 38 | assert_contains "${lines[*]}" "already promoted as DATABASE_URL" 39 | } 40 | 41 | @test "($PLUGIN_COMMAND_PREFIX:promote) changes DATABASE_URL" { 42 | password="$(sudo cat "$PLUGIN_DATA_ROOT/l/PASSWORD")" 43 | dokku config:set my-app "DATABASE_URL=mysql://u:p@host:3306/db" "DOKKU_MYSQL_BLUE_URL=mysql://mysql:$password@dokku-mysql-l:3306/l" 44 | dokku "$PLUGIN_COMMAND_PREFIX:promote" l my-app 45 | url=$(dokku config:get my-app DATABASE_URL) 46 | assert_equal "$url" "mysql://mysql:$password@dokku-mysql-l:3306/l" 47 | } 48 | 49 | @test "($PLUGIN_COMMAND_PREFIX:promote) creates new config url when needed" { 50 | password="$(sudo cat "$PLUGIN_DATA_ROOT/l/PASSWORD")" 51 | dokku config:set my-app "DATABASE_URL=mysql://u:p@host:3306/db" "DOKKU_MYSQL_BLUE_URL=mysql://mysql:$password@dokku-mysql-l:3306/l" 52 | dokku "$PLUGIN_COMMAND_PREFIX:promote" l my-app 53 | run dokku config my-app 54 | assert_contains "${lines[*]}" "DOKKU_MYSQL_" 55 | } 56 | 57 | @test "($PLUGIN_COMMAND_PREFIX:promote) uses MYSQL_DATABASE_SCHEME variable" { 58 | password="$(sudo cat "$PLUGIN_DATA_ROOT/l/PASSWORD")" 59 | dokku config:set my-app "MYSQL_DATABASE_SCHEME=mysql2" "DATABASE_URL=mysql://u:p@host:3306/db" "DOKKU_MYSQL_BLUE_URL=mysql2://mysql:$password@dokku-mysql-l:3306/l" 60 | dokku "$PLUGIN_COMMAND_PREFIX:promote" l my-app 61 | url=$(dokku config:get my-app DATABASE_URL) 62 | assert_contains "$url" "mysql2://mysql:$password@dokku-mysql-l:3306/l" 63 | } 64 | -------------------------------------------------------------------------------- /install: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/config" 3 | source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/common-functions" 4 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions" 5 | set -eo pipefail 6 | [[ $DOKKU_TRACE ]] && set -x 7 | 8 | plugin-install() { 9 | pull-docker-image() { 10 | declare IMAGE="$1" 11 | if [[ "$PLUGIN_DISABLE_PULL" == "true" ]]; then 12 | echo " ! ${PLUGIN_DISABLE_PULL_VARIABLE} environment variable detected. Not running pull command." 1>&2 13 | echo " ! docker image pull ${IMAGE}" 1>&2 14 | return 15 | fi 16 | if [[ "$("$DOCKER_BIN" image ls -q "${IMAGE}" 2>/dev/null)" == "" ]]; then 17 | "$DOCKER_BIN" image pull "${IMAGE}" 18 | fi 19 | } 20 | 21 | fn-plugin-property-setup "$PLUGIN_COMMAND_PREFIX" 22 | pull-docker-image "${PLUGIN_IMAGE}:${PLUGIN_IMAGE_VERSION}" 23 | pull-docker-image "$PLUGIN_BUSYBOX_IMAGE" 24 | pull-docker-image "$PLUGIN_AMBASSADOR_IMAGE" 25 | pull-docker-image "$PLUGIN_S3BACKUP_IMAGE" 26 | pull-docker-image "$PLUGIN_WAIT_IMAGE" 27 | 28 | mkdir -p "$PLUGIN_DATA_ROOT" || echo "Failed to create $PLUGIN_SERVICE data directory" 29 | chown "${DOKKU_SYSTEM_USER}:${DOKKU_SYSTEM_GROUP}" "$PLUGIN_DATA_ROOT" 30 | 31 | mkdir -p "$PLUGIN_CONFIG_ROOT" || echo "Failed to create $PLUGIN_SERVICE config directory" 32 | chown "${DOKKU_SYSTEM_USER}:${DOKKU_SYSTEM_GROUP}" "$PLUGIN_CONFIG_ROOT" 33 | 34 | rm -f "/etc/sudoers.d/dokku-${PLUGIN_COMMAND_PREFIX}*" 35 | _SUDOERS_FILE="/etc/sudoers.d/dokku-${PLUGIN_COMMAND_PREFIX}" 36 | 37 | touch "$_SUDOERS_FILE" 38 | cat >"$_SUDOERS_FILE" <"$SERVICE_ROOT/IMAGE" 56 | echo "${image##*:}" >"$SERVICE_ROOT/IMAGE_VERSION" 57 | fi 58 | fi 59 | 60 | chown "${DOKKU_SYSTEM_USER}:${DOKKU_SYSTEM_GROUP}" "$SERVICE_ROOT/IMAGE" "$SERVICE_ROOT/IMAGE_VERSION" 61 | 62 | if [[ -f "$SERVICE_ROOT/${PLUGIN_VARIABLE}_CONFIG_OPTIONS" ]]; then 63 | mv "$SERVICE_ROOT/${PLUGIN_VARIABLE}_CONFIG_OPTIONS" "$SERVICE_ROOT/CONFIG_OPTIONS" 64 | chown "${DOKKU_SYSTEM_USER}:${DOKKU_SYSTEM_GROUP}" "$SERVICE_ROOT/CONFIG_OPTIONS" 65 | fi 66 | done 67 | } 68 | 69 | plugin-install "$@" 70 | -------------------------------------------------------------------------------- /subcommands/clone: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 6 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/functions" 7 | 8 | service-clone-cmd() { 9 | #E you can clone an existing service to a new one 10 | #E dokku $PLUGIN_COMMAND_PREFIX:clone lollipop lollipop-2 11 | #A service, service to run command against 12 | #A new-service, name of new service 13 | #F -c|--config-options "--args --go=here", extra arguments to pass to the container create command 14 | #F -C|--custom-env "USER=alpha;HOST=beta", semi-colon delimited environment variables to start the service with 15 | #F -i|--image IMAGE, the image name to start the service with 16 | #F -I|--image-version IMAGE_VERSION, the image version to start the service with 17 | #F -m|--memory MEMORY, container memory limit in megabytes (default: unlimited) 18 | #F -N|--initial-network INITIAL_NETWORK, the initial network to attach the service to 19 | #F -p|--password PASSWORD, override the user-level service password 20 | #F -P|--post-create-network NETWORKS, a comma-separated list of networks to attach the service container to after service creation 21 | #F -r|--root-password PASSWORD, override the root-level service password 22 | #F -S|--post-start-network NETWORKS, a comma-separated list of networks to attach the service container to after service start 23 | #F -s|--shm-size SHM_SIZE, override shared memory size for $PLUGIN_COMMAND_PREFIX docker container 24 | declare desc="create container then copy data from into " 25 | local cmd="$PLUGIN_COMMAND_PREFIX:clone" argv=("$@") 26 | [[ ${argv[0]} == "$cmd" ]] && shift 1 27 | declare SERVICE="$1" NEW_SERVICE="$2" CLONE_FLAGS_LIST=("${@:3}") 28 | is_implemented_command "$cmd" || dokku_log_fail "Not yet implemented" 29 | 30 | [[ -z "$SERVICE" ]] && dokku_log_fail "Please specify a valid name for the service" 31 | [[ -z "$NEW_SERVICE" ]] && dokku_log_fail "Please specify a name for the new service" 32 | verify_service_name "$SERVICE" 33 | if service_exists "$NEW_SERVICE"; then 34 | dokku_log_fail "Invalid service name $NEW_SERVICE. Verify the service name is not already in use." 35 | fi 36 | 37 | local SERVICE_ROOT="$PLUGIN_DATA_ROOT/$SERVICE" 38 | local ID="$(cat "$SERVICE_ROOT/ID")" 39 | is_container_status "$ID" "Running" || dokku_log_fail "Service ${SERVICE} container is not running" 40 | 41 | PLUGIN_IMAGE=$(service_version "$SERVICE" | grep -o "^.*:" | sed -r "s/://g") 42 | PLUGIN_IMAGE_VERSION=$(service_version "$SERVICE" | grep -o ":.*$" | sed -r "s/://g") 43 | 44 | service_parse_args "${@:3}" 45 | 46 | dokku_log_info2 "Cloning $SERVICE to $NEW_SERVICE @ $PLUGIN_IMAGE:$PLUGIN_IMAGE_VERSION" 47 | service_create "$NEW_SERVICE" "${@:3}" 48 | dokku_log_info1 "Copying data from $SERVICE to $NEW_SERVICE" 49 | service_export "$SERVICE" | service_import "$NEW_SERVICE" >/dev/null 2>&1 || true 50 | dokku_log_info2 "Done" 51 | } 52 | 53 | service-clone-cmd "$@" 54 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | HARDWARE = $(shell uname -m) 2 | SYSTEM_NAME = $(shell uname -s | tr '[:upper:]' '[:lower:]') 3 | ARCH = $(shell dpkg --print-architecture) 4 | SHFMT_VERSION = 3.0.2 5 | XUNIT_TO_GITHUB_VERSION = 0.3.0 6 | XUNIT_READER_VERSION = 0.1.0 7 | 8 | 9 | bats: 10 | ifeq ($(SYSTEM_NAME),darwin) 11 | ifneq ($(shell bats --version >/dev/null 2>&1 ; echo $$?),0) 12 | brew install bats-core 13 | endif 14 | else 15 | git clone https://github.com/bats-core/bats-core.git /tmp/bats 16 | cd /tmp/bats && sudo ./install.sh /usr/local 17 | rm -rf /tmp/bats 18 | endif 19 | 20 | shellcheck: 21 | ifneq ($(shell shellcheck --version >/dev/null 2>&1 ; echo $$?),0) 22 | ifeq ($(SYSTEM_NAME),darwin) 23 | brew install shellcheck 24 | else 25 | ifeq ($(ARCH),arm64) 26 | sudo add-apt-repository 'deb http://ports.ubuntu.com/ubuntu-ports jammy-backports main restricted universe multiverse' 27 | else 28 | sudo add-apt-repository 'deb http://archive.ubuntu.com/ubuntu jammy-backports main restricted universe multiverse' 29 | endif 30 | sudo rm -rf /var/lib/apt/lists/* && sudo apt-get clean 31 | sudo apt-get update -qq && sudo apt-get install -qq -y shellcheck 32 | endif 33 | endif 34 | 35 | shfmt: 36 | ifneq ($(shell shfmt --version >/dev/null 2>&1 ; echo $$?),0) 37 | ifeq ($(shfmt),Darwin) 38 | brew install shfmt 39 | else 40 | wget -qO /tmp/shfmt https://github.com/mvdan/sh/releases/download/v$(SHFMT_VERSION)/shfmt_v$(SHFMT_VERSION)_linux_amd64 41 | chmod +x /tmp/shfmt 42 | sudo mv /tmp/shfmt /usr/local/bin/shfmt 43 | endif 44 | endif 45 | 46 | readlink: 47 | ifeq ($(shell uname),Darwin) 48 | ifeq ($(shell greadlink > /dev/null 2>&1 ; echo $$?),127) 49 | brew install coreutils 50 | endif 51 | ln -nfs `which greadlink` tests/bin/readlink 52 | endif 53 | 54 | ci-dependencies: shellcheck bats readlink 55 | 56 | lint-setup: 57 | @mkdir -p tmp/test-results/shellcheck tmp/shellcheck 58 | @find . -not -path '*/\.*' -type f | xargs file | grep text | awk -F ':' '{ print $$1 }' | xargs head -n1 | egrep -B1 "bash" | grep "==>" | awk '{ print $$2 }' > tmp/shellcheck/test-files 59 | @cat tests/shellcheck-exclude | sed -n -e '/^# SC/p' | cut -d' ' -f2 | paste -d, -s - > tmp/shellcheck/exclude 60 | 61 | lint: lint-setup 62 | # these are disabled due to their expansive existence in the codebase. we should clean it up though 63 | @cat tests/shellcheck-exclude | sed -n -e '/^# SC/p' 64 | @echo linting... 65 | @cat tmp/shellcheck/test-files | xargs shellcheck -e $(shell cat tmp/shellcheck/exclude) | tests/shellcheck-to-junit --output tmp/test-results/shellcheck/results.xml --files tmp/shellcheck/test-files --exclude $(shell cat tmp/shellcheck/exclude) 66 | 67 | unit-tests: 68 | @echo running unit tests... 69 | @mkdir -p tmp/test-results/bats 70 | @cd tests && echo "executing tests: $(shell cd tests ; ls *.bats | xargs)" 71 | cd tests && bats --report-formatter junit --timing -o ../tmp/test-results/bats *.bats 72 | 73 | tmp/xunit-reader: 74 | mkdir -p tmp 75 | curl -o tmp/xunit-reader.tgz -sL https://github.com/josegonzalez/go-xunit-reader/releases/download/v$(XUNIT_READER_VERSION)/xunit-reader_$(XUNIT_READER_VERSION)_$(SYSTEM_NAME)_$(HARDWARE).tgz 76 | tar xf tmp/xunit-reader.tgz -C tmp 77 | chmod +x tmp/xunit-reader 78 | 79 | setup: 80 | bash tests/setup.sh 81 | $(MAKE) ci-dependencies 82 | 83 | test: lint unit-tests 84 | 85 | report: tmp/xunit-reader 86 | tmp/xunit-reader -p 'tmp/test-results/bats/*.xml' 87 | tmp/xunit-reader -p 'tmp/test-results/shellcheck/*.xml' 88 | 89 | .PHONY: clean 90 | clean: 91 | rm -f README.md 92 | 93 | .PHONY: generate 94 | generate: clean README.md 95 | 96 | .PHONY: README.md 97 | README.md: 98 | bin/generate 99 | -------------------------------------------------------------------------------- /subcommands/upgrade: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 6 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/functions" 7 | source "$PLUGIN_AVAILABLE_PATH/ps/functions" 8 | 9 | service-upgrade-cmd() { 10 | #E you can upgrade an existing service to a new image or image-version 11 | #E dokku $PLUGIN_COMMAND_PREFIX:upgrade lollipop 12 | #A service, service to run command against 13 | #F -c|--config-options "--args --go=here", extra arguments to pass to the container create command 14 | #F -C|--custom-env "USER=alpha;HOST=beta", semi-colon delimited environment variables to start the service with 15 | #F -i|--image IMAGE, the image name to start the service with 16 | #F -I|--image-version IMAGE_VERSION, the image version to start the service with 17 | #F -N|--initial-network INITIAL_NETWORK, the initial network to attach the service to 18 | #F -P|--post-create-network NETWORKS, a comma-separated list of networks to attach the service container to after service creation 19 | #F -R|--restart-apps "true", whether or not to force an app restart (default: false) 20 | #F -S|--post-start-network NETWORKS, a comma-separated list of networks to attach the service container to after service start 21 | #F -s|--shm-size SHM_SIZE, override shared memory size for $PLUGIN_COMMAND_PREFIX docker container 22 | declare desc="upgrade service to the specified versions" 23 | local cmd="$PLUGIN_COMMAND_PREFIX:upgrade" argv=("$@") 24 | [[ ${argv[0]} == "$cmd" ]] && shift 1 25 | declare SERVICE="$1" UPGRADE_FLAGS_LIST=("${@:2}") 26 | 27 | [[ -z "$SERVICE" ]] && dokku_log_fail "Please specify a valid name for the service" 28 | verify_service_name "$SERVICE" 29 | 30 | local SERVICE_ROOT="$PLUGIN_DATA_ROOT/$SERVICE" 31 | 32 | service_parse_args "${@:2}" 33 | 34 | if ! service_image_exists "$SERVICE" "$PLUGIN_IMAGE" "$PLUGIN_IMAGE_VERSION"; then 35 | if [[ "$PLUGIN_DISABLE_PULL" == "true" ]]; then 36 | dokku_log_warn "${PLUGIN_DISABLE_PULL_VARIABLE} environment variable detected. Not running pull command." 1>&2 37 | dokku_log_warn " docker image pull ${IMAGE}" 1>&2 38 | dokku_log_warn "$PLUGIN_SERVICE service $SERVICE upgrade failed" 39 | exit 1 40 | fi 41 | "$DOCKER_BIN" image pull "$PLUGIN_IMAGE:$PLUGIN_IMAGE_VERSION" || dokku_log_fail "$PLUGIN_SERVICE image $PLUGIN_IMAGE:$PLUGIN_IMAGE_VERSION pull failed" 42 | fi 43 | 44 | local NEW_PLUGIN_IMAGE_TAG="$PLUGIN_IMAGE:$PLUGIN_IMAGE_VERSION" 45 | if [[ "$(service_version "$SERVICE")" == "$NEW_PLUGIN_IMAGE_TAG" ]]; then 46 | dokku_log_info1 "Service $SERVICE already running $NEW_PLUGIN_IMAGE_TAG" 47 | return 48 | fi 49 | 50 | service_commit_config "$SERVICE" 51 | 52 | dokku_log_info2 "Upgrading $SERVICE to $NEW_PLUGIN_IMAGE_TAG" 53 | if [[ "$SERVICE_RESTART_APPS" == "true" ]]; then 54 | dokku_log_info2 "Stopping all linked services" 55 | for app in $(service_linked_apps "$SERVICE"); do 56 | [[ "$app" == "-" ]] && continue 57 | ps_stop "$app" 58 | done 59 | fi 60 | 61 | dokku_log_info2 "Stopping $SERVICE" 62 | service_container_rm "$SERVICE" 63 | service_start "$SERVICE" "${@:2}" 64 | 65 | if [[ "$SERVICE_RESTART_APPS" == "true" ]]; then 66 | dokku_log_info2 "Starting all linked services" 67 | for app in $(service_linked_apps "$SERVICE"); do 68 | [[ "$app" == "-" ]] && continue 69 | ps_start "$app" 70 | done 71 | fi 72 | 73 | dokku_log_info2 "Done" 74 | } 75 | 76 | service-upgrade-cmd "$@" 77 | -------------------------------------------------------------------------------- /subcommands/link: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 6 | source "$(dirname "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")/functions" 7 | 8 | service-link-cmd() { 9 | #E a $PLUGIN_COMMAND_PREFIX service can be linked to a container. 10 | #E this will use native docker links via the docker-options plugin. 11 | #E here we link it to our 'playground' app. 12 | #E > NOTE: this will restart your app 13 | #E dokku $PLUGIN_COMMAND_PREFIX:link lollipop playground 14 | #E the following environment variables will be set automatically by docker 15 | #E (not on the app itself, so they won’t be listed when calling dokku config): 16 | #E 17 | #E DOKKU_${PLUGIN_VARIABLE}_LOLLIPOP_NAME=/lollipop/DATABASE 18 | #E DOKKU_${PLUGIN_VARIABLE}_LOLLIPOP_PORT=tcp://172.17.0.1:${PLUGIN_DATASTORE_PORTS[0]} 19 | #E DOKKU_${PLUGIN_VARIABLE}_LOLLIPOP_PORT_${PLUGIN_DATASTORE_PORTS[0]}_TCP=tcp://172.17.0.1:${PLUGIN_DATASTORE_PORTS[0]} 20 | #E DOKKU_${PLUGIN_VARIABLE}_LOLLIPOP_PORT_${PLUGIN_DATASTORE_PORTS[0]}_TCP_PROTO=tcp 21 | #E DOKKU_${PLUGIN_VARIABLE}_LOLLIPOP_PORT_${PLUGIN_DATASTORE_PORTS[0]}_TCP_PORT=${PLUGIN_DATASTORE_PORTS[0]} 22 | #E DOKKU_${PLUGIN_VARIABLE}_LOLLIPOP_PORT_${PLUGIN_DATASTORE_PORTS[0]}_TCP_ADDR=172.17.0.1 23 | #E 24 | #E the following will be set on the linked application by default: 25 | #E 26 | #E ${PLUGIN_DEFAULT_ALIAS}_URL=${PLUGIN_SCHEME}://mysql:SOME_PASSWORD@dokku-${PLUGIN_COMMAND_PREFIX}-lollipop:${PLUGIN_DATASTORE_PORTS[0]}/lollipop 27 | #E 28 | #E the host exposed here only works internally in docker containers. 29 | #E if you want your container to be reachable from outside, you should 30 | #E use the 'expose' subcommand. another service can be linked to your app: 31 | #E dokku $PLUGIN_COMMAND_PREFIX:link other_service playground 32 | #E it is possible to change the protocol for ${PLUGIN_DEFAULT_ALIAS}_URL by setting the 33 | #E environment variable ${PLUGIN_VARIABLE}_DATABASE_SCHEME on the app. doing so will 34 | #E after linking will cause the plugin to think the service is not 35 | #E linked, and we advise you to unlink before proceeding. 36 | #E dokku config:set playground ${PLUGIN_VARIABLE}_DATABASE_SCHEME=${PLUGIN_SCHEME}2 37 | #E dokku $PLUGIN_COMMAND_PREFIX:link lollipop playground 38 | #E this will cause ${PLUGIN_DEFAULT_ALIAS}_URL to be set as: 39 | #E 40 | #E ${PLUGIN_SCHEME}2://mysql:SOME_PASSWORD@dokku-${PLUGIN_COMMAND_PREFIX}-lollipop:${PLUGIN_DATASTORE_PORTS[0]}/lollipop 41 | #A service, service to run command against 42 | #A app, app to run command against 43 | #F -a|--alias "BLUE_DATABASE", an alternative alias to use for linking to an app via environment variable 44 | #F -q|--querystring "pool=5", ampersand delimited querystring arguments to append to the service link 45 | #F -n|--no-restart "false", whether or not to restart the app on link (default: true) 46 | declare desc="link the $PLUGIN_SERVICE service to the app" 47 | local cmd="$PLUGIN_COMMAND_PREFIX:link" argv=("$@") 48 | [[ ${argv[0]} == "$cmd" ]] && shift 1 49 | declare SERVICE="$1" APP="$2" LINK_FLAGS_LIST=("${@:3}") 50 | APP=${APP:="$DOKKU_APP_NAME"} 51 | 52 | [[ -z "$SERVICE" ]] && dokku_log_fail "Please specify a valid name for the service" 53 | [[ -z "$APP" ]] && dokku_log_fail "Please specify an app to run the command on" 54 | verify_app_name "$APP" 55 | verify_service_name "$SERVICE" 56 | 57 | service_parse_args "${@:3}" 58 | service_link "$SERVICE" "$APP" 59 | } 60 | 61 | service-link-cmd "$@" 62 | -------------------------------------------------------------------------------- /tests/service_link.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | load test_helper 3 | 4 | setup() { 5 | dokku "$PLUGIN_COMMAND_PREFIX:create" ls 6 | dokku "$PLUGIN_COMMAND_PREFIX:create" ms 7 | dokku apps:create my-app 8 | } 9 | 10 | teardown() { 11 | dokku --force "$PLUGIN_COMMAND_PREFIX:destroy" ms 12 | dokku --force "$PLUGIN_COMMAND_PREFIX:destroy" ls 13 | dokku --force apps:destroy my-app 14 | } 15 | 16 | @test "($PLUGIN_COMMAND_PREFIX:link) error when there are no arguments" { 17 | run dokku "$PLUGIN_COMMAND_PREFIX:link" 18 | echo "output: $output" 19 | echo "status: $status" 20 | assert_contains "${lines[*]}" "Please specify a valid name for the service" 21 | assert_failure 22 | } 23 | 24 | @test "($PLUGIN_COMMAND_PREFIX:link) error when the app argument is missing" { 25 | run dokku "$PLUGIN_COMMAND_PREFIX:link" ls 26 | echo "output: $output" 27 | echo "status: $status" 28 | assert_contains "${lines[*]}" "Please specify an app to run the command on" 29 | assert_failure 30 | } 31 | 32 | @test "($PLUGIN_COMMAND_PREFIX:link) error when the app does not exist" { 33 | run dokku "$PLUGIN_COMMAND_PREFIX:link" ls not_existing_app 34 | echo "output: $output" 35 | echo "status: $status" 36 | assert_contains "${lines[*]}" "App not_existing_app does not exist" 37 | assert_failure 38 | } 39 | 40 | @test "($PLUGIN_COMMAND_PREFIX:link) error when the service does not exist" { 41 | run dokku "$PLUGIN_COMMAND_PREFIX:link" not_existing_service my-app 42 | echo "output: $output" 43 | echo "status: $status" 44 | assert_contains "${lines[*]}" "service not_existing_service does not exist" 45 | assert_failure 46 | } 47 | 48 | @test "($PLUGIN_COMMAND_PREFIX:link) error when the service is already linked to app" { 49 | dokku "$PLUGIN_COMMAND_PREFIX:link" ls my-app 50 | run dokku "$PLUGIN_COMMAND_PREFIX:link" ls my-app 51 | echo "output: $output" 52 | echo "status: $status" 53 | assert_contains "${lines[*]}" "Already linked as DATABASE_URL" 54 | assert_failure 55 | 56 | dokku "$PLUGIN_COMMAND_PREFIX:unlink" ls my-app 57 | } 58 | 59 | @test "($PLUGIN_COMMAND_PREFIX:link) exports DATABASE_URL to app" { 60 | run dokku "$PLUGIN_COMMAND_PREFIX:link" ls my-app 61 | echo "output: $output" 62 | echo "status: $status" 63 | url=$(dokku config:get my-app DATABASE_URL) 64 | password="$(sudo cat "$PLUGIN_DATA_ROOT/ls/PASSWORD")" 65 | assert_contains "$url" "mysql://mysql:$password@dokku-mysql-ls:3306/ls" 66 | assert_success 67 | dokku "$PLUGIN_COMMAND_PREFIX:unlink" ls my-app 68 | } 69 | 70 | @test "($PLUGIN_COMMAND_PREFIX:link) generates an alternate config url when DATABASE_URL already in use" { 71 | dokku config:set my-app DATABASE_URL=mysql://user:pass@host:3306/db 72 | dokku "$PLUGIN_COMMAND_PREFIX:link" ls my-app 73 | run dokku config my-app 74 | assert_contains "${lines[*]}" "DOKKU_MYSQL_AQUA_URL" 75 | assert_success 76 | 77 | dokku "$PLUGIN_COMMAND_PREFIX:link" ms my-app 78 | run dokku config my-app 79 | assert_contains "${lines[*]}" "DOKKU_MYSQL_BLACK_URL" 80 | assert_success 81 | dokku "$PLUGIN_COMMAND_PREFIX:unlink" ms my-app 82 | dokku "$PLUGIN_COMMAND_PREFIX:unlink" ls my-app 83 | } 84 | 85 | @test "($PLUGIN_COMMAND_PREFIX:link) links to app with docker-options" { 86 | dokku "$PLUGIN_COMMAND_PREFIX:link" ls my-app 87 | run dokku docker-options:report my-app 88 | assert_contains "${lines[*]}" "--link dokku.mysql.ls:dokku-mysql-ls" 89 | assert_success 90 | dokku "$PLUGIN_COMMAND_PREFIX:unlink" ls my-app 91 | } 92 | 93 | @test "($PLUGIN_COMMAND_PREFIX:link) uses apps MYSQL_DATABASE_SCHEME variable" { 94 | dokku config:set my-app MYSQL_DATABASE_SCHEME=mysql2 95 | dokku "$PLUGIN_COMMAND_PREFIX:link" ls my-app 96 | url=$(dokku config:get my-app DATABASE_URL) 97 | password="$(sudo cat "$PLUGIN_DATA_ROOT/ls/PASSWORD")" 98 | assert_contains "$url" "mysql2://mysql:$password@dokku-mysql-ls:3306/ls" 99 | assert_success 100 | dokku "$PLUGIN_COMMAND_PREFIX:unlink" ls my-app 101 | } 102 | 103 | @test "($PLUGIN_COMMAND_PREFIX:link) adds a querystring" { 104 | dokku "$PLUGIN_COMMAND_PREFIX:link" ls my-app --querystring "pool=5" 105 | url=$(dokku config:get my-app DATABASE_URL) 106 | assert_contains "$url" "?pool=5" 107 | assert_success 108 | dokku "$PLUGIN_COMMAND_PREFIX:unlink" ls my-app 109 | } 110 | 111 | @test "($PLUGIN_COMMAND_PREFIX:link) uses a specified config url when alias is specified" { 112 | dokku "$PLUGIN_COMMAND_PREFIX:link" ls my-app --alias "ALIAS" 113 | url=$(dokku config:get my-app ALIAS_URL) 114 | password="$(sudo cat "$PLUGIN_DATA_ROOT/ls/PASSWORD")" 115 | assert_contains "$url" "mysql://mysql:$password@dokku-mysql-ls:3306/ls" 116 | assert_success 117 | dokku "$PLUGIN_COMMAND_PREFIX:unlink" ls my-app 118 | } 119 | 120 | @test "($PLUGIN_COMMAND_PREFIX:link) respects --no-restart" { 121 | run dokku "$PLUGIN_COMMAND_PREFIX:link" ls my-app 122 | echo "output: $output" 123 | echo "status: $status" 124 | assert_output_contains "Skipping restart of linked app" 0 125 | assert_success 126 | 127 | run dokku "$PLUGIN_COMMAND_PREFIX:unlink" ls my-app 128 | echo "output: $output" 129 | echo "status: $status" 130 | assert_success 131 | 132 | run dokku "$PLUGIN_COMMAND_PREFIX:link" ls my-app --no-restart 133 | echo "output: $output" 134 | echo "status: $status" 135 | assert_output_contains "Skipping restart of linked app" 136 | assert_success 137 | 138 | run dokku "$PLUGIN_COMMAND_PREFIX:unlink" ls my-app 139 | echo "output: $output" 140 | echo "status: $status" 141 | assert_success 142 | } 143 | -------------------------------------------------------------------------------- /tests/shellcheck-to-junit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | from __future__ import print_function 4 | import argparse 5 | import collections 6 | import datetime 7 | import re 8 | import socket 9 | import sys 10 | 11 | from xml.etree import ElementTree 12 | 13 | 14 | def CDATA(text=None): 15 | element = ElementTree.Element('![CDATA[') 16 | element.text = text 17 | return element 18 | 19 | 20 | def _serialize_xml(write, elem, qnames, namespaces,short_empty_elements, **kwargs): 21 | 22 | if elem.tag == '![CDATA[': 23 | write("\n<{}{}]]>\n".format(elem.tag, elem.text)) 24 | if elem.tail: 25 | write(ElementTree._escape_cdata(elem.tail)) 26 | else: 27 | return ElementTree._original_serialize_xml(write, elem, qnames, namespaces,short_empty_elements, **kwargs) 28 | 29 | 30 | ElementTree._original_serialize_xml = ElementTree._serialize_xml 31 | ElementTree._serialize_xml = ElementTree._serialize['xml'] = _serialize_xml 32 | 33 | 34 | def read_in(): 35 | lines = sys.stdin.readlines() 36 | for i in range(len(lines)): 37 | lines[i] = lines[i].rstrip() 38 | return lines 39 | 40 | 41 | def process_lines(lines): 42 | files = {} 43 | current_file = None 44 | previous_line = None 45 | line_no = None 46 | new_issues = [] 47 | code = None 48 | 49 | RE_VIOLATION = re.compile(r"\^-- (SC[\w]+): (.*)") 50 | RE_VIOLATION_NEW = re.compile(r"\^[-]+\^ (SC[\w]+): (.*)") 51 | 52 | for line in lines: 53 | # start a new block 54 | if line == '': 55 | if current_file is not None: 56 | file_data = files.get(current_file, {}) 57 | files[current_file] = file_data 58 | 59 | issue_data = file_data.get(line_no, {}) 60 | issue_data['code'] = code 61 | files[current_file][line_no] = issue_data 62 | 63 | issues = issue_data.get('issues', []) 64 | issues.extend(new_issues) 65 | issue_data['issues'] = issues 66 | 67 | files[current_file][line_no] = issue_data 68 | 69 | code = None 70 | current_file = None 71 | line_no = None 72 | elif line.startswith('In ./') and not previous_line: 73 | current_file = line.split(' ')[1].replace('./', '') 74 | line_no = line.split(' ')[3] 75 | new_issues = [] 76 | code = None 77 | elif code is None and len(new_issues) == 0: 78 | code = line 79 | else: 80 | match = RE_VIOLATION.match(line.strip()) 81 | if not match: 82 | match = RE_VIOLATION_NEW.match(line.strip()) 83 | 84 | if not match: 85 | if 'https://www.shellcheck.net/wiki/SC' in line: 86 | continue 87 | if 'For more information:' == line: 88 | continue 89 | print('Error: Issue parsing line "{0}"'.format(line.strip())) 90 | else: 91 | new_issues.append({ 92 | 'shellcheck_id': match.group(1), 93 | 'message': match.group(2), 94 | 'original_message': line 95 | }) 96 | 97 | previous_line = line 98 | 99 | return files 100 | 101 | 102 | def output_junit(files, args): 103 | timestamp = datetime.datetime.now().replace(microsecond=0).isoformat() 104 | failures = 0 105 | for file, data in files.items(): 106 | for line, issue_data in data.items(): 107 | code = issue_data.get('code') 108 | for issue in issue_data.get('issues', []): 109 | failures += 1 110 | 111 | tests = 0 112 | if args.files: 113 | with open(args.files, 'r') as f: 114 | tests = len(f.readlines()) 115 | 116 | root = ElementTree.Element("testsuite", 117 | name="shellcheck", 118 | tests="{0}".format(tests), 119 | failures="{0}".format(failures), 120 | errors="0", 121 | skipped="0", 122 | timestamp=timestamp, 123 | time="0", 124 | hostname=socket.gethostname()) 125 | 126 | properties = ElementTree.SubElement(root, "properties") 127 | if args.exclude: 128 | ElementTree.SubElement(properties, 129 | "property", 130 | name="exclude", 131 | value=args.exclude) 132 | 133 | if args.files: 134 | with open(args.files, 'r') as f: 135 | lines = f.readlines() 136 | for i in range(len(lines)): 137 | file = lines[i].rstrip().replace('./', '') 138 | data = files.get(file, None) 139 | if data: 140 | for line, issue_data in data.items(): 141 | code = issue_data.get('code') 142 | for issue in issue_data.get('issues', []): 143 | testcase = ElementTree.SubElement(root, 144 | "testcase", 145 | classname=file, 146 | name=file, 147 | time="0") 148 | shellcheck_id = issue.get('shellcheck_id') 149 | message = 'line {0}: {1}'.format( 150 | line, issue.get('message')) 151 | original_message = issue.get('original_message') 152 | e = ElementTree.Element("failure", 153 | type=shellcheck_id, 154 | message=message) 155 | cdata = CDATA("\n".join([code, original_message])) 156 | e.append(cdata) 157 | testcase.append(e) 158 | ElementTree.SubElement(root, 159 | "testcase", 160 | classname=file, 161 | name=file, 162 | time="0") 163 | 164 | ElementTree.SubElement(root, "system-out") 165 | ElementTree.SubElement(root, "system-err") 166 | 167 | content = ElementTree.tostring(root, encoding='UTF-8', method='xml') 168 | if args.output: 169 | with open(args.output, 'w') as f: 170 | try: 171 | f.write(content) 172 | except TypeError: 173 | f.write(content.decode("utf-8")) 174 | 175 | 176 | def main(): 177 | parser = argparse.ArgumentParser( 178 | description='Process shellcheck output to junit.') 179 | parser.add_argument('--output', 180 | dest='output', 181 | action='store', 182 | default=None, 183 | help='file to write shellcheck output') 184 | parser.add_argument('--files', 185 | dest='files', 186 | action='store', 187 | default=None, 188 | help='a file containing a list of all files processed by shellcheck') 189 | parser.add_argument('--exclude', 190 | dest='exclude', 191 | action='store', 192 | default=None, 193 | help='a comma-separated list of rules being excluded by shellcheck') 194 | args = parser.parse_args() 195 | 196 | lines = read_in() 197 | files = process_lines(lines) 198 | files = collections.OrderedDict(sorted(files.items())) 199 | output_junit(files, args) 200 | for line in lines: 201 | print(line) 202 | 203 | 204 | if __name__ == '__main__': 205 | main() 206 | -------------------------------------------------------------------------------- /tests/link_networks.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | load test_helper 3 | 4 | setup() { 5 | dokku "$PLUGIN_COMMAND_PREFIX:create" ls 6 | dokku network:create custom-network 7 | } 8 | 9 | teardown() { 10 | dokku --force "$PLUGIN_COMMAND_PREFIX:destroy" ls || true 11 | dokku --force "$PLUGIN_COMMAND_PREFIX:destroy" lsa || true 12 | dokku network:destroy --force custom-network 13 | } 14 | 15 | @test "($PLUGIN_COMMAND_PREFIX:set) set initial-network" { 16 | run dokku "$PLUGIN_COMMAND_PREFIX:set" ls initial-network custom-network 17 | echo "output: $output" 18 | echo "status: $status" 19 | assert_success 20 | 21 | run dokku "$PLUGIN_COMMAND_PREFIX:info" ls --initial-network 22 | echo "output: $output" 23 | echo "status: $status" 24 | assert_output "custom-network" 25 | assert_success 26 | 27 | run docker inspect dokku.$PLUGIN_COMMAND_PREFIX.ls -f '{{range $net,$v := .NetworkSettings.Networks}}{{printf "%s\n" $net}}{{end}}' 28 | echo "output: $output" 29 | echo "status: $status" 30 | assert_success 31 | assert_output_contains bridge 32 | assert_output_contains custom-network 0 33 | 34 | run dokku "$PLUGIN_COMMAND_PREFIX:stop" ls 35 | echo "output: $output" 36 | echo "status: $status" 37 | assert_success 38 | 39 | run dokku "$PLUGIN_COMMAND_PREFIX:start" ls 40 | echo "output: $output" 41 | echo "status: $status" 42 | assert_success 43 | 44 | run docker inspect dokku.$PLUGIN_COMMAND_PREFIX.ls -f '{{range $net,$v := .NetworkSettings.Networks}}{{printf "%s\n" $net}}{{end}}' 45 | echo "output: $output" 46 | echo "status: $status" 47 | assert_success 48 | assert_output_contains bridge 0 49 | assert_output_contains custom-network 50 | 51 | run docker inspect dokku.$PLUGIN_COMMAND_PREFIX.ls -f '{{range $net,$v := .NetworkSettings.Networks}}{{range $k,$alias := $v.DNSNames}}{{printf "alias:%s\n" $alias}}{{end}}{{end}}' 52 | echo "output: $output" 53 | echo "status: $status" 54 | assert_success 55 | assert_output_contains "alias:dokku.$PLUGIN_COMMAND_PREFIX.ls" 56 | assert_output_contains "alias:dokku-$PLUGIN_COMMAND_PREFIX-ls" 57 | 58 | run dokku "$PLUGIN_COMMAND_PREFIX:set" ls initial-network 59 | echo "output: $output" 60 | echo "status: $status" 61 | assert_success 62 | 63 | run dokku "$PLUGIN_COMMAND_PREFIX:info" ls --initial-network 64 | echo "output: $output" 65 | echo "status: $status" 66 | assert_output "" 67 | assert_success 68 | 69 | run dokku "$PLUGIN_COMMAND_PREFIX:stop" ls 70 | echo "output: $output" 71 | echo "status: $status" 72 | assert_success 73 | 74 | run dokku "$PLUGIN_COMMAND_PREFIX:start" ls 75 | echo "output: $output" 76 | echo "status: $status" 77 | assert_success 78 | 79 | run docker inspect dokku.$PLUGIN_COMMAND_PREFIX.ls -f '{{range $net,$v := .NetworkSettings.Networks}}{{printf "%s\n" $net}}{{end}}' 80 | echo "output: $output" 81 | echo "status: $status" 82 | assert_success 83 | assert_output_contains bridge 84 | assert_output_contains custom-network 0 85 | } 86 | 87 | @test "($PLUGIN_COMMAND_PREFIX:set) set post-create-network" { 88 | run dokku "$PLUGIN_COMMAND_PREFIX:set" ls post-create-network custom-network 89 | echo "output: $output" 90 | echo "status: $status" 91 | assert_success 92 | 93 | run dokku "$PLUGIN_COMMAND_PREFIX:info" ls --post-create-network 94 | echo "output: $output" 95 | echo "status: $status" 96 | assert_output "custom-network" 97 | assert_success 98 | 99 | run docker inspect dokku.$PLUGIN_COMMAND_PREFIX.ls -f '{{range $net,$v := .NetworkSettings.Networks}}{{printf "%s\n" $net}}{{end}}' 100 | echo "output: $output" 101 | echo "status: $status" 102 | assert_success 103 | assert_output_contains bridge 104 | assert_output_contains custom-network 0 105 | 106 | run dokku "$PLUGIN_COMMAND_PREFIX:stop" ls 107 | echo "output: $output" 108 | echo "status: $status" 109 | assert_success 110 | 111 | run dokku "$PLUGIN_COMMAND_PREFIX:start" ls 112 | echo "output: $output" 113 | echo "status: $status" 114 | assert_success 115 | 116 | run docker inspect dokku.$PLUGIN_COMMAND_PREFIX.ls -f '{{range $net,$v := .NetworkSettings.Networks}}{{printf "%s\n" $net}}{{end}}' 117 | echo "output: $output" 118 | echo "status: $status" 119 | assert_success 120 | assert_output_contains custom-network 121 | assert_output_contains bridge 122 | 123 | run docker inspect dokku.$PLUGIN_COMMAND_PREFIX.ls -f '{{range $net,$v := .NetworkSettings.Networks}}{{range $k,$alias := $v.DNSNames}}{{printf "alias:%s\n" $alias}}{{end}}{{end}}' 124 | echo "output: $output" 125 | echo "status: $status" 126 | assert_success 127 | assert_output_contains "alias:dokku.$PLUGIN_COMMAND_PREFIX.ls" 128 | assert_output_contains "alias:dokku-$PLUGIN_COMMAND_PREFIX-ls" 129 | 130 | run dokku "$PLUGIN_COMMAND_PREFIX:set" ls post-create-network 131 | echo "output: $output" 132 | echo "status: $status" 133 | assert_success 134 | 135 | run dokku "$PLUGIN_COMMAND_PREFIX:info" ls --post-create-network 136 | echo "output: $output" 137 | echo "status: $status" 138 | assert_output "" 139 | assert_success 140 | 141 | run dokku "$PLUGIN_COMMAND_PREFIX:stop" ls 142 | echo "output: $output" 143 | echo "status: $status" 144 | assert_success 145 | 146 | run dokku "$PLUGIN_COMMAND_PREFIX:start" ls 147 | echo "output: $output" 148 | echo "status: $status" 149 | assert_success 150 | 151 | run docker inspect dokku.$PLUGIN_COMMAND_PREFIX.ls -f '{{range $net,$v := .NetworkSettings.Networks}}{{printf "%s\n" $net}}{{end}}' 152 | echo "output: $output" 153 | echo "status: $status" 154 | assert_success 155 | assert_output_contains bridge 156 | assert_output_contains custom-network 0 157 | } 158 | 159 | @test "($PLUGIN_COMMAND_PREFIX:set) set an post-start-network" { 160 | run dokku "$PLUGIN_COMMAND_PREFIX:set" ls post-start-network custom-network 161 | echo "output: $output" 162 | echo "status: $status" 163 | assert_success 164 | 165 | run dokku "$PLUGIN_COMMAND_PREFIX:info" ls --post-start-network 166 | echo "output: $output" 167 | echo "status: $status" 168 | assert_output "custom-network" 169 | assert_success 170 | 171 | run docker inspect dokku.$PLUGIN_COMMAND_PREFIX.ls -f '{{range $net,$v := .NetworkSettings.Networks}}{{printf "%s\n" $net}}{{end}}' 172 | echo "output: $output" 173 | echo "status: $status" 174 | assert_success 175 | assert_output_contains bridge 176 | assert_output_contains custom-network 0 177 | 178 | run dokku "$PLUGIN_COMMAND_PREFIX:stop" ls 179 | echo "output: $output" 180 | echo "status: $status" 181 | assert_success 182 | 183 | run dokku "$PLUGIN_COMMAND_PREFIX:start" ls 184 | echo "output: $output" 185 | echo "status: $status" 186 | assert_success 187 | 188 | run docker inspect dokku.$PLUGIN_COMMAND_PREFIX.ls -f '{{range $net,$v := .NetworkSettings.Networks}}{{printf "%s\n" $net}}{{end}}' 189 | echo "output: $output" 190 | echo "status: $status" 191 | assert_success 192 | assert_output_contains bridge 193 | assert_output_contains custom-network 194 | 195 | run docker inspect dokku.$PLUGIN_COMMAND_PREFIX.ls -f '{{range $net,$v := .NetworkSettings.Networks}}{{range $k,$alias := $v.DNSNames}}{{printf "alias:%s\n" $alias}}{{end}}{{end}}' 196 | echo "output: $output" 197 | echo "status: $status" 198 | assert_success 199 | assert_output_contains "alias:dokku.$PLUGIN_COMMAND_PREFIX.ls" 200 | assert_output_contains "alias:dokku-$PLUGIN_COMMAND_PREFIX-ls" 201 | 202 | run dokku "$PLUGIN_COMMAND_PREFIX:set" ls post-start-network 203 | echo "output: $output" 204 | echo "status: $status" 205 | assert_success 206 | 207 | run dokku "$PLUGIN_COMMAND_PREFIX:info" ls --post-start-network 208 | echo "output: $output" 209 | echo "status: $status" 210 | assert_output "" 211 | assert_success 212 | 213 | run dokku "$PLUGIN_COMMAND_PREFIX:stop" ls 214 | echo "output: $output" 215 | echo "status: $status" 216 | assert_success 217 | 218 | run dokku "$PLUGIN_COMMAND_PREFIX:start" ls 219 | echo "output: $output" 220 | echo "status: $status" 221 | assert_success 222 | 223 | run docker inspect dokku.$PLUGIN_COMMAND_PREFIX.ls -f '{{range $net,$v := .NetworkSettings.Networks}}{{printf "%s\n" $net}}{{end}}' 224 | echo "output: $output" 225 | echo "status: $status" 226 | assert_success 227 | assert_output_contains bridge 228 | assert_output_contains custom-network 0 229 | } 230 | 231 | @test "($PLUGIN_COMMAND_PREFIX:create) flags" { 232 | run dokku "$PLUGIN_COMMAND_PREFIX:create" lsa --initial-network custom-network 233 | echo "output: $output" 234 | echo "status: $status" 235 | assert_success 236 | 237 | run docker inspect "dokku.$PLUGIN_COMMAND_PREFIX.lsa" -f '{{range $net,$v := .NetworkSettings.Networks}}{{printf "%s\n" $net}}{{end}}' 238 | echo "output: $output" 239 | echo "status: $status" 240 | assert_success 241 | assert_output_contains bridge 0 242 | assert_output_contains custom-network 243 | 244 | run dokku "$PLUGIN_COMMAND_PREFIX:destroy" lsa --force 245 | echo "output: $output" 246 | echo "status: $status" 247 | assert_success 248 | 249 | run dokku "$PLUGIN_COMMAND_PREFIX:create" lsa --post-create-network custom-network 250 | echo "output: $output" 251 | echo "status: $status" 252 | assert_success 253 | 254 | run docker inspect "dokku.$PLUGIN_COMMAND_PREFIX.lsa" -f '{{range $net,$v := .NetworkSettings.Networks}}{{printf "%s\n" $net}}{{end}}' 255 | echo "output: $output" 256 | echo "status: $status" 257 | assert_success 258 | assert_output_contains bridge 259 | assert_output_contains custom-network 260 | 261 | run docker inspect dokku.$PLUGIN_COMMAND_PREFIX.lsa -f '{{range $net,$v := .NetworkSettings.Networks}}{{range $k,$alias := $v.DNSNames}}{{printf "alias:%s\n" $alias}}{{end}}{{end}}' 262 | echo "output: $output" 263 | echo "status: $status" 264 | assert_success 265 | assert_output_contains "alias:dokku.$PLUGIN_COMMAND_PREFIX.lsa" 266 | assert_output_contains "alias:dokku-$PLUGIN_COMMAND_PREFIX-lsa" 267 | 268 | run dokku "$PLUGIN_COMMAND_PREFIX:destroy" lsa --force 269 | echo "output: $output" 270 | echo "status: $status" 271 | assert_success 272 | 273 | run dokku "$PLUGIN_COMMAND_PREFIX:create" lsa --post-start-network custom-network 274 | echo "output: $output" 275 | echo "status: $status" 276 | assert_success 277 | 278 | run docker inspect "dokku.$PLUGIN_COMMAND_PREFIX.lsa" -f '{{range $net,$v := .NetworkSettings.Networks}}{{printf "%s\n" $net}}{{end}}' 279 | echo "output: $output" 280 | echo "status: $status" 281 | assert_success 282 | assert_output_contains bridge 283 | assert_output_contains custom-network 284 | 285 | run dokku "$PLUGIN_COMMAND_PREFIX:destroy" lsa --force 286 | echo "output: $output" 287 | echo "status: $status" 288 | assert_success 289 | } 290 | -------------------------------------------------------------------------------- /help-functions: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/common-functions" 6 | export SUBCOMMAND_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/subcommands" 7 | 8 | fn-help() { 9 | declare CMD="$1" 10 | local cmd EXIT_CODE 11 | 12 | if [[ "$CMD" == "help" ]] || [[ "$CMD" == "$PLUGIN_COMMAND_PREFIX:help" ]] || [[ "$CMD" == "$PLUGIN_COMMAND_PREFIX" ]] || [[ "$CMD" == "$PLUGIN_COMMAND_PREFIX:default" ]]; then 13 | fn-help-all "$@" 14 | exit 0 15 | fi 16 | 17 | pushd "$SUBCOMMAND_ROOT" >/dev/null 2>&1 18 | for cmd in *; do 19 | if [[ "$CMD" == "${PLUGIN_COMMAND_PREFIX}:$cmd" ]]; then 20 | "$SUBCOMMAND_ROOT/$cmd" "$@" 21 | EXIT_CODE="$?" 22 | exit "$EXIT_CODE" 23 | fi 24 | done 25 | popd >/dev/null 2>&1 26 | 27 | exit "$DOKKU_NOT_IMPLEMENTED_EXIT" 28 | } 29 | 30 | fn-help-all() { 31 | declare CMD="$1" SUBCOMMAND="$2" 32 | local CMD_OUTPUT BLUE BOLD FULL_OUTPUT NORMAL 33 | FULL_OUTPUT=true 34 | 35 | if [[ "$CMD" == "$PLUGIN_COMMAND_PREFIX:help" ]] || [[ "$CMD" == "$PLUGIN_COMMAND_PREFIX" ]] || [[ "$CMD" == "$PLUGIN_COMMAND_PREFIX:default" ]]; then 36 | BOLD="$(fn-help-fancy-tput bold)" 37 | NORMAL="$(fn-help-fancy-color "\033[m")" 38 | BLUE="$(fn-help-fancy-color "\033[0;34m")" 39 | CYAN="$(fn-help-fancy-color "\033[1;36m")" 40 | if [[ -n "$SUBCOMMAND" ]] && [[ "$SUBCOMMAND" != "--all" ]]; then 41 | fn-help-contents-subcommand "$SUBCOMMAND" "$FULL_OUTPUT" 42 | return "$?" 43 | fi 44 | 45 | echo -e "${BOLD}usage${NORMAL}: dokku ${PLUGIN_COMMAND_PREFIX}[:COMMAND]" 46 | echo '' 47 | echo -e "${BOLD}List your $PLUGIN_COMMAND_PREFIX services.${NORMAL}" 48 | echo '' 49 | echo -e "${BLUE}Example:${NORMAL}" 50 | echo '' 51 | echo " \$ dokku $PLUGIN_COMMAND_PREFIX:list" 52 | echo '' 53 | fn-help-list-example | column -c5 -t -s, 54 | echo '' 55 | echo -e "dokku ${BOLD}${PLUGIN_COMMAND_PREFIX}${NORMAL} commands: (get help with ${CYAN}dokku ${PLUGIN_COMMAND_PREFIX}:help SUBCOMMAND${NORMAL})" 56 | echo '' 57 | fn-help-contents | sort | column -c2 -t -s, 58 | echo '' 59 | elif [[ $(ps -o command= $PPID) == *"--all"* ]]; then 60 | fn-help-contents 61 | else 62 | cat </dev/null 2>&1 72 | for cmd in *; do 73 | fn-help-contents-subcommand "$cmd" || true 74 | done 75 | } 76 | 77 | fn-help-contents-subcommand() { 78 | declare SUBCOMMAND="$1" FULL_OUTPUT="$2" 79 | local HELP_TMPDIR=$(mktemp -d --tmpdir) 80 | local UNCLEAN_FILE="${HELP_TMPDIR}/cmd-unclean" CLEAN_FILE="${HELP_TMPDIR}/cmd-clean" 81 | local BOLD CMD_OUTPUT CYAN EXAMPLE LIGHT_GRAY NORMAL 82 | trap 'rm -rf "$HELP_TMPDIR" > /dev/null' RETURN INT TERM EXIT 83 | 84 | rm -rf "$UNCLEAN_FILE" "$CLEAN_FILE" 85 | cat "$SUBCOMMAND_ROOT/$SUBCOMMAND" >"$UNCLEAN_FILE" 86 | 87 | fn-help-subcommand-sanitize "$UNCLEAN_FILE" "$CLEAN_FILE" 88 | if ! is_implemented_command "$SUBCOMMAND"; then 89 | return 1 90 | fi 91 | 92 | args="$(fn-help-subcommand-args "$CLEAN_FILE" "$FULL_OUTPUT")" 93 | SUBCOMMAND=":$SUBCOMMAND" 94 | [[ "$SUBCOMMAND" == ":default" ]] && SUBCOMMAND="" 95 | cmd_line="$(echo -e "${SUBCOMMAND} ${args}" | sed -e 's/[[:space:]]*$//')" 96 | desc="$(grep desc "$CLEAN_FILE" | head -1)" 97 | eval "$desc" 98 | 99 | BLUE="$(fn-help-fancy-color "\033[0;34m")" 100 | BOLD="$(fn-help-fancy-tput bold)" 101 | CYAN="$(fn-help-fancy-color "\033[1;36m")" 102 | NORMAL="$(fn-help-fancy-color "\033[m")" 103 | LIGHT_GRAY="$(fn-help-fancy-color "\033[2;37m")" 104 | LIGHT_RED="$(fn-help-fancy-color "\033[1;31m")" 105 | CMD_OUTPUT="$(echo -e " ${PLUGIN_COMMAND_PREFIX}${cmd_line}, ${LIGHT_GRAY}${desc}${NORMAL}")" 106 | if [[ "$FULL_OUTPUT" != "true" ]]; then 107 | echo "$CMD_OUTPUT" 108 | return 0 109 | fi 110 | 111 | echo -e "${BOLD}usage:${NORMAL} dokku ${PLUGIN_COMMAND_PREFIX}${cmd_line}" 112 | echo '' 113 | echo -e "${BOLD}${desc}${NORMAL}" 114 | echo '' 115 | 116 | ARGS="$(fn-help-subcommand-list-args "$CLEAN_FILE")" 117 | if [[ -n "$ARGS" ]]; then 118 | echo -e "${CYAN}arguments:${NORMAL}" 119 | echo '' 120 | echo "$ARGS" | column -c2 -t -s, 121 | echo '' 122 | fi 123 | 124 | FLAGS="$(fn-help-subcommand-list-flags "$CLEAN_FILE")" 125 | if [[ -n "$FLAGS" ]]; then 126 | echo -e "${BLUE}flags:${NORMAL}" 127 | echo '' 128 | echo "$FLAGS" | column -c2 -t -s, 129 | echo '' 130 | fi 131 | 132 | EXAMPLE="$(fn-help-subcommand-example "$CLEAN_FILE")" 133 | if [[ -n "$EXAMPLE" ]]; then 134 | echo -e "${LIGHT_RED}examples:${NORMAL}" 135 | echo '' 136 | echo "$EXAMPLE" 137 | echo '' 138 | fi 139 | 140 | return 0 141 | } 142 | 143 | fn-help-fancy-tput() { 144 | declare desc="a wrapper around tput" 145 | 146 | if [[ -n "$DOKKU_NO_COLOR" ]] || [[ "$TERM" == "unknown" ]] || [[ "$TERM" == "dumb" ]]; then 147 | return 148 | fi 149 | 150 | tput "$@" 151 | } 152 | 153 | fn-help-fancy-color() { 154 | declare desc="a wrapper around colors" 155 | 156 | if [[ -n "$DOKKU_NO_COLOR" ]] || [[ "$TERM" == "unknown" ]] || [[ "$TERM" == "dumb" ]]; then 157 | return 158 | fi 159 | 160 | echo "$@" 161 | } 162 | 163 | fn-help-list-example() { 164 | # shellcheck disable=SC2034 165 | declare desc="return $PLUGIN_COMMAND_PREFIX plugin help content" 166 | cat <* ]] && line="\n ${BOLD}${line}${NORMAL}" 243 | # shellcheck disable=SC2001 244 | [[ "$line" == " "* ]] && line=" ${OTHER_GRAY}$(echo "$line" | sed -e 's/^[[:space:]]*//')${NORMAL}" 245 | echo -e "${NEWLINE}${line}" 246 | LAST_LINE="sentence" 247 | NEWLINE="\n" 248 | fi 249 | done 250 | } 251 | 252 | fn-help-subcommand-list-args() { 253 | declare FUNC_FILE="$1" 254 | local EXAMPLE LIGHT_GRAY NORMAL 255 | 256 | FLAGS=$(grep "#A" "$FUNC_FILE" | cut -d'A' -f2- | sed -e 's/^[[:space:]]*//' || true) 257 | if [[ -z "$FLAGS" ]]; then 258 | return 0 259 | fi 260 | 261 | NORMAL="$(fn-help-fancy-color "\033[m")" 262 | LIGHT_GRAY="$(fn-help-fancy-color "\033[2;37m")" 263 | 264 | _fn-help-apply-shell-expansion "$FLAGS" | while read -r line; do 265 | echo -e "$(echo "$line" | cut -d',' -f1),${LIGHT_GRAY}$(echo "$line" | cut -d',' -f2-)${NORMAL}" 266 | done 267 | } 268 | 269 | fn-help-subcommand-list-flags() { 270 | declare FUNC_FILE="$1" 271 | local EXAMPLE LIGHT_GRAY NORMAL 272 | 273 | FLAGS=$(grep "#F" "$FUNC_FILE" | cut -d'F' -f2- | sed -e 's/^[[:space:]]*//' || true) 274 | if [[ -z "$FLAGS" ]]; then 275 | return 0 276 | fi 277 | 278 | NORMAL="$(fn-help-fancy-color "\033[m")" 279 | LIGHT_GRAY="$(fn-help-fancy-color "\033[2;37m")" 280 | 281 | _fn-help-apply-shell-expansion "$FLAGS" | while read -r line; do 282 | echo -e "$(echo "$line" | cut -d',' -f1),${LIGHT_GRAY}$(echo "$line" | cut -d',' -f2-)${NORMAL}" 283 | done 284 | } 285 | 286 | fn-help-subcommand-sanitize() { 287 | declare FUNC_FILE="$1" OUTGOING_FUNC_FILE="$2" 288 | local FUNCTION_FOUND=false 289 | local IFS OIFS 290 | 291 | touch "$OUTGOING_FUNC_FILE" 292 | 293 | OIFS="$IFS" 294 | IFS=, 295 | while read -r p; do 296 | IFS="$OIFS" 297 | if [[ "$p" == *"-cmd \"\$@\""* ]] || [[ "$p" == "" ]]; then 298 | continue 299 | fi 300 | 301 | if [[ "$FUNCTION_FOUND" == true ]]; then 302 | echo "$p" >>"$OUTGOING_FUNC_FILE" 303 | continue 304 | fi 305 | 306 | if [[ "$p" == *"()"* ]]; then 307 | FUNCTION_FOUND=true 308 | echo "$p" >>"$OUTGOING_FUNC_FILE" 309 | continue 310 | fi 311 | done <"$FUNC_FILE" 312 | } 313 | 314 | _fn-help-apply-shell-expansion() { 315 | declare desc="expand environment variables for a shell command" 316 | declare data="$1" 317 | declare delimiter="__apply_shell_expansion_delimiter__" 318 | declare command="cat <<$delimiter"$'\n'"$data"$'\n'"$delimiter" 319 | eval "$command" 320 | } 321 | -------------------------------------------------------------------------------- /functions: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/config" 3 | set -eo pipefail 4 | [[ $DOKKU_TRACE ]] && set -x 5 | source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/common-functions" 6 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" 7 | source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions" 8 | source "$PLUGIN_AVAILABLE_PATH/config/functions" 9 | if [[ -f "$PLUGIN_AVAILABLE_PATH/docker-options/functions" ]]; then 10 | source "$PLUGIN_AVAILABLE_PATH/docker-options/functions" 11 | fi 12 | 13 | service_connect() { 14 | local SERVICE="$1" 15 | local SERVICE_ROOT="$PLUGIN_DATA_ROOT/$SERVICE" 16 | local SERVICE_NAME="$(get_service_name "$SERVICE")" 17 | local DATABASE_NAME="$(get_database_name "$SERVICE")" 18 | local PASSWORD="$(service_password "$SERVICE")" 19 | local SERVICE_TTY_OPTS 20 | has_tty && SERVICE_TTY_OPTS="-t" 21 | 22 | "$DOCKER_BIN" container exec --env=LANG=C.UTF-8 --env=LC_ALL=C.UTF-8 -i $SERVICE_TTY_OPTS "$SERVICE_NAME" mysql --user=mysql --password="$PASSWORD" --database="$DATABASE_NAME" 23 | } 24 | 25 | service_create() { 26 | local SERVICE="$1" 27 | is_valid_service_name "$SERVICE" || dokku_log_fail "Please specify a valid name for the service. Valid characters are: [A-Za-z0-9_]+" 28 | [[ -z "$SERVICE" ]] && dokku_log_fail "Please specify a valid name for the service" 29 | [[ ! -d "$PLUGIN_DATA_ROOT/$SERVICE" ]] || dokku_log_fail "$PLUGIN_SERVICE service $SERVICE already exists" 30 | SERVICE_ROOT="$PLUGIN_DATA_ROOT/$SERVICE" 31 | LINKS_FILE="$SERVICE_ROOT/LINKS" 32 | 33 | service_parse_args "${@:2}" 34 | 35 | if ! service_image_exists "$SERVICE"; then 36 | if [[ "$PLUGIN_DISABLE_PULL" == "true" ]]; then 37 | dokku_log_warn "${PLUGIN_DISABLE_PULL_VARIABLE} environment variable detected. Not running pull command." 1>&2 38 | dokku_log_warn " docker image pull ${IMAGE}" 1>&2 39 | dokku_log_warn "$PLUGIN_SERVICE service creation failed" 40 | exit 1 41 | fi 42 | "$DOCKER_BIN" image pull "$PLUGIN_IMAGE:$PLUGIN_IMAGE_VERSION" || dokku_log_fail "$PLUGIN_SERVICE image $PLUGIN_IMAGE:$PLUGIN_IMAGE_VERSION pull failed" 43 | fi 44 | 45 | plugn trigger service-action pre-create "$PLUGIN_COMMAND_PREFIX" "$SERVICE" 46 | mkdir -p "$SERVICE_ROOT" || dokku_log_fail "Unable to create service directory" 47 | mkdir -p "$SERVICE_ROOT/data" || dokku_log_fail "Unable to create service data directory" 48 | mkdir -p "$SERVICE_ROOT/$PLUGIN_CONFIG_SUFFIX" || dokku_log_fail "Unable to create service config directory" 49 | touch "$LINKS_FILE" 50 | 51 | echo -e "[mysqld]\nperformance_schema = 0" >"$SERVICE_ROOT/$PLUGIN_CONFIG_SUFFIX/disable_performance_schema.cnf" 52 | echo -e "[mysqld]\ncharacter-set-server = utf8mb4\ncollation-server = utf8mb4_unicode_ci" >"$SERVICE_ROOT/$PLUGIN_CONFIG_SUFFIX/charset_utf8.cnf" 53 | ROOTPASSWORD=$(openssl rand -hex 8) 54 | PASSWORD=$(openssl rand -hex 8) 55 | if [[ -n "$SERVICE_PASSWORD" ]]; then 56 | PASSWORD="$SERVICE_PASSWORD" 57 | dokku_log_warn "Specified password may not be as secure as the auto-generated password" 58 | fi 59 | if [[ -n "$SERVICE_ROOT_PASSWORD" ]]; then 60 | ROOTPASSWORD="$SERVICE_ROOT_PASSWORD" 61 | dokku_log_warn "Specified root password may not be as secure as the auto-generated root password" 62 | fi 63 | echo "$ROOTPASSWORD" >"$SERVICE_ROOT/ROOTPASSWORD" 64 | echo "$PASSWORD" >"$SERVICE_ROOT/PASSWORD" 65 | chmod 640 "$SERVICE_ROOT/ROOTPASSWORD" "$SERVICE_ROOT/PASSWORD" 66 | 67 | service_commit_config "$SERVICE" 68 | write_database_name "$SERVICE" 69 | plugn trigger service-action post-create "$PLUGIN_COMMAND_PREFIX" "$SERVICE" 70 | service_create_container "$SERVICE" 71 | plugn trigger service-action post-create-complete "$PLUGIN_COMMAND_PREFIX" "$SERVICE" 72 | } 73 | 74 | service_create_container() { 75 | local SERVICE="$1" 76 | local SERVICE_ROOT="$PLUGIN_DATA_ROOT/$SERVICE" 77 | local SERVICE_HOST_ROOT="$PLUGIN_DATA_HOST_ROOT/$SERVICE" 78 | local SERVICE_NAME="$(get_service_name "$SERVICE")" 79 | local ROOTPASSWORD="$(service_root_password "$SERVICE")" 80 | local PASSWORD="$(service_password "$SERVICE")" 81 | local DATABASE_NAME="$(get_database_name "$SERVICE")" 82 | local MEMORY_LIMIT="" 83 | 84 | if [[ -f "$SERVICE_ROOT/CONFIG_OPTIONS" ]]; then 85 | export CONFIG_OPTIONS="$(cat "$SERVICE_ROOT/CONFIG_OPTIONS")" 86 | fi 87 | 88 | local network_alias="$(service_dns_hostname "$SERVICE")" 89 | 90 | rm -f "$SERVICE_ROOT/ID" 91 | declare -a DOCKER_ARGS 92 | DOCKER_ARGS=() 93 | DOCKER_ARGS+=("--cidfile=$SERVICE_ROOT/ID") 94 | DOCKER_ARGS+=("--env-file=$SERVICE_ROOT/ENV") 95 | DOCKER_ARGS+=("--env=MYSQL_DATABASE=$DATABASE_NAME") 96 | DOCKER_ARGS+=("--env=MYSQL_PASSWORD=$PASSWORD") 97 | DOCKER_ARGS+=("--env=MYSQL_ROOT_PASSWORD=$ROOTPASSWORD") 98 | DOCKER_ARGS+=("--env=MYSQL_USER=mysql") 99 | DOCKER_ARGS+=("--hostname=$SERVICE_NAME") 100 | DOCKER_ARGS+=("--label=dokku.service=$PLUGIN_COMMAND_PREFIX") 101 | DOCKER_ARGS+=("--label=dokku=service") 102 | DOCKER_ARGS+=("--name=$SERVICE_NAME") 103 | DOCKER_ARGS+=("--restart=always") 104 | DOCKER_ARGS+=("--volume=$SERVICE_HOST_ROOT/$PLUGIN_CONFIG_SUFFIX:/etc/mysql/conf.d") 105 | DOCKER_ARGS+=("--volume=$SERVICE_HOST_ROOT/data:/var/lib/mysql") 106 | 107 | declare -a LINK_CONTAINER_DOCKER_ARGS 108 | LINK_CONTAINER_DOCKER_ARGS=() 109 | LINK_CONTAINER_DOCKER_ARGS+=("--rm") 110 | LINK_CONTAINER_DOCKER_ARGS+=("--link") 111 | LINK_CONTAINER_DOCKER_ARGS+=("$SERVICE_NAME:$network_alias") 112 | 113 | [[ -f "$SERVICE_ROOT/SERVICE_MEMORY" ]] && SERVICE_MEMORY="$(cat "$SERVICE_ROOT/SERVICE_MEMORY")" 114 | if [[ -n "$SERVICE_MEMORY" ]]; then 115 | DOCKER_ARGS+=("--memory=${SERVICE_MEMORY}m") 116 | fi 117 | 118 | [[ -f "$SERVICE_ROOT/SHM_SIZE" ]] && SERVICE_SHM_SIZE="$(cat "$SERVICE_ROOT/SHM_SIZE")" 119 | if [[ -n "$SERVICE_SHM_SIZE" ]]; then 120 | DOCKER_ARGS+=("--shm-size=${SERVICE_SHM_SIZE}") 121 | fi 122 | 123 | [[ -f "$SERVICE_ROOT/IMAGE" ]] && PLUGIN_IMAGE="$(cat "$SERVICE_ROOT/IMAGE")" 124 | [[ -f "$SERVICE_ROOT/IMAGE_VERSION" ]] && PLUGIN_IMAGE_VERSION="$(cat "$SERVICE_ROOT/IMAGE_VERSION")" 125 | 126 | local network="$(fn-plugin-property-get "$PLUGIN_COMMAND_PREFIX" "$SERVICE" "initial-network")" 127 | if [[ -n "$network" ]]; then 128 | DOCKER_ARGS+=("--network=${network}") 129 | DOCKER_ARGS+=("--network-alias=${network_alias}") 130 | LINK_CONTAINER_DOCKER_ARGS+=("--network=${network}") 131 | fi 132 | 133 | # shellcheck disable=SC2086 134 | suppress_output "$DOCKER_BIN" container create "${DOCKER_ARGS[@]}" "$PLUGIN_IMAGE:$PLUGIN_IMAGE_VERSION" $CONFIG_OPTIONS 135 | 136 | if [[ -n "$(fn-plugin-property-get "$PLUGIN_COMMAND_PREFIX" "$SERVICE" "post-create-network")" ]]; then 137 | dokku_log_verbose_quiet "Connecting to networks after container create" 138 | while read -r line || [[ -n "$line" ]]; do 139 | dokku_log_verbose_quiet "- $line" 140 | "$DOCKER_BIN" network connect --alias "$network_alias" "$line" "$SERVICE_NAME" 141 | done < <(fn-plugin-property-get "$PLUGIN_COMMAND_PREFIX" "$SERVICE" "post-create-network" | tr "," "\n") 142 | fi 143 | suppress_output "$DOCKER_BIN" container start "$(cat "$SERVICE_ROOT/ID")" 144 | service_port_reconcile_status "$SERVICE" 145 | 146 | if [[ -n "$(fn-plugin-property-get "$PLUGIN_COMMAND_PREFIX" "$SERVICE" "post-start-network")" ]]; then 147 | dokku_log_verbose_quiet "Connecting to networks after container start" 148 | while read -r line || [[ -n "$line" ]]; do 149 | dokku_log_verbose_quiet "- $line" 150 | "$DOCKER_BIN" network connect --alias "$network_alias" "$line" "$SERVICE_NAME" 151 | done < <(fn-plugin-property-get "$PLUGIN_COMMAND_PREFIX" "$SERVICE" "post-start-network" | tr "," "\n") 152 | fi 153 | 154 | dokku_log_verbose_quiet "Waiting for container to be ready" 155 | if ! suppress_output "$DOCKER_BIN" container run "${LINK_CONTAINER_DOCKER_ARGS[@]}" "$PLUGIN_WAIT_IMAGE" -c "$network_alias:$PLUGIN_DATASTORE_WAIT_PORT"; then 156 | dokku_log_info2_quiet "Start of $SERVICE container output" 157 | dokku_container_log_verbose_quiet "$SERVICE_NAME" 158 | dokku_log_info2_quiet "End of $SERVICE container output" 159 | return 1 160 | fi 161 | 162 | dokku_log_info2 "$PLUGIN_SERVICE container created: $SERVICE" 163 | service_info "$SERVICE" 164 | } 165 | 166 | service_export() { 167 | local SERVICE="$1" 168 | local SERVICE_ROOT="$PLUGIN_DATA_ROOT/$SERVICE" 169 | local SERVICE_NAME="$(get_service_name "$SERVICE")" 170 | local DATABASE_NAME="$(get_database_name "$SERVICE")" 171 | local ROOTPASSWORD="$(service_root_password "$SERVICE")" 172 | 173 | [[ -n $SSH_TTY ]] && stty -opost 174 | "$DOCKER_BIN" container exec "$SERVICE_NAME" mysqldump --default-character-set=utf8mb4 --user=root --password="$ROOTPASSWORD" --single-transaction --no-tablespaces --quick "$DATABASE_NAME" 175 | status=$? 176 | [[ -n $SSH_TTY ]] && stty opost 177 | exit $status 178 | } 179 | 180 | service_import() { 181 | local SERVICE="$1" 182 | local SERVICE_ROOT="$PLUGIN_DATA_ROOT/$SERVICE" 183 | local SERVICE_NAME="$(get_service_name "$SERVICE")" 184 | local SERVICE_HOST_ROOT="$PLUGIN_DATA_HOST_ROOT/$SERVICE" 185 | local DATABASE_NAME="$(get_database_name "$SERVICE")" 186 | local ROOTPASSWORD="$(service_root_password "$SERVICE")" 187 | 188 | if [[ -t 0 ]]; then 189 | dokku_log_fail "No data provided on stdin." 190 | fi 191 | "$DOCKER_BIN" container exec -i "$SERVICE_NAME" mysql --user=root --password="$ROOTPASSWORD" "$DATABASE_NAME" 192 | } 193 | 194 | service_start() { 195 | local SERVICE="$1" 196 | local QUIET="$2" 197 | local SERVICE_ROOT="$PLUGIN_DATA_ROOT/$SERVICE" 198 | local SERVICE_NAME="$(get_service_name "$SERVICE")" 199 | local ID=$("$DOCKER_BIN" container ps -aq --no-trunc --filter "status=running" --filter "name=^/$SERVICE_NAME$") || true 200 | if [[ -n $ID ]]; then 201 | [[ -z $QUIET ]] && dokku_log_warn "Service is already started" 202 | if [[ ! -f "$SERVICE_ROOT/ID" ]] || [[ "$(cat "$SERVICE_ROOT/ID")" != "$ID" ]]; then 203 | [[ -z $QUIET ]] && dokku_log_warn "Updating local container ID" 204 | echo "$ID" >"$SERVICE_ROOT/ID" 205 | fi 206 | return 0 207 | fi 208 | 209 | dokku_log_info2_quiet "Starting container" 210 | local PREVIOUS_ID=$("$DOCKER_BIN" container ps -aq --no-trunc --filter "status=exited" --filter "name=^/$SERVICE_NAME$") || true 211 | local ROOTPASSWORD="$(service_root_password "$SERVICE")" 212 | local PASSWORD="$(service_password "$SERVICE")" 213 | 214 | if [[ -n $PREVIOUS_ID ]]; then 215 | "$DOCKER_BIN" container start "$PREVIOUS_ID" >/dev/null 216 | service_port_reconcile_status "$SERVICE" 217 | dokku_log_info2 "Container started" 218 | elif service_image_exists "$SERVICE" && [[ -n "$ROOTPASSWORD" ]] && [[ -n "$PASSWORD" ]]; then 219 | service_create_container "$SERVICE" 220 | else 221 | if ! service_image_exists "$SERVICE"; then 222 | [[ -f "$SERVICE_ROOT/IMAGE" ]] && PLUGIN_IMAGE="$(cat "$SERVICE_ROOT/IMAGE")" 223 | [[ -f "$SERVICE_ROOT/IMAGE_VERSION" ]] && PLUGIN_IMAGE_VERSION="$(cat "$SERVICE_ROOT/IMAGE_VERSION")" 224 | dokku_log_verbose_quiet "Missing image $PLUGIN_IMAGE:$PLUGIN_IMAGE_VERSION for $SERVICE" 225 | else 226 | dokku_log_verbose_quiet "Neither container nor valid configuration exists for $SERVICE" 227 | fi 228 | fi 229 | } 230 | 231 | service_url() { 232 | local SERVICE="$1" 233 | local SERVICE_DNS_HOSTNAME="$(service_dns_hostname "$SERVICE")" 234 | local DATABASE_NAME="$(get_database_name "$SERVICE")" 235 | local PASSWORD="$(service_password "$SERVICE")" 236 | echo "$PLUGIN_SCHEME://mysql:$PASSWORD@$SERVICE_DNS_HOSTNAME:${PLUGIN_DATASTORE_PORTS[0]}/$DATABASE_NAME" 237 | } 238 | -------------------------------------------------------------------------------- /bin/generate: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | from __future__ import print_function 4 | 5 | import os 6 | import re 7 | 8 | 9 | def compile( 10 | service, 11 | version, 12 | variable, 13 | alias, 14 | image, 15 | scheme, 16 | ports, 17 | sponsors, 18 | options, 19 | unimplemented, 20 | dokku_version, 21 | ): 22 | prefix = "\n\n".join( 23 | [ 24 | header(service), 25 | description(service, image, version), 26 | ] 27 | ) 28 | 29 | if len(sponsors) > 0: 30 | prefix += "\n\n" 31 | prefix += sponsors_section(service, sponsors) 32 | 33 | return ( 34 | "\n\n".join( 35 | [ 36 | prefix, 37 | requirements_section(dokku_version), 38 | installation_section(service, dokku_version), 39 | commands_section( 40 | service, variable, alias, image, scheme, ports, unimplemented 41 | ), 42 | usage_section( 43 | service, 44 | variable, 45 | alias, 46 | image, 47 | scheme, 48 | ports, 49 | options, 50 | unimplemented, 51 | ), 52 | ] 53 | ) 54 | .replace("\n\n\n\n\n", "\n") 55 | .replace("\n\n\n\n", "\n") 56 | .replace("\n\n\n", "\n\n") 57 | ) 58 | 59 | 60 | def header(service): 61 | return " ".join( 62 | [ 63 | f"# dokku {service}", 64 | f'[![Build Status](https://img.shields.io/github/actions/workflow/status/dokku/dokku-{service}/ci.yml?branch=master&style=flat-square "Build Status")](https://github.com/dokku/dokku-{service}/actions/workflows/ci.yml?query=branch%3Amaster)', 65 | f'[![IRC Network](https://img.shields.io/badge/irc-libera-blue.svg?style=flat-square "IRC Libera")](https://webchat.libera.chat/?channels=dokku)', 66 | ] 67 | ) 68 | 69 | 70 | def description(service, full_image, version): 71 | base = "_" 72 | image = full_image 73 | if "/" in full_image: 74 | base = "r/" + full_image.split("/")[0] 75 | image = full_image.split("/")[1] 76 | 77 | return f"Official {service} plugin for dokku. Currently defaults to installing [{full_image} {version}](https://hub.docker.com/{base}/{image}/)." 78 | 79 | 80 | def sponsors_section(service, sponsors): 81 | if len(sponsors) == 0: 82 | return "" 83 | 84 | sponsor_data = [ 85 | "## Sponsors", 86 | "", 87 | f"The {service} plugin was generously sponsored by the following:", 88 | "", 89 | ] 90 | sponsor_data.extend([f"- [{s}](https://github.com/{s})" for s in sponsors]) 91 | 92 | return "\n".join(sponsor_data) 93 | 94 | 95 | def requirements_section(dokku_version): 96 | return "\n".join( 97 | [ 98 | "## Requirements", 99 | "", 100 | f"- dokku {dokku_version}", 101 | "- docker 1.8.x", 102 | ] 103 | ) 104 | 105 | 106 | def installation_section(service, dokku_version): 107 | return "\n".join( 108 | [ 109 | "## Installation", 110 | "", 111 | "```shell", 112 | f"# on {dokku_version}", 113 | f"sudo dokku plugin:install https://github.com/dokku/dokku-{service}.git --name {service}", 114 | "```", 115 | ] 116 | ) 117 | 118 | 119 | def commands_section(service, variable, alias, image, scheme, ports, unimplemented): 120 | content = [ 121 | "## Commands", 122 | "", 123 | "```", 124 | ] 125 | 126 | subcommands = os.listdir("subcommands") 127 | subcommands.sort() 128 | 129 | command_list = [] 130 | descriptions = [] 131 | for filename in subcommands: 132 | if filename in unimplemented: 133 | continue 134 | data = command_data(filename, service, variable, alias, image, scheme, ports) 135 | description = data["description"] 136 | arguments = data["arguments_string"] 137 | 138 | command_list.append(f"{service}:{filename} {arguments}") 139 | descriptions.append(description) 140 | 141 | maxlen = max(map(len, command_list)) 142 | if maxlen > 50: 143 | maxlen = 50 144 | for command, description in zip(command_list, descriptions): 145 | space_count = maxlen - len(command) 146 | content.append("{0}{1} # {2}".format(command, " " * space_count, description)) 147 | 148 | content.append("```") 149 | return "\n".join(content) 150 | 151 | 152 | def usage_section( 153 | service, variable, alias, image, scheme, ports, options, unimplemented 154 | ): 155 | return "\n\n".join( 156 | [ 157 | "## Usage", 158 | f"Help for any commands can be displayed by specifying the command as an argument to {service}:help. Plugin help output in conjunction with any files in the `docs/` folder is used to generate the plugin documentation. Please consult the `{service}:help` command for any undocumented commands.", 159 | usage_intro( 160 | service, variable, alias, image, scheme, ports, options, unimplemented 161 | ), 162 | usage_lifecycle( 163 | service, variable, alias, image, scheme, ports, options, unimplemented 164 | ), 165 | usage_automation( 166 | service, variable, alias, image, scheme, ports, options, unimplemented 167 | ), 168 | usage_data_management( 169 | service, variable, alias, image, scheme, ports, options, unimplemented 170 | ), 171 | usage_backup( 172 | service, variable, alias, image, scheme, ports, options, unimplemented 173 | ), 174 | usage_docker_pull( 175 | service, variable, alias, image, scheme, ports, options, unimplemented 176 | ), 177 | ] 178 | ) 179 | 180 | 181 | def usage_intro(service, variable, alias, image, scheme, ports, options, unimplemented): 182 | commands = ["create", "info", "list", "logs", "link", "unlink", "set"] 183 | content = ["### Basic Usage"] 184 | 185 | return fetch_commands_content( 186 | service, 187 | variable, 188 | alias, 189 | image, 190 | scheme, 191 | ports, 192 | options, 193 | unimplemented, 194 | commands, 195 | content, 196 | ) 197 | 198 | 199 | def usage_lifecycle( 200 | service, variable, alias, image, scheme, ports, options, unimplemented 201 | ): 202 | commands = [ 203 | "connect", 204 | "enter", 205 | "expose", 206 | "unexpose", 207 | "promote", 208 | "start", 209 | "stop", 210 | "pause", 211 | "restart", 212 | "upgrade", 213 | ] 214 | content = [ 215 | "### Service Lifecycle", 216 | "", 217 | "The lifecycle of each service can be managed through the following commands:", 218 | "", 219 | ] 220 | 221 | return fetch_commands_content( 222 | service, 223 | variable, 224 | alias, 225 | image, 226 | scheme, 227 | ports, 228 | options, 229 | unimplemented, 230 | commands, 231 | content, 232 | ) 233 | 234 | 235 | def usage_automation( 236 | service, variable, alias, image, scheme, ports, options, unimplemented 237 | ): 238 | commands = ["app-links", "clone", "exists", "linked", "links"] 239 | content = [ 240 | "### Service Automation", 241 | "", 242 | "Service scripting can be executed using the following commands:", 243 | "", 244 | ] 245 | 246 | return fetch_commands_content( 247 | service, 248 | variable, 249 | alias, 250 | image, 251 | scheme, 252 | ports, 253 | options, 254 | unimplemented, 255 | commands, 256 | content, 257 | ) 258 | 259 | 260 | def usage_data_management( 261 | service, variable, alias, image, scheme, ports, options, unimplemented 262 | ): 263 | commands = ["import", "export"] 264 | content = [ 265 | "### Data Management", 266 | "", 267 | "The underlying service data can be imported and exported with the following commands:", 268 | "", 269 | ] 270 | 271 | return fetch_commands_content( 272 | service, 273 | variable, 274 | alias, 275 | image, 276 | scheme, 277 | ports, 278 | options, 279 | unimplemented, 280 | commands, 281 | content, 282 | ) 283 | 284 | 285 | def usage_backup( 286 | service, variable, alias, image, scheme, ports, options, unimplemented 287 | ): 288 | commands = [ 289 | "backup-auth", 290 | "backup-deauth", 291 | "backup", 292 | "backup-set-encryption", 293 | "backup-set-public-key-encryption", 294 | "backup-unset-encryption", 295 | "backup-unset-public-key-encryption", 296 | "backup-schedule", 297 | "backup-schedule-cat", 298 | "backup-unschedule", 299 | ] 300 | content = [ 301 | "### Backups", 302 | "", 303 | "Datastore backups are supported via AWS S3 and S3 compatible services like [minio](https://github.com/minio/minio).", 304 | "", 305 | "You may skip the `backup-auth` step if your dokku install is running within EC2 and has access to the bucket via an IAM profile. In that case, use the `--use-iam` option with the `backup` command.", 306 | "", 307 | "If both passphrase and public key forms of encryption are set, the public key encryption will take precedence.", 308 | "", 309 | "The underlying core backup script is present [here](https://github.com/dokku/docker-s3backup/blob/main/backup.sh).", 310 | "", 311 | "Backups can be performed using the backup commands:", 312 | "", 313 | ] 314 | 315 | return fetch_commands_content( 316 | service, 317 | variable, 318 | alias, 319 | image, 320 | scheme, 321 | ports, 322 | options, 323 | unimplemented, 324 | commands, 325 | content, 326 | ) 327 | 328 | 329 | def usage_docker_pull( 330 | service, variable, alias, image, scheme, ports, options, unimplemented 331 | ): 332 | service_prefix = service.upper() 333 | return "\n".join( 334 | [ 335 | "### Disabling `docker image pull` calls", 336 | "", 337 | f"If you wish to disable the `docker image pull` calls that the plugin triggers, you may set the `{service_prefix}_DISABLE_PULL` environment variable to `true`. Once disabled, you will need to pull the service image you wish to deploy as shown in the `stderr` output.", 338 | "", 339 | "Please ensure the proper images are in place when `docker image pull` is disabled.", 340 | ] 341 | ) 342 | 343 | 344 | def fetch_commands_content( 345 | service, 346 | variable, 347 | alias, 348 | image, 349 | scheme, 350 | ports, 351 | options, 352 | unimplemented, 353 | commands, 354 | content, 355 | ): 356 | i = 0 357 | for command in commands: 358 | output = command_help( 359 | command, 360 | service, 361 | variable, 362 | alias, 363 | image, 364 | scheme, 365 | ports, 366 | options, 367 | unimplemented, 368 | ) 369 | if output == "": 370 | continue 371 | content.append(output) 372 | i += 1 373 | 374 | if i == 0: 375 | return "" 376 | 377 | return "\n".join(content) 378 | 379 | 380 | def parse_args(line): 381 | line = line.strip() 382 | arguments = [] 383 | for arg in re.findall("([A-Z_]+)", line): 384 | arg = arg.replace("_", "-").lower() 385 | if arg.endswith("optional-flag"): 386 | arg = arg.replace("-optional-flag", "") 387 | arguments.append(f"[--{arg}]") 388 | elif arg.endswith("-flag"): 389 | if arg == "info-flag": 390 | arguments.append(f"[--single-info-flag]") 391 | else: 392 | arg = arg.replace("-flag", "") 393 | first_letter = arg[0] 394 | arguments.append(f"[-{first_letter}|--{arg}]") 395 | elif arg.endswith("-flags-list"): 396 | arg = arg.replace("-list", "") 397 | arguments.append(f"[--{arg}...]") 398 | elif arg.endswith("list"): 399 | arg = arg.replace("-list", "") 400 | arguments.append(f"<{arg}...>") 401 | else: 402 | arguments.append(f"<{arg}>") 403 | return " ".join(arguments) 404 | 405 | 406 | def command_help( 407 | command, service, variable, alias, image, scheme, ports, options, unimplemented 408 | ): 409 | if command in unimplemented: 410 | return "" 411 | 412 | data = command_data(command, service, variable, alias, image, scheme, ports) 413 | content = [ 414 | f"### {data['description']}", 415 | "", 416 | "```shell", 417 | "# usage", 418 | f"dokku {service}:{command} {data['arguments_string']}".strip(), 419 | "```", 420 | ] 421 | 422 | # if len(data["arguments"]) > 0: 423 | # content.append("") 424 | # content.append("arguments:") 425 | # content.append("") 426 | # for argument in data["arguments"]: 427 | # content.append(f"- {argument}") 428 | 429 | if len(data["flags"]) > 0: 430 | content.append("") 431 | content.append("flags:") 432 | content.append("") 433 | for flag in data["flags"]: 434 | if "--config-options" in flag and options != "": 435 | flag = f"{flag} (default: `{options}`)" 436 | content.append(f"- {flag}") 437 | 438 | if len(data["examples"]) > 0: 439 | content.append("") 440 | content.append(data["examples"]) 441 | 442 | doc_file = os.path.join("docs", f"{command}.md") 443 | if os.path.isfile(doc_file): 444 | content.append("") 445 | with open(doc_file) as f: 446 | content.append(f.read()) 447 | 448 | return "\n" + "\n".join(content) 449 | 450 | 451 | def command_data(command, service, variable, alias, image, scheme, ports): 452 | description = None 453 | arguments = [] 454 | arguments_string = "" 455 | example_lines = [] 456 | flags = [] 457 | with open(os.path.join("subcommands", command)) as f: 458 | for line in f.readlines(): 459 | line = line.strip() 460 | line = line.replace("$PLUGIN_SERVICE", service) 461 | line = line.replace("$PLUGIN_COMMAND_PREFIX", service) 462 | line = line.replace("${PLUGIN_COMMAND_PREFIX}", service) 463 | line = line.replace("${PLUGIN_VARIABLE}", variable) 464 | line = line.replace("${PLUGIN_DEFAULT_ALIAS}", alias) 465 | line = line.replace("${PLUGIN_IMAGE}", image) 466 | line = line.replace("${PLUGIN_SCHEME}", scheme) 467 | line = line.replace("${PLUGIN_DATASTORE_PORTS[0]}", ports[0]) 468 | line = line.replace("${PLUGIN_DATASTORE_PORTS[@]}", " ".join(ports)) 469 | 470 | if "declare desc" in line: 471 | description = re.search('"(.+)"', line).group(1) 472 | elif "$1" in line: 473 | arguments_string = parse_args(line) 474 | elif line.startswith("#A "): 475 | argument = line.replace("#A ", "") 476 | parts = [a.strip() for a in argument.split(",", 1)] 477 | arguments.append(f"`{parts[0]}`: {parts[1]}") 478 | elif line.startswith("#F "): 479 | flag = line.replace("#F ", "") 480 | parts = [a.strip() for a in flag.split(",", 1)] 481 | flags.append(f"`{parts[0]}`: {parts[1]}") 482 | elif line.startswith("#E "): 483 | example_lines.append(line.replace("#E ", "")) 484 | 485 | examples = [] 486 | sentence_lines = [] 487 | command_lines = [] 488 | codeblock_lines = [] 489 | blockquote_lines = [] 490 | for line in example_lines: 491 | if line.startswith("export") or line.startswith("dokku"): 492 | if len(blockquote_lines) > 0: 493 | examples.append("\n" + process_blockquote(blockquote_lines)) 494 | blockquote_lines = [] 495 | if len(codeblock_lines) > 0: 496 | examples.append("\n" + process_codeblock(codeblock_lines)) 497 | codeblock_lines = [] 498 | if len(sentence_lines) > 0: 499 | examples.append("\n" + process_sentence(sentence_lines)) 500 | sentence_lines = [] 501 | 502 | command_lines.append(line) 503 | elif line.startswith(" "): 504 | if len(blockquote_lines) > 0: 505 | examples.append("\n" + process_blockquote(blockquote_lines)) 506 | blockquote_lines = [] 507 | if len(command_lines) > 0: 508 | examples.append("\n" + process_command(command_lines)) 509 | command_lines = [] 510 | if len(sentence_lines) > 0: 511 | examples.append("\n" + process_sentence(sentence_lines)) 512 | sentence_lines = [] 513 | 514 | codeblock_lines.append(line.strip()) 515 | elif line.startswith(">"): 516 | if len(codeblock_lines) > 0: 517 | examples.append("\n" + process_codeblock(codeblock_lines)) 518 | codeblock_lines = [] 519 | if len(command_lines) > 0: 520 | examples.append("\n" + process_command(command_lines)) 521 | command_lines = [] 522 | if len(sentence_lines) > 0: 523 | examples.append("\n" + process_sentence(sentence_lines)) 524 | sentence_lines = [] 525 | 526 | blockquote_lines.append(line) 527 | else: 528 | if len(blockquote_lines) > 0: 529 | examples.append("\n" + process_blockquote(blockquote_lines)) 530 | blockquote_lines = [] 531 | if len(codeblock_lines) > 0: 532 | examples.append("\n" + process_codeblock(codeblock_lines)) 533 | codeblock_lines = [] 534 | if len(command_lines) > 0: 535 | examples.append("\n" + process_command(command_lines)) 536 | command_lines = [] 537 | 538 | sentence_lines.append(line) 539 | 540 | if len(blockquote_lines) > 0: 541 | examples.append("\n" + process_blockquote(blockquote_lines)) 542 | blockquote_lines = [] 543 | if len(codeblock_lines) > 0: 544 | examples.append("\n" + process_codeblock(codeblock_lines)) 545 | codeblock_lines = [] 546 | if len(command_lines) > 0: 547 | examples.append("\n" + process_command(command_lines)) 548 | command_lines = [] 549 | if len(sentence_lines) > 0: 550 | examples.append("\n" + process_sentence(sentence_lines)) 551 | sentence_lines = [] 552 | 553 | return { 554 | "description": description, 555 | "arguments_string": arguments_string, 556 | "arguments": arguments, 557 | "flags": flags, 558 | "examples": "\n".join(examples).strip(), 559 | } 560 | 561 | 562 | def process_sentence(sentence_lines): 563 | sentence_lines = " ".join(sentence_lines) 564 | sentences = ". ".join( 565 | upperfirst(i.strip()) for i in sentence_lines.split(". ") 566 | ).strip() 567 | if not sentences.endswith(".") and not sentences.endswith(":"): 568 | sentences += ":" 569 | 570 | text = [] 571 | for sentence in sentences.split(". "): 572 | parts = [] 573 | for word in sentence.strip().split(" "): 574 | if word.isupper() and len(word) > 1: 575 | for ending in [":", "."]: 576 | if word.endswith(ending): 577 | word = "`{0}`{1}".format(word[:-1], ending) 578 | else: 579 | word = "`{0}`".format(word) 580 | parts.append(word) 581 | text.append(" ".join(parts)) 582 | 583 | text = ". ".join(text) 584 | 585 | # some cleanup 586 | text = text.replace("(0.0.0.0)", "(`0.0.0.0`)") 587 | text = text.replace("'", "`") 588 | text = text.replace("`s", "'s") 589 | text = text.replace("``", "`") 590 | text = text.strip(" ") 591 | 592 | return text 593 | 594 | 595 | def upperfirst(x): 596 | return x[:1].upper() + x[1:] 597 | 598 | 599 | def process_blockquote(blockquote_lines): 600 | return "\n".join(blockquote_lines) 601 | 602 | 603 | def process_command(command_lines): 604 | command_lines = "\n".join(command_lines) 605 | return f"```shell\n{command_lines}\n```" 606 | 607 | 608 | def process_codeblock(codeblock_lines): 609 | codeblock_lines = "\n".join(codeblock_lines) 610 | return f"```\n{codeblock_lines}\n```" 611 | 612 | 613 | def main(): 614 | service = None 615 | version = None 616 | variable = None 617 | image = None 618 | alias = None 619 | options = None 620 | unimplemented = [] 621 | 622 | with open("Dockerfile") as f: 623 | for line in f.readlines(): 624 | if "FROM " in line: 625 | image, version = line.split(" ")[1].split(":") 626 | image = image.strip() 627 | version = version.strip() 628 | 629 | with open("config") as f: 630 | for line in f.readlines(): 631 | if "PLUGIN_COMMAND_PREFIX=" in line: 632 | service = re.search('"(.+)"', line).group(1) 633 | if "PLUGIN_DEFAULT_ALIAS=" in line: 634 | alias = re.search('"(.+)"', line).group(1) 635 | if "PLUGIN_VARIABLE=" in line: 636 | variable = re.search('"(.+)"', line).group(1) 637 | if "PLUGIN_SCHEME=" in line: 638 | scheme = re.search('"(.+)"', line).group(1) 639 | if "PLUGIN_DATASTORE_PORTS=" in line: 640 | ports = re.search("\((.+)\)", line).group(1).split(" ") 641 | if "PLUGIN_UNIMPLEMENTED_SUBCOMMANDS=" in line: 642 | match = re.search("\((.+)\)", line) 643 | if match is not None: 644 | unimplemented = [s.strip('"') for s in match.group(1).split(" ")] 645 | 646 | with open("config") as f: 647 | for line in f.readlines(): 648 | if f"{variable}_CONFIG_OPTIONS" in line: 649 | match = re.search('"(.+)"', line) 650 | if match is not None: 651 | options = match.group(1) 652 | 653 | sponsors = [] 654 | with open("plugin.toml") as f: 655 | for line in f.readlines(): 656 | if line.startswith("sponsors"): 657 | sponsors = re.search('\[(["\w\s,_-]+)\]', line).group(1) 658 | sponsors = [s.strip('"') for s in sponsors.split(",")] 659 | 660 | text = compile( 661 | service, 662 | version, 663 | variable, 664 | alias, 665 | image, 666 | scheme, 667 | ports, 668 | sponsors, 669 | options, 670 | unimplemented, 671 | "0.19.x+", 672 | ) 673 | 674 | base_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) 675 | readme_file = os.path.join(base_path, "README.md") 676 | with open(readme_file, "w") as f: 677 | f.write(text + "\n") 678 | 679 | 680 | if __name__ == "__main__": 681 | main() 682 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dokku mysql [![Build Status](https://img.shields.io/github/actions/workflow/status/dokku/dokku-mysql/ci.yml?branch=master&style=flat-square "Build Status")](https://github.com/dokku/dokku-mysql/actions/workflows/ci.yml?query=branch%3Amaster) [![IRC Network](https://img.shields.io/badge/irc-libera-blue.svg?style=flat-square "IRC Libera")](https://webchat.libera.chat/?channels=dokku) 2 | 3 | Official mysql plugin for dokku. Currently defaults to installing [mysql 9.5.0](https://hub.docker.com/_/mysql/). 4 | 5 | ## Requirements 6 | 7 | - dokku 0.19.x+ 8 | - docker 1.8.x 9 | 10 | ## Installation 11 | 12 | ```shell 13 | # on 0.19.x+ 14 | sudo dokku plugin:install https://github.com/dokku/dokku-mysql.git --name mysql 15 | ``` 16 | 17 | ## Commands 18 | 19 | ``` 20 | mysql:app-links # list all mysql service links for a given app 21 | mysql:backup [--use-iam] # create a backup of the mysql service to an existing s3 bucket 22 | mysql:backup-auth # set up authentication for backups on the mysql service 23 | mysql:backup-deauth # remove backup authentication for the mysql service 24 | mysql:backup-schedule [--use-iam] # schedule a backup of the mysql service 25 | mysql:backup-schedule-cat # cat the contents of the configured backup cronfile for the service 26 | mysql:backup-set-encryption # set encryption for all future backups of mysql service 27 | mysql:backup-set-public-key-encryption # set GPG Public Key encryption for all future backups of mysql service 28 | mysql:backup-unschedule # unschedule the backup of the mysql service 29 | mysql:backup-unset-encryption # unset encryption for future backups of the mysql service 30 | mysql:backup-unset-public-key-encryption # unset GPG Public Key encryption for future backups of the mysql service 31 | mysql:clone [--clone-flags...] # create container then copy data from into 32 | mysql:connect # connect to the service via the mysql connection tool 33 | mysql:create [--create-flags...] # create a mysql service 34 | mysql:destroy [-f|--force] # delete the mysql service/data/container if there are no links left 35 | mysql:enter # enter or run a command in a running mysql service container 36 | mysql:exists # check if the mysql service exists 37 | mysql:export # export a dump of the mysql service database 38 | mysql:expose # expose a mysql service on custom host:port if provided (random port on the 0.0.0.0 interface if otherwise unspecified) 39 | mysql:import # import a dump into the mysql service database 40 | mysql:info [--single-info-flag] # print the service information 41 | mysql:link [--link-flags...] # link the mysql service to the app 42 | mysql:linked # check if the mysql service is linked to an app 43 | mysql:links # list all apps linked to the mysql service 44 | mysql:list # list all mysql services 45 | mysql:logs [-t|--tail] # print the most recent log(s) for this service 46 | mysql:pause # pause a running mysql service 47 | mysql:promote # promote service as DATABASE_URL in 48 | mysql:restart # graceful shutdown and restart of the mysql service container 49 | mysql:set # set or clear a property for a service 50 | mysql:start # start a previously stopped mysql service 51 | mysql:stop # stop a running mysql service 52 | mysql:unexpose # unexpose a previously exposed mysql service 53 | mysql:unlink # unlink the mysql service from the app 54 | mysql:upgrade [--upgrade-flags...] # upgrade service to the specified versions 55 | ``` 56 | 57 | ## Usage 58 | 59 | Help for any commands can be displayed by specifying the command as an argument to mysql:help. Plugin help output in conjunction with any files in the `docs/` folder is used to generate the plugin documentation. Please consult the `mysql:help` command for any undocumented commands. 60 | 61 | ### Basic Usage 62 | 63 | ### create a mysql service 64 | 65 | ```shell 66 | # usage 67 | dokku mysql:create [--create-flags...] 68 | ``` 69 | 70 | flags: 71 | 72 | - `-c|--config-options "--args --go=here"`: extra arguments to pass to the container create command (default: `None`) 73 | - `-C|--custom-env "USER=alpha;HOST=beta"`: semi-colon delimited environment variables to start the service with 74 | - `-i|--image IMAGE`: the image name to start the service with 75 | - `-I|--image-version IMAGE_VERSION`: the image version to start the service with 76 | - `-m|--memory MEMORY`: container memory limit in megabytes (default: unlimited) 77 | - `-N|--initial-network INITIAL_NETWORK`: the initial network to attach the service to 78 | - `-p|--password PASSWORD`: override the user-level service password 79 | - `-P|--post-create-network NETWORKS`: a comma-separated list of networks to attach the service container to after service creation 80 | - `-r|--root-password PASSWORD`: override the root-level service password 81 | - `-S|--post-start-network NETWORKS`: a comma-separated list of networks to attach the service container to after service start 82 | - `-s|--shm-size SHM_SIZE`: override shared memory size for mysql docker container 83 | 84 | Create a mysql service named lollipop: 85 | 86 | ```shell 87 | dokku mysql:create lollipop 88 | ``` 89 | 90 | You can also specify the image and image version to use for the service. It *must* be compatible with the mysql image. 91 | 92 | ```shell 93 | export MYSQL_IMAGE="mysql" 94 | export MYSQL_IMAGE_VERSION="${PLUGIN_IMAGE_VERSION}" 95 | dokku mysql:create lollipop 96 | ``` 97 | 98 | You can also specify custom environment variables to start the mysql service in semicolon-separated form. 99 | 100 | ```shell 101 | export MYSQL_CUSTOM_ENV="USER=alpha;HOST=beta" 102 | dokku mysql:create lollipop 103 | ``` 104 | 105 | ### print the service information 106 | 107 | ```shell 108 | # usage 109 | dokku mysql:info [--single-info-flag] 110 | ``` 111 | 112 | flags: 113 | 114 | - `--config-dir`: show the service configuration directory 115 | - `--data-dir`: show the service data directory 116 | - `--dsn`: show the service DSN 117 | - `--exposed-ports`: show service exposed ports 118 | - `--id`: show the service container id 119 | - `--internal-ip`: show the service internal ip 120 | - `--initial-network`: show the initial network being connected to 121 | - `--links`: show the service app links 122 | - `--post-create-network`: show the networks to attach to after service container creation 123 | - `--post-start-network`: show the networks to attach to after service container start 124 | - `--service-root`: show the service root directory 125 | - `--status`: show the service running status 126 | - `--version`: show the service image version 127 | 128 | Get connection information as follows: 129 | 130 | ```shell 131 | dokku mysql:info lollipop 132 | ``` 133 | 134 | You can also retrieve a specific piece of service info via flags: 135 | 136 | ```shell 137 | dokku mysql:info lollipop --config-dir 138 | dokku mysql:info lollipop --data-dir 139 | dokku mysql:info lollipop --dsn 140 | dokku mysql:info lollipop --exposed-ports 141 | dokku mysql:info lollipop --id 142 | dokku mysql:info lollipop --internal-ip 143 | dokku mysql:info lollipop --initial-network 144 | dokku mysql:info lollipop --links 145 | dokku mysql:info lollipop --post-create-network 146 | dokku mysql:info lollipop --post-start-network 147 | dokku mysql:info lollipop --service-root 148 | dokku mysql:info lollipop --status 149 | dokku mysql:info lollipop --version 150 | ``` 151 | 152 | ### list all mysql services 153 | 154 | ```shell 155 | # usage 156 | dokku mysql:list 157 | ``` 158 | 159 | List all services: 160 | 161 | ```shell 162 | dokku mysql:list 163 | ``` 164 | 165 | ### print the most recent log(s) for this service 166 | 167 | ```shell 168 | # usage 169 | dokku mysql:logs [-t|--tail] 170 | ``` 171 | 172 | flags: 173 | 174 | - `-t|--tail []`: do not stop when end of the logs are reached and wait for additional output 175 | 176 | You can tail logs for a particular service: 177 | 178 | ```shell 179 | dokku mysql:logs lollipop 180 | ``` 181 | 182 | By default, logs will not be tailed, but you can do this with the --tail flag: 183 | 184 | ```shell 185 | dokku mysql:logs lollipop --tail 186 | ``` 187 | 188 | The default tail setting is to show all logs, but an initial count can also be specified: 189 | 190 | ```shell 191 | dokku mysql:logs lollipop --tail 5 192 | ``` 193 | 194 | ### link the mysql service to the app 195 | 196 | ```shell 197 | # usage 198 | dokku mysql:link [--link-flags...] 199 | ``` 200 | 201 | flags: 202 | 203 | - `-a|--alias "BLUE_DATABASE"`: an alternative alias to use for linking to an app via environment variable 204 | - `-q|--querystring "pool=5"`: ampersand delimited querystring arguments to append to the service link 205 | - `-n|--no-restart "false"`: whether or not to restart the app on link (default: true) 206 | 207 | A mysql service can be linked to a container. This will use native docker links via the docker-options plugin. Here we link it to our `playground` app. 208 | 209 | > NOTE: this will restart your app 210 | 211 | ```shell 212 | dokku mysql:link lollipop playground 213 | ``` 214 | 215 | The following environment variables will be set automatically by docker (not on the app itself, so they won’t be listed when calling dokku config): 216 | 217 | ``` 218 | DOKKU_MYSQL_LOLLIPOP_NAME=/lollipop/DATABASE 219 | DOKKU_MYSQL_LOLLIPOP_PORT=tcp://172.17.0.1:3306 220 | DOKKU_MYSQL_LOLLIPOP_PORT_3306_TCP=tcp://172.17.0.1:3306 221 | DOKKU_MYSQL_LOLLIPOP_PORT_3306_TCP_PROTO=tcp 222 | DOKKU_MYSQL_LOLLIPOP_PORT_3306_TCP_PORT=3306 223 | DOKKU_MYSQL_LOLLIPOP_PORT_3306_TCP_ADDR=172.17.0.1 224 | ``` 225 | 226 | The following will be set on the linked application by default: 227 | 228 | ``` 229 | DATABASE_URL=mysql://mysql:SOME_PASSWORD@dokku-mysql-lollipop:3306/lollipop 230 | ``` 231 | 232 | The host exposed here only works internally in docker containers. If you want your container to be reachable from outside, you should use the `expose` subcommand. Another service can be linked to your app: 233 | 234 | ```shell 235 | dokku mysql:link other_service playground 236 | ``` 237 | 238 | It is possible to change the protocol for `DATABASE_URL` by setting the environment variable `MYSQL_DATABASE_SCHEME` on the app. Doing so will after linking will cause the plugin to think the service is not linked, and we advise you to unlink before proceeding. 239 | 240 | ```shell 241 | dokku config:set playground MYSQL_DATABASE_SCHEME=mysql2 242 | dokku mysql:link lollipop playground 243 | ``` 244 | 245 | This will cause `DATABASE_URL` to be set as: 246 | 247 | ``` 248 | mysql2://mysql:SOME_PASSWORD@dokku-mysql-lollipop:3306/lollipop 249 | ``` 250 | 251 | ### unlink the mysql service from the app 252 | 253 | ```shell 254 | # usage 255 | dokku mysql:unlink 256 | ``` 257 | 258 | flags: 259 | 260 | - `-n|--no-restart "false"`: whether or not to restart the app on unlink (default: true) 261 | 262 | You can unlink a mysql service: 263 | 264 | > NOTE: this will restart your app and unset related environment variables 265 | 266 | ```shell 267 | dokku mysql:unlink lollipop playground 268 | ``` 269 | 270 | ### set or clear a property for a service 271 | 272 | ```shell 273 | # usage 274 | dokku mysql:set 275 | ``` 276 | 277 | Set the network to attach after the service container is started: 278 | 279 | ```shell 280 | dokku mysql:set lollipop post-create-network custom-network 281 | ``` 282 | 283 | Set multiple networks: 284 | 285 | ```shell 286 | dokku mysql:set lollipop post-create-network custom-network,other-network 287 | ``` 288 | 289 | Unset the post-create-network value: 290 | 291 | ```shell 292 | dokku mysql:set lollipop post-create-network 293 | ``` 294 | 295 | ### Service Lifecycle 296 | 297 | The lifecycle of each service can be managed through the following commands: 298 | 299 | ### connect to the service via the mysql connection tool 300 | 301 | ```shell 302 | # usage 303 | dokku mysql:connect 304 | ``` 305 | 306 | Connect to the service via the mysql connection tool: 307 | 308 | > NOTE: disconnecting from ssh while running this command may leave zombie processes due to moby/moby#9098 309 | 310 | ```shell 311 | dokku mysql:connect lollipop 312 | ``` 313 | 314 | ### enter or run a command in a running mysql service container 315 | 316 | ```shell 317 | # usage 318 | dokku mysql:enter 319 | ``` 320 | 321 | A bash prompt can be opened against a running service. Filesystem changes will not be saved to disk. 322 | 323 | > NOTE: disconnecting from ssh while running this command may leave zombie processes due to moby/moby#9098 324 | 325 | ```shell 326 | dokku mysql:enter lollipop 327 | ``` 328 | 329 | You may also run a command directly against the service. Filesystem changes will not be saved to disk. 330 | 331 | ```shell 332 | dokku mysql:enter lollipop touch /tmp/test 333 | ``` 334 | 335 | ### expose a mysql service on custom host:port if provided (random port on the 0.0.0.0 interface if otherwise unspecified) 336 | 337 | ```shell 338 | # usage 339 | dokku mysql:expose 340 | ``` 341 | 342 | Expose the service on the service's normal ports, allowing access to it from the public interface (`0.0.0.0`): 343 | 344 | ```shell 345 | dokku mysql:expose lollipop 3306 346 | ``` 347 | 348 | Expose the service on the service's normal ports, with the first on a specified ip address (127.0.0.1): 349 | 350 | ```shell 351 | dokku mysql:expose lollipop 127.0.0.1:3306 352 | ``` 353 | 354 | ### unexpose a previously exposed mysql service 355 | 356 | ```shell 357 | # usage 358 | dokku mysql:unexpose 359 | ``` 360 | 361 | Unexpose the service, removing access to it from the public interface (`0.0.0.0`): 362 | 363 | ```shell 364 | dokku mysql:unexpose lollipop 365 | ``` 366 | 367 | ### promote service as DATABASE_URL in 368 | 369 | ```shell 370 | # usage 371 | dokku mysql:promote 372 | ``` 373 | 374 | If you have a mysql service linked to an app and try to link another mysql service another link environment variable will be generated automatically: 375 | 376 | ``` 377 | DOKKU_DATABASE_BLUE_URL=mysql://other_service:ANOTHER_PASSWORD@dokku-mysql-other-service:3306/other_service 378 | ``` 379 | 380 | You can promote the new service to be the primary one: 381 | 382 | > NOTE: this will restart your app 383 | 384 | ```shell 385 | dokku mysql:promote other_service playground 386 | ``` 387 | 388 | This will replace `DATABASE_URL` with the url from other_service and generate another environment variable to hold the previous value if necessary. You could end up with the following for example: 389 | 390 | ``` 391 | DATABASE_URL=mysql://other_service:ANOTHER_PASSWORD@dokku-mysql-other-service:3306/other_service 392 | DOKKU_DATABASE_BLUE_URL=mysql://other_service:ANOTHER_PASSWORD@dokku-mysql-other-service:3306/other_service 393 | DOKKU_DATABASE_SILVER_URL=mysql://lollipop:SOME_PASSWORD@dokku-mysql-lollipop:3306/lollipop 394 | ``` 395 | 396 | ### start a previously stopped mysql service 397 | 398 | ```shell 399 | # usage 400 | dokku mysql:start 401 | ``` 402 | 403 | Start the service: 404 | 405 | ```shell 406 | dokku mysql:start lollipop 407 | ``` 408 | 409 | ### stop a running mysql service 410 | 411 | ```shell 412 | # usage 413 | dokku mysql:stop 414 | ``` 415 | 416 | Stop the service and removes the running container: 417 | 418 | ```shell 419 | dokku mysql:stop lollipop 420 | ``` 421 | 422 | ### pause a running mysql service 423 | 424 | ```shell 425 | # usage 426 | dokku mysql:pause 427 | ``` 428 | 429 | Pause the running container for the service: 430 | 431 | ```shell 432 | dokku mysql:pause lollipop 433 | ``` 434 | 435 | ### graceful shutdown and restart of the mysql service container 436 | 437 | ```shell 438 | # usage 439 | dokku mysql:restart 440 | ``` 441 | 442 | Restart the service: 443 | 444 | ```shell 445 | dokku mysql:restart lollipop 446 | ``` 447 | 448 | ### upgrade service to the specified versions 449 | 450 | ```shell 451 | # usage 452 | dokku mysql:upgrade [--upgrade-flags...] 453 | ``` 454 | 455 | flags: 456 | 457 | - `-c|--config-options "--args --go=here"`: extra arguments to pass to the container create command (default: `None`) 458 | - `-C|--custom-env "USER=alpha;HOST=beta"`: semi-colon delimited environment variables to start the service with 459 | - `-i|--image IMAGE`: the image name to start the service with 460 | - `-I|--image-version IMAGE_VERSION`: the image version to start the service with 461 | - `-N|--initial-network INITIAL_NETWORK`: the initial network to attach the service to 462 | - `-P|--post-create-network NETWORKS`: a comma-separated list of networks to attach the service container to after service creation 463 | - `-R|--restart-apps "true"`: whether or not to force an app restart (default: false) 464 | - `-S|--post-start-network NETWORKS`: a comma-separated list of networks to attach the service container to after service start 465 | - `-s|--shm-size SHM_SIZE`: override shared memory size for mysql docker container 466 | 467 | You can upgrade an existing service to a new image or image-version: 468 | 469 | ```shell 470 | dokku mysql:upgrade lollipop 471 | ``` 472 | 473 | ### Service Automation 474 | 475 | Service scripting can be executed using the following commands: 476 | 477 | ### list all mysql service links for a given app 478 | 479 | ```shell 480 | # usage 481 | dokku mysql:app-links 482 | ``` 483 | 484 | List all mysql services that are linked to the `playground` app. 485 | 486 | ```shell 487 | dokku mysql:app-links playground 488 | ``` 489 | 490 | ### create container then copy data from into 491 | 492 | ```shell 493 | # usage 494 | dokku mysql:clone [--clone-flags...] 495 | ``` 496 | 497 | flags: 498 | 499 | - `-c|--config-options "--args --go=here"`: extra arguments to pass to the container create command (default: `None`) 500 | - `-C|--custom-env "USER=alpha;HOST=beta"`: semi-colon delimited environment variables to start the service with 501 | - `-i|--image IMAGE`: the image name to start the service with 502 | - `-I|--image-version IMAGE_VERSION`: the image version to start the service with 503 | - `-m|--memory MEMORY`: container memory limit in megabytes (default: unlimited) 504 | - `-N|--initial-network INITIAL_NETWORK`: the initial network to attach the service to 505 | - `-p|--password PASSWORD`: override the user-level service password 506 | - `-P|--post-create-network NETWORKS`: a comma-separated list of networks to attach the service container to after service creation 507 | - `-r|--root-password PASSWORD`: override the root-level service password 508 | - `-S|--post-start-network NETWORKS`: a comma-separated list of networks to attach the service container to after service start 509 | - `-s|--shm-size SHM_SIZE`: override shared memory size for mysql docker container 510 | 511 | You can clone an existing service to a new one: 512 | 513 | ```shell 514 | dokku mysql:clone lollipop lollipop-2 515 | ``` 516 | 517 | ### check if the mysql service exists 518 | 519 | ```shell 520 | # usage 521 | dokku mysql:exists 522 | ``` 523 | 524 | Here we check if the lollipop mysql service exists. 525 | 526 | ```shell 527 | dokku mysql:exists lollipop 528 | ``` 529 | 530 | ### check if the mysql service is linked to an app 531 | 532 | ```shell 533 | # usage 534 | dokku mysql:linked 535 | ``` 536 | 537 | Here we check if the lollipop mysql service is linked to the `playground` app. 538 | 539 | ```shell 540 | dokku mysql:linked lollipop playground 541 | ``` 542 | 543 | ### list all apps linked to the mysql service 544 | 545 | ```shell 546 | # usage 547 | dokku mysql:links 548 | ``` 549 | 550 | List all apps linked to the `lollipop` mysql service. 551 | 552 | ```shell 553 | dokku mysql:links lollipop 554 | ``` 555 | 556 | ### Data Management 557 | 558 | The underlying service data can be imported and exported with the following commands: 559 | 560 | ### import a dump into the mysql service database 561 | 562 | ```shell 563 | # usage 564 | dokku mysql:import 565 | ``` 566 | 567 | Import a datastore dump: 568 | 569 | ```shell 570 | dokku mysql:import lollipop < data.dump 571 | ``` 572 | 573 | ### export a dump of the mysql service database 574 | 575 | ```shell 576 | # usage 577 | dokku mysql:export 578 | ``` 579 | 580 | By default, datastore output is exported to stdout: 581 | 582 | ```shell 583 | dokku mysql:export lollipop 584 | ``` 585 | 586 | You can redirect this output to a file: 587 | 588 | ```shell 589 | dokku mysql:export lollipop > data.dump 590 | ``` 591 | 592 | ### Backups 593 | 594 | Datastore backups are supported via AWS S3 and S3 compatible services like [minio](https://github.com/minio/minio). 595 | 596 | You may skip the `backup-auth` step if your dokku install is running within EC2 and has access to the bucket via an IAM profile. In that case, use the `--use-iam` option with the `backup` command. 597 | 598 | If both passphrase and public key forms of encryption are set, the public key encryption will take precedence. 599 | 600 | The underlying core backup script is present [here](https://github.com/dokku/docker-s3backup/blob/main/backup.sh). 601 | 602 | Backups can be performed using the backup commands: 603 | 604 | ### set up authentication for backups on the mysql service 605 | 606 | ```shell 607 | # usage 608 | dokku mysql:backup-auth 609 | ``` 610 | 611 | Setup s3 backup authentication: 612 | 613 | ```shell 614 | dokku mysql:backup-auth lollipop AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY 615 | ``` 616 | 617 | Setup s3 backup authentication with different region: 618 | 619 | ```shell 620 | dokku mysql:backup-auth lollipop AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_REGION 621 | ``` 622 | 623 | Setup s3 backup authentication with different signature version and endpoint: 624 | 625 | ```shell 626 | dokku mysql:backup-auth lollipop AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_REGION AWS_SIGNATURE_VERSION ENDPOINT_URL 627 | ``` 628 | 629 | More specific example for minio auth: 630 | 631 | ```shell 632 | dokku mysql:backup-auth lollipop MINIO_ACCESS_KEY_ID MINIO_SECRET_ACCESS_KEY us-east-1 s3v4 https://YOURMINIOSERVICE 633 | ``` 634 | 635 | ### remove backup authentication for the mysql service 636 | 637 | ```shell 638 | # usage 639 | dokku mysql:backup-deauth 640 | ``` 641 | 642 | Remove s3 authentication: 643 | 644 | ```shell 645 | dokku mysql:backup-deauth lollipop 646 | ``` 647 | 648 | ### create a backup of the mysql service to an existing s3 bucket 649 | 650 | ```shell 651 | # usage 652 | dokku mysql:backup [--use-iam] 653 | ``` 654 | 655 | flags: 656 | 657 | - `-u|--use-iam`: use the IAM profile associated with the current server 658 | 659 | Backup the `lollipop` service to the `my-s3-bucket` bucket on `AWS`:` 660 | 661 | ```shell 662 | dokku mysql:backup lollipop my-s3-bucket --use-iam 663 | ``` 664 | 665 | Restore a backup file (assuming it was extracted via `tar -xf backup.tgz`): 666 | 667 | ```shell 668 | dokku mysql:import lollipop < backup-folder/export 669 | ``` 670 | 671 | ### set encryption for all future backups of mysql service 672 | 673 | ```shell 674 | # usage 675 | dokku mysql:backup-set-encryption 676 | ``` 677 | 678 | Set the GPG-compatible passphrase for encrypting backups for backups: 679 | 680 | ```shell 681 | dokku mysql:backup-set-encryption lollipop 682 | ``` 683 | 684 | Public key encryption will take precendence over the passphrase encryption if both types are set. 685 | 686 | ### set GPG Public Key encryption for all future backups of mysql service 687 | 688 | ```shell 689 | # usage 690 | dokku mysql:backup-set-public-key-encryption 691 | ``` 692 | 693 | Set the `GPG` Public Key for encrypting backups: 694 | 695 | ```shell 696 | dokku mysql:backup-set-public-key-encryption lollipop 697 | ``` 698 | 699 | This method currently requires the to be present on the keyserver `keyserver.ubuntu.com`: 700 | 701 | ### unset encryption for future backups of the mysql service 702 | 703 | ```shell 704 | # usage 705 | dokku mysql:backup-unset-encryption 706 | ``` 707 | 708 | Unset the `GPG` encryption passphrase for backups: 709 | 710 | ```shell 711 | dokku mysql:backup-unset-encryption lollipop 712 | ``` 713 | 714 | ### unset GPG Public Key encryption for future backups of the mysql service 715 | 716 | ```shell 717 | # usage 718 | dokku mysql:backup-unset-public-key-encryption 719 | ``` 720 | 721 | Unset the `GPG` Public Key encryption for backups: 722 | 723 | ```shell 724 | dokku mysql:backup-unset-public-key-encryption lollipop 725 | ``` 726 | 727 | ### schedule a backup of the mysql service 728 | 729 | ```shell 730 | # usage 731 | dokku mysql:backup-schedule [--use-iam] 732 | ``` 733 | 734 | flags: 735 | 736 | - `-u|--use-iam`: use the IAM profile associated with the current server 737 | 738 | Schedule a backup: 739 | 740 | > 'schedule' is a crontab expression, eg. "0 3 * * *" for each day at 3am 741 | 742 | ```shell 743 | dokku mysql:backup-schedule lollipop "0 3 * * *" my-s3-bucket 744 | ``` 745 | 746 | Schedule a backup and authenticate via iam: 747 | 748 | ```shell 749 | dokku mysql:backup-schedule lollipop "0 3 * * *" my-s3-bucket --use-iam 750 | ``` 751 | 752 | ### cat the contents of the configured backup cronfile for the service 753 | 754 | ```shell 755 | # usage 756 | dokku mysql:backup-schedule-cat 757 | ``` 758 | 759 | Cat the contents of the configured backup cronfile for the service: 760 | 761 | ```shell 762 | dokku mysql:backup-schedule-cat lollipop 763 | ``` 764 | 765 | ### unschedule the backup of the mysql service 766 | 767 | ```shell 768 | # usage 769 | dokku mysql:backup-unschedule 770 | ``` 771 | 772 | Remove the scheduled backup from cron: 773 | 774 | ```shell 775 | dokku mysql:backup-unschedule lollipop 776 | ``` 777 | 778 | ### Disabling `docker image pull` calls 779 | 780 | If you wish to disable the `docker image pull` calls that the plugin triggers, you may set the `MYSQL_DISABLE_PULL` environment variable to `true`. Once disabled, you will need to pull the service image you wish to deploy as shown in the `stderr` output. 781 | 782 | Please ensure the proper images are in place when `docker image pull` is disabled. 783 | --------------------------------------------------------------------------------