From 40b20e0c08b9b51e71f306093d459ca5628b01c5 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 28 Nov 2022 02:24:27 -0500 Subject: [PATCH 01/61] refactor: move herokuish app cache from the filesystem into a docker volume This simplifies cache referencing and also paves the way for removing more from the app git directory. Refs #1558 --- docs/appendices/0.31.0-migration-guide.md | 5 +++ docs/getting-started/upgrading/index.md | 1 + plugins/builder-herokuish/builder-build | 20 +++++---- plugins/builder-herokuish/install | 15 +++++++ plugins/common/docker.go | 28 +++++++++---- plugins/common/subprocess.go | 5 +++ plugins/repo/.gitignore | 3 +- plugins/repo/Makefile | 2 +- plugins/repo/repo.go | 51 +++++++++++++++-------- plugins/repo/src/triggers/triggers.go | 2 +- tests/unit/build-env.bats | 8 +++- tests/unit/repo.bats | 29 ++++++++----- tests/unit/test_helper.bash | 7 ++++ 13 files changed, 126 insertions(+), 50 deletions(-) create mode 100644 docs/appendices/0.31.0-migration-guide.md diff --git a/docs/appendices/0.31.0-migration-guide.md b/docs/appendices/0.31.0-migration-guide.md new file mode 100644 index 000000000..547cbc6ce --- /dev/null +++ b/docs/appendices/0.31.0-migration-guide.md @@ -0,0 +1,5 @@ +# 0.30.0 Migration Guide + +## Changes + +- Herokuish build cache is now mounted from a docker volume - eg. `cache-node-js-app` - instead of the local filesystem. All existing app cache will be cleared upon upgrading past 0.29.0. diff --git a/docs/getting-started/upgrading/index.md b/docs/getting-started/upgrading/index.md index 0279ee147..9332e8443 100644 --- a/docs/getting-started/upgrading/index.md +++ b/docs/getting-started/upgrading/index.md @@ -18,6 +18,7 @@ Docker releases updates periodically to their engine. We recommend reading their Before upgrading, check the migration guides to get comfortable with new features and prepare your deployment to be upgraded. +- [Upgrading to 0.31](/docs/appendices/0.31.0-migration-guide.md) - [Upgrading to 0.30](/docs/appendices/0.30.0-migration-guide.md) - [Upgrading to 0.29](/docs/appendices/0.29.0-migration-guide.md) - [Upgrading to 0.28](/docs/appendices/0.28.0-migration-guide.md) diff --git a/plugins/builder-herokuish/builder-build b/plugins/builder-herokuish/builder-build index 401ab58d6..7505a992d 100755 --- a/plugins/builder-herokuish/builder-build +++ b/plugins/builder-herokuish/builder-build @@ -3,6 +3,16 @@ set -eo pipefail [[ $DOKKU_TRACE ]] && set -x source "$PLUGIN_AVAILABLE_PATH/config/functions" +fn-builder-herokuish-ensure-cache() { + declare APP="$1" + + existing_cache="$("$DOCKER_BIN" volume ls --quiet --filter label=com.dokku.app-name=test --filter label=com.dokku.builder-type=herokuish)" + if [[ "$existing_cache" != "cache-$APP" ]]; then + "$DOCKER_BIN" volume rm "cache-$APP" >/dev/null 2>&1 || true + "$DOCKER_BIN" volume create "--label=org.label-schema.schema-version=1.0" "--label=org.label-schema.vendor=dokku" "--label=com.dokku.app-name=$APP" "--label=com.dokku.builder-type=herokuish" "cache-$APP" >/dev/null + fi +} + trigger-builder-herokuish-builder-build() { declare desc="builder-herokuish builder-build plugin trigger" declare trigger="builder-build" @@ -21,8 +31,6 @@ trigger-builder-herokuish-builder-build() { fi local IMAGE=$(get_app_image_name "$APP") - local DOKKU_APP_CACHE_DIR="$DOKKU_ROOT/$APP/cache" - local DOKKU_APP_HOST_CACHE_DIR="$DOKKU_HOST_ROOT/$APP/cache" local DOCKER_COMMIT_LABEL_ARGS=("--change" "LABEL dokku=" "--change" "LABEL org.label-schema.schema-version=1.0" "--change" "LABEL org.label-schema.vendor=dokku" "--change" "LABEL com.dokku.image-stage=build" "--change" "LABEL com.dokku.builder-type=herokuish" "--change" "LABEL com.dokku.app-name=$APP") local DOCKER_RUN_LABEL_ARGS=("--label=dokku" "--label=org.label-schema.schema-version=1.0" "--label=org.label-schema.vendor=dokku" "--label=com.dokku.image-stage=build" "--label=com.dokku.builder-type=herokuish" "--label=com.dokku.app-name=$APP") local CID TAR_CID @@ -49,7 +57,6 @@ trigger-builder-herokuish-builder-build() { "$DOCKER_BIN" container commit "${DOCKER_COMMIT_LABEL_ARGS[@]}" "$TAR_CID" "$IMAGE" >/dev/null DOKKU_SKIP_IMAGE_CLEANUP_REGISTRATION=1 plugn trigger scheduler-register-retired "$APP" "$TAR_CID" - [[ -d $DOKKU_APP_CACHE_DIR ]] || mkdir -p "$DOKKU_APP_CACHE_DIR" plugn trigger pre-build-buildpack "$APP" "$SOURCECODE_WORK_DIR" local DOCKER_ARGS=$(: | plugn trigger docker-args-build "$APP" "$BUILDER_TYPE") @@ -60,8 +67,8 @@ trigger-builder-herokuish-builder-build() { eval "ARG_ARRAY=($DOCKER_ARGS)" local DOKKU_CONTAINER_EXIT_CODE=0 - if ! CID=$("$DOCKER_BIN" container create "${DOCKER_RUN_LABEL_ARGS[@]}" $DOKKU_GLOBAL_RUN_ARGS -v "$DOKKU_APP_HOST_CACHE_DIR:/cache" --env=CACHE_PATH=/cache "${ARG_ARRAY[@]}" "$IMAGE" /build); then - DOKKU_SKIP_IMAGE_CLEANUP_REGISTRATION=1 plugn trigger scheduler-register-retired "$APP" "$TAR_CID" + fn-builder-herokuish-ensure-cache "$APP" + if ! CID=$("$DOCKER_BIN" container create "${DOCKER_RUN_LABEL_ARGS[@]}" $DOKKU_GLOBAL_RUN_ARGS -v "cache-$APP:/cache" --env=CACHE_PATH=/cache "${ARG_ARRAY[@]}" "$IMAGE" /build); then plugn trigger scheduler-register-retired "$APP" "$CID" dokku_log_warn "Failure during app build" return 1 @@ -70,7 +77,6 @@ trigger-builder-herokuish-builder-build() { plugn trigger post-container-create "app" "$CID" "$APP" "build" "$DOCKER_BIN" container start "$CID" >/dev/null || DOKKU_CONTAINER_EXIT_CODE=$? if ! "$DOCKER_BIN" container attach "$CID"; then - DOKKU_SKIP_IMAGE_CLEANUP_REGISTRATION=1 plugn trigger scheduler-register-retired "$APP" "$TAR_CID" plugn trigger scheduler-register-retired "$APP" "$CID" dokku_log_warn "Failure during app build" return 1 @@ -78,14 +84,12 @@ trigger-builder-herokuish-builder-build() { DOKKU_CONTAINER_EXIT_CODE="$("$DOCKER_BIN" container wait "$CID" 2>/dev/null || echo "$DOKKU_CONTAINER_EXIT_CODE")" if [[ "$DOKKU_CONTAINER_EXIT_CODE" -ne 0 ]]; then - DOKKU_SKIP_IMAGE_CLEANUP_REGISTRATION=1 plugn trigger scheduler-register-retired "$APP" "$TAR_CID" plugn trigger scheduler-register-retired "$APP" "$CID" dokku_log_warn "Failure during app build" return 1 fi "$DOCKER_BIN" container commit "${DOCKER_COMMIT_LABEL_ARGS[@]}" "$CID" "$IMAGE" >/dev/null - DOKKU_SKIP_IMAGE_CLEANUP_REGISTRATION=1 plugn trigger scheduler-register-retired "$APP" "$TAR_CID" plugn trigger scheduler-register-retired "$APP" "$CID" plugn trigger post-build-buildpack "$APP" "$SOURCECODE_WORK_DIR" } diff --git a/plugins/builder-herokuish/install b/plugins/builder-herokuish/install index 00efe10cf..357b692f7 100755 --- a/plugins/builder-herokuish/install +++ b/plugins/builder-herokuish/install @@ -1,13 +1,28 @@ #!/usr/bin/env bash +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions" set -eo pipefail [[ $DOKKU_TRACE ]] && set -x +fn-builder-herokuish-remove-old-cache() { + for app in $(dokku_apps "false" 2>/dev/null); do + local DOKKU_APP_CACHE_DIR="$DOKKU_ROOT/$APP/cache" + local DOKKU_APP_HOST_CACHE_DIR="$DOKKU_HOST_ROOT/$APP/cache" + if [[ ! -d "$DOKKU_APP_CACHE_DIR" ]]; then + continue + fi + + "$DOCKER_BIN" container run --rm --label=dokku --label=org.label-schema.schema-version=1.0 --label=org.label-schema.vendor=dokku "--label=com.dokku.app-name=$APP" -v "$DOKKU_APP_HOST_CACHE_DIR:/cache" "$DOKKU_IMAGE" "find /cache -depth -mindepth 1 -maxdepth 1 -exec rm -Rf {} ;" + rm -rf "$DOKKU_APP_CACHE_DIR" + done +} + trigger-builder-herokuish-install() { declare desc="installs the builder-herokuish plugin" declare trigger="install" fn-plugin-property-setup "builder-herokuish" + fn-builder-herokuish-remove-old-cache } trigger-builder-herokuish-install "$@" diff --git a/plugins/common/docker.go b/plugins/common/docker.go index 95d1b2f0d..70bae5703 100644 --- a/plugins/common/docker.go +++ b/plugins/common/docker.go @@ -203,7 +203,7 @@ func DockerCleanup(appName string, forceCleanup bool) error { containerIDs := append(exitedContainerIDs, deadContainerIDs...) if len(containerIDs) > 0 { - removeContainers(containerIDs) + DockerRemoveContainers(containerIDs) } // delete dangling images @@ -351,21 +351,18 @@ func VerifyImage(image string) bool { return imageCmd.Execute() } -func listContainers(status string, appName string) ([]string, error) { +// DockerFilterContainers returns a slice of container IDs based on the passed in filters +func DockerFilterContainers(filters []string) ([]string, error) { command := []string{ DockerBin(), "container", "list", "--quiet", "--all", - "--filter", - fmt.Sprintf("status=%v", status), - "--filter", - fmt.Sprintf("label=%v", os.Getenv("DOKKU_CONTAINER_LABEL")), } - if appName != "" { - command = append(command, []string{"--filter", fmt.Sprintf("label=com.dokku.app-name=%v", appName)}...) + for _, filter := range filters { + command = append(command, "--filter", filter) } var stderr bytes.Buffer @@ -382,6 +379,18 @@ func listContainers(status string, appName string) ([]string, error) { return output, nil } +func listContainers(status string, appName string) ([]string, error) { + filters := []string{ + fmt.Sprintf("status=%v", status), + fmt.Sprintf("label=%v", os.Getenv("DOKKU_CONTAINER_LABEL")), + } + + if appName != "" { + filters = append(filters, fmt.Sprintf("label=com.dokku.app-name=%v", appName)) + } + return DockerFilterContainers(filters) +} + func pruneUnusedImages(appName string) { command := []string{ DockerBin(), @@ -400,7 +409,8 @@ func pruneUnusedImages(appName string) { pruneCmd.Execute() } -func removeContainers(containerIDs []string) { +// DockerRemoveContainers will call `docker container rm` on the specified containers +func DockerRemoveContainers(containerIDs []string) { command := []string{ DockerBin(), "container", diff --git a/plugins/common/subprocess.go b/plugins/common/subprocess.go index 642b06c80..19b02e81e 100644 --- a/plugins/common/subprocess.go +++ b/plugins/common/subprocess.go @@ -18,6 +18,7 @@ type ShellCmd struct { CommandString string Args []string ShowOutput bool + ExitError *exec.ExitError } // NewShellCmd returns a new ShellCmd struct @@ -57,6 +58,10 @@ func (sc *ShellCmd) Execute() bool { sc.setup() if err := sc.Command.Run(); err != nil { + exitError, ok := err.(*exec.ExitError) + if ok { + sc.ExitError = exitError + } return false } return true diff --git a/plugins/repo/.gitignore b/plugins/repo/.gitignore index 4e8c51e8a..e1f4bd87f 100644 --- a/plugins/repo/.gitignore +++ b/plugins/repo/.gitignore @@ -2,5 +2,4 @@ /subcommands/* /triggers/* /triggers -/pre-delete -/post-stack-set +/post-* diff --git a/plugins/repo/Makefile b/plugins/repo/Makefile index fd3d74547..0af946983 100644 --- a/plugins/repo/Makefile +++ b/plugins/repo/Makefile @@ -1,5 +1,5 @@ SUBCOMMANDS = subcommands/gc subcommands/purge-cache -TRIGGERS = triggers/post-stack-set triggers/pre-delete +TRIGGERS = triggers/post-stack-set triggers/post-delete BUILD = commands subcommands triggers PLUGIN_NAME = repo diff --git a/plugins/repo/repo.go b/plugins/repo/repo.go index 04e7cdea1..1f459fe1e 100644 --- a/plugins/repo/repo.go +++ b/plugins/repo/repo.go @@ -6,28 +6,45 @@ import ( "strings" "github.com/dokku/dokku/plugins/common" - "github.com/dokku/dokku/plugins/config" ) +// PurgeCacheFailed wraps error to allow returning the correct exit code +type PurgeCacheFailed struct { + exitCode int +} + +// ExitCode returns an exit code to use in case this error bubbles +// up into an os.Exit() call +func (err *PurgeCacheFailed) ExitCode() int { + return err.exitCode +} + +// Error returns a standard non-existent app error +func (err *PurgeCacheFailed) Error() string { + return fmt.Sprintf("failed to purge cache, exit code %d", err.exitCode) +} + // PurgeCache deletes the contents of the build cache stored in the repository func PurgeCache(appName string) error { - cacheDir := strings.Join([]string{common.AppRoot(appName), "cache"}, "/") - cacheHostDir := strings.Join([]string{common.AppHostRoot(appName), "cache"}, "/") - dokkuGlobalRunArgs := common.MustGetEnv("DOKKU_GLOBAL_RUN_ARGS") - image := config.GetWithDefault(appName, "DOKKU_IMAGE", os.Getenv("DOKKU_IMAGE")) - if info, _ := os.Stat(cacheDir); info != nil && info.IsDir() { - dockerLabelArgs := fmt.Sprintf("--label=com.dokku.app-name=%s", appName) - purgeCacheCmd := common.NewShellCmd(strings.Join([]string{ - common.DockerBin(), - "container", - "run", "--rm", dockerLabelArgs, dokkuGlobalRunArgs, - "-v", strings.Join([]string{cacheHostDir, ":/cache"}, ""), image, - `find /cache -depth -mindepth 1 -maxdepth 1 -exec rm -Rf {} ;`}, " ")) - purgeCacheCmd.Execute() - err := os.MkdirAll(cacheDir, 0644) - if err != nil { - return err + containerIDs, _ := common.DockerFilterContainers([]string{ + fmt.Sprintf("label=com.dokku.app-name=%v", appName), + "label=com.dokku.image-stage=build", + }) + if len(containerIDs) > 0 { + common.DockerRemoveContainers(containerIDs) + } + purgeCacheCmd := common.NewShellCmd(strings.Join([]string{ + common.DockerBin(), + "volume", + "rm", "-f", fmt.Sprintf("cache-%s", appName)}, " ")) + purgeCacheCmd.ShowOutput = false + purgeCacheCmd.Command.Stderr = os.Stderr + if !purgeCacheCmd.Execute() { + exitCode := 1 + if purgeCacheCmd.ExitError != nil { + exitCode = purgeCacheCmd.ExitError.ExitCode() } + return &PurgeCacheFailed{exitCode} } return nil diff --git a/plugins/repo/src/triggers/triggers.go b/plugins/repo/src/triggers/triggers.go index 0dd1c21d0..63c4b468b 100644 --- a/plugins/repo/src/triggers/triggers.go +++ b/plugins/repo/src/triggers/triggers.go @@ -21,7 +21,7 @@ func main() { case "post-stack-set": appName := flag.Arg(0) err = repo.PurgeCache(appName) - case "pre-delete": + case "post-delete": appName := flag.Arg(0) err = repo.PurgeCache(appName) default: diff --git a/tests/unit/build-env.bats b/tests/unit/build-env.bats index 2a0df8f61..f53fe5372 100644 --- a/tests/unit/build-env.bats +++ b/tests/unit/build-env.bats @@ -59,12 +59,16 @@ teardown() { } @test "(build-env) buildpack deploy with Dockerfile" { - run /bin/bash -c "dokku config:set --no-restart $TEST_APP BUILDPACK_URL='https://github.com/heroku/heroku-buildpack-nodejs'" + run /bin/bash -c "dokku config:set --no-restart $TEST_APP BUILDPACK_URL='https://github.com/dokku/heroku-buildpack-null'" + echo "output: $output" + echo "status: $status" + assert_success + + run deploy_app python dokku@dokku.me:$TEST_APP move_dockerfile_into_place echo "output: $output" echo "status: $status" assert_success - deploy_app dockerfile run /bin/bash -c "dokku --quiet config:get $TEST_APP DOKKU_APP_TYPE" echo "output: $output" echo "status: $status" diff --git a/tests/unit/repo.bats b/tests/unit/repo.bats index abdc68213..6a74c315d 100644 --- a/tests/unit/repo.bats +++ b/tests/unit/repo.bats @@ -4,9 +4,11 @@ load test_helper setup() { global_setup + create_app } teardown() { + destroy_app global_teardown } @@ -24,7 +26,7 @@ teardown() { assert_output "$help_output" } -@test "(repo) repo:gc, repo:purge-cache" { +@test "(repo) repo:gc" { run deploy_app echo "output: $output" echo "status: $status" @@ -34,29 +36,36 @@ teardown() { echo "output: $output" echo "status: $status" assert_success +} - run /bin/bash -c "touch $DOKKU_ROOT/$TEST_APP/cache/derp" +@test "(repo) repo:purge-cache" { + run deploy_app echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "find $DOKKU_ROOT/$TEST_APP/cache -type f | wc -l" + run /bin/bash -c "docker volume ls -q --filter label=com.dokku.app-name=$TEST_APP | wc -l" echo "count: '$output'" echo "status: $status" - assert_not_output 0 - - run /bin/bash -c "dokku repo:purge-cache $TEST_APP" - echo "output: $output" - echo "status: $status" - assert_success + assert_output 1 run /bin/bash -c "find $DOKKU_ROOT/$TEST_APP/cache -type f | wc -l" echo "count: '$output'" echo "status: $status" assert_output 0 - run destroy_app + run /bin/bash -c "dokku repo:purge-cache $TEST_APP" echo "output: $output" echo "status: $status" assert_success + + run /bin/bash -c "docker volume ls -q --filter label=com.dokku.app-name=$TEST_APP | wc -l" + echo "count: '$output'" + echo "status: $status" + assert_output 0 + + run /bin/bash -c "find $DOKKU_ROOT/$TEST_APP/cache -type f | wc -l" + echo "count: '$output'" + echo "status: $status" + assert_output 0 } diff --git a/tests/unit/test_helper.bash b/tests/unit/test_helper.bash index 7ca8aad7f..7d39ec012 100644 --- a/tests/unit/test_helper.bash +++ b/tests/unit/test_helper.bash @@ -535,6 +535,13 @@ add_postdeploy_command() { echo "${contents}" >"$APP_REPO_DIR/app.json" } +move_dockerfile_into_place() { + local APP="$1" + local APP_REPO_DIR="$2" + [[ -z "$APP" ]] && local APP="$TEST_APP" + mv "$APP_REPO_DIR/alt.Dockerfile" "$APP_REPO_DIR/Dockerfile" +} + add_requirements_txt() { local APP="$1" local APP_REPO_DIR="$2" From 60b343470b6a3d5d82c32ab79108a2851fb41bbf Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 28 May 2023 21:21:26 -0400 Subject: [PATCH 02/61] feat: add support for specifying multiple networks on a given app Closes #5314 --- docs/networking/network.md | 7 ++ plugins/common/common.go | 9 +++ plugins/network/src/commands/commands.go | 4 +- .../network/src/subcommands/subcommands.go | 4 +- plugins/network/subcommands.go | 19 +++--- plugins/network/triggers.go | 67 ++++++++++--------- tests/unit/network.bats | 36 +++++++++- 7 files changed, 104 insertions(+), 42 deletions(-) diff --git a/docs/networking/network.md b/docs/networking/network.md index 899a97ded..30f898de4 100644 --- a/docs/networking/network.md +++ b/docs/networking/network.md @@ -173,6 +173,13 @@ dokku network:set node-js-app attach-post-deploy other-test-network dokku network:set node-js-app initial-network global-network ``` +Multiple networks can also be specified for the `attach-post-create` and `attach-post-deploy` phases. + +```shell +# one or more networks can be specified +dokku network:set node-js-app attach-post-create test-network test-network-2 +``` + Setting the `attach` network property to an empty value will de-associate the container with the network. ```shell diff --git a/plugins/common/common.go b/plugins/common/common.go index 53a7e27fd..99b8f4777 100644 --- a/plugins/common/common.go +++ b/plugins/common/common.go @@ -596,6 +596,15 @@ func (err *AppDoesNotExist) Error() string { return fmt.Sprintf("App %s does not exist", err.appName) } +// VarArgs skips a number of incoming arguments, returning what is left over +func VarArgs(arguments []string, skip int) []string { + if len(arguments) <= skip { + return []string{} + } + + return arguments[skip:] +} + // VerifyAppName checks if an app conforming to either the old or new // naming conventions exists func VerifyAppName(appName string) error { diff --git a/plugins/network/src/commands/commands.go b/plugins/network/src/commands/commands.go index 7160ceab9..e916485c5 100644 --- a/plugins/network/src/commands/commands.go +++ b/plugins/network/src/commands/commands.go @@ -23,10 +23,10 @@ Additional commands:` network:exists , Checks if a docker network exists network:info , Outputs information about a docker network network:list, Lists all docker networks - network:report [] [], Displays a network report for one or more apps network:rebuild , Rebuilds network settings for an app network:rebuildall, Rebuild network settings for all apps - network:set (), Set or clear a network property for an app + network:report [] [], Displays a network report for one or more apps + network:set (...), Set or clear a network property for an app ` ) diff --git a/plugins/network/src/subcommands/subcommands.go b/plugins/network/src/subcommands/subcommands.go index d2b9cfb2c..e81c49be1 100644 --- a/plugins/network/src/subcommands/subcommands.go +++ b/plugins/network/src/subcommands/subcommands.go @@ -67,12 +67,14 @@ func main() { appName := args.Arg(0) property := args.Arg(1) value := args.Arg(2) + values := common.VarArgs(args.Args(), 2) if *global { appName = "--global" property = args.Arg(0) value = args.Arg(1) + values = common.VarArgs(args.Args(), 1) } - err = network.CommandSet(appName, property, value) + err = network.CommandSet(appName, property, value, values) default: err = fmt.Errorf("Invalid plugin subcommand call: %s", subcommand) } diff --git a/plugins/network/subcommands.go b/plugins/network/subcommands.go index bf04f5784..987caf136 100644 --- a/plugins/network/subcommands.go +++ b/plugins/network/subcommands.go @@ -141,7 +141,7 @@ func CommandReport(appName string, format string, infoFlag string) error { } // CommandSet set or clear a network property for an app -func CommandSet(appName string, property string, value string) error { +func CommandSet(appName string, property string, value string, values []string) error { if appName != "--global" { if err := common.VerifyAppName(appName); err != nil { return err @@ -152,7 +152,7 @@ func CommandSet(appName string, property string, value string) error { value = "false" } - attachProperites := map[string]bool{ + attachProperties := map[string]bool{ "attach-post-create": true, "attach-post-deploy": true, } @@ -161,14 +161,17 @@ func CommandSet(appName string, property string, value string) error { "host": true, "bridge": true, } - if attachProperites[property] { - if invalidNetworks[value] { - return errors.New("Invalid network name specified for attach") - } + if attachProperties[property] { + for _, networkName := range values { + if invalidNetworks[networkName] { + return fmt.Errorf("Invalid network name specified for attach: %s", networkName) + } - if isConflictingPropertyValue(appName, property, value) { - return errors.New("Network name already associated with this app") + if isConflictingPropertyValue(appName, property, networkName) { + return fmt.Errorf("Network name already associated with this app: %s", networkName) + } } + value = strings.Join(values, ",") } common.CommandPropertySet("network", appName, property, value, DefaultProperties, GlobalProperties) diff --git a/plugins/network/triggers.go b/plugins/network/triggers.go index fbdc8d510..02673cdca 100644 --- a/plugins/network/triggers.go +++ b/plugins/network/triggers.go @@ -225,22 +225,26 @@ func TriggerPostContainerCreate(containerType string, containerID string, appNam } - networkName := reportComputedAttachPostCreate(appName) - if networkName == "" { + networks := reportComputedAttachPostCreate(appName) + if networks == "" { return nil } - exists, err := networkExists(networkName) - if err != nil { - return err + for _, networkName := range strings.Split(networks, ",") { + exists, err := networkExists(networkName) + if err != nil { + return err + } + + if !exists { + return fmt.Errorf("Network %v does not exist", networkName) + } + + return attachAppToNetwork(containerID, networkName, appName, phase, processType) } - if !exists { - return fmt.Errorf("Network %v does not exist", networkName) - } - - return attachAppToNetwork(containerID, networkName, appName, phase, processType) + return nil } // TriggerPostCreate sets bind-all-interfaces to false by default @@ -260,34 +264,37 @@ func TriggerPostDelete(appName string) error { // TriggerCorePostDeploy associates the container with a specified network func TriggerCorePostDeploy(appName string) error { - networkName := reportComputedAttachPostDeploy(appName) - if networkName == "" { + networks := reportComputedAttachPostDeploy(appName) + if networks == "" { return nil } - common.LogInfo1Quiet(fmt.Sprintf("Associating app with network %s", networkName)) - containerIDs, err := common.GetAppRunningContainerIDs(appName, "") - if err != nil { - return err - } - - exists, err := networkExists(networkName) - if err != nil { - return err - } - - if !exists { - return fmt.Errorf("Network %v does not exist", networkName) - } - - for _, containerID := range containerIDs { - processType, err := common.DockerInspect(containerID, "{{ index .Config.Labels \"com.dokku.process-type\"}}") + for _, networkName := range strings.Split(networks, ",") { + common.LogInfo1Quiet(fmt.Sprintf("Associating app with network %s", networkName)) + containerIDs, err := common.GetAppRunningContainerIDs(appName, "") if err != nil { return err } - if err := attachAppToNetwork(containerID, networkName, appName, "deploy", processType); err != nil { + + exists, err := networkExists(networkName) + if err != nil { return err } + + if !exists { + return fmt.Errorf("Network %v does not exist", networkName) + } + + for _, containerID := range containerIDs { + processType, err := common.DockerInspect(containerID, "{{ index .Config.Labels \"com.dokku.process-type\"}}") + if err != nil { + return err + } + if err := attachAppToNetwork(containerID, networkName, appName, "deploy", processType); err != nil { + return err + } + } } + return nil } diff --git a/tests/unit/network.bats b/tests/unit/network.bats index 9a5abb6bd..92599b517 100644 --- a/tests/unit/network.bats +++ b/tests/unit/network.bats @@ -156,11 +156,21 @@ teardown() { echo "status: $status" assert_success + run /bin/bash -c "dokku network:create create-network-2" + echo "output: $output" + echo "status: $status" + assert_success + run /bin/bash -c "dokku network:create deploy-network" echo "output: $output" echo "status: $status" assert_success + run /bin/bash -c "dokku network:create deploy-network-2" + echo "output: $output" + echo "status: $status" + assert_success + run /bin/bash -c "dokku network:create initial-network" echo "output: $output" echo "status: $status" @@ -208,6 +218,22 @@ teardown() { assert_success assert_http_success "${TEST_APP}.dokku.me" + run /bin/bash -c "dokku network:set $TEST_APP attach-post-create create-network create-network-2" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku network:set $TEST_APP attach-post-deploy deploy-network deploy-network-2" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku ps:rebuild $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + assert_http_success "${TEST_APP}.dokku.me" + run /bin/bash -c "dokku --force network:destroy create-network" echo "output: $output" echo "status: $status" @@ -224,7 +250,15 @@ teardown() { assert_success # necessary in order to remove networks in use by "dead" containers - docker container prune --force + run /bin/bash -c "docker container prune --force" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "docker container ls -a" + echo "output: $output" + echo "status: $status" + assert_success run /bin/bash -c "dokku --force network:destroy create-network" echo "output: $output" From 3c5a98cb1b76e873ff9220dbf3799d80a71e190d Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sat, 1 Jul 2023 04:14:53 -0400 Subject: [PATCH 03/61] chore: remove stickler-ci config Stickler-CI was shutdown in May 2023 - https://stickler-ci.com/shutting-down - and thus we no longer need configuration for it. So long, and thanks for all the fish :) --- .stickler.yml | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 .stickler.yml diff --git a/.stickler.yml b/.stickler.yml deleted file mode 100644 index de214fa2c..000000000 --- a/.stickler.yml +++ /dev/null @@ -1,13 +0,0 @@ ---- -linters: - shellcheck: - shell: bash - exclude: SC2034,SC1090,SC2191 - golint: - -files: - ignore: - - 'debian/*' - - 'docs/*' - - 'tests/*' - - '*/vendor/*' From 5dc2f204324accb6f84746b9e657e941167b630a Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sat, 1 Jul 2023 03:22:01 -0400 Subject: [PATCH 04/61] fix: remove all containers and images by label on app destroy Previously, we would filter containers and images by name, which could be fail if the app was renamed or no longer was tagged "correctly" due to a rebuild. Now, we filter by label, ensuring an app is completely cleaned up on deletion. Also: - Move docker image cleanup from apps plugin to builder plugin: This isn't where it belongs - images should be cleaned up in a builder plugin (or the builders plugin) and not in the apps plugin, which has nothing to do with docker images anyways. - Remove the code from the scheduler plugin, as that will only do anything if the scheduler is enabled for the app. - Bypass image removal if there are no images to remove - Always delete app containers: The herokuish builder creates intermediate build containers - something that should be refactored - and ideally this gets run in that builder plugin, but keeping the logic here ensures we don't have code duplication. This is otherwise safe as it will be a no-op if there are no containers to clean up. --- plugins/apps/functions.go | 48 -------------------- plugins/apps/triggers.go | 14 ------ plugins/builder/functions.go | 53 ++++++++++++++++++++++ plugins/builder/triggers.go | 20 +++++++- plugins/common/docker.go | 4 ++ plugins/scheduler-docker-local/post-delete | 18 +------- 6 files changed, 78 insertions(+), 79 deletions(-) diff --git a/plugins/apps/functions.go b/plugins/apps/functions.go index 13cb9105e..60cfb6e4c 100644 --- a/plugins/apps/functions.go +++ b/plugins/apps/functions.go @@ -1,7 +1,6 @@ package apps import ( - "bytes" "errors" "fmt" "os" @@ -95,53 +94,6 @@ func destroyApp(appName string) error { return nil } -func listImagesByAppLabel(appName string) ([]string, error) { - command := []string{ - common.DockerBin(), - "image", - "list", - "--quiet", - "--filter", - fmt.Sprintf("label=com.dokku.app-name=%v", appName), - } - - var stderr bytes.Buffer - listCmd := common.NewShellCmd(strings.Join(command, " ")) - listCmd.ShowOutput = false - listCmd.Command.Stderr = &stderr - b, err := listCmd.Output() - - if err != nil { - return []string{}, errors.New(strings.TrimSpace(stderr.String())) - } - - output := strings.Split(strings.TrimSpace(string(b[:])), "\n") - return output, nil -} - -func listImagesByImageRepo(imageRepo string) ([]string, error) { - command := []string{ - common.DockerBin(), - "image", - "list", - "--quiet", - imageRepo, - } - - var stderr bytes.Buffer - listCmd := common.NewShellCmd(strings.Join(command, " ")) - listCmd.ShowOutput = false - listCmd.Command.Stderr = &stderr - b, err := listCmd.Output() - - if err != nil { - return []string{}, errors.New(strings.TrimSpace(stderr.String())) - } - - output := strings.Split(strings.TrimSpace(string(b[:])), "\n") - return output, nil -} - // creates an app if allowed func maybeCreateApp(appName string) error { if err := appExists(appName); err == nil { diff --git a/plugins/apps/triggers.go b/plugins/apps/triggers.go index c980b3203..9a82d9363 100644 --- a/plugins/apps/triggers.go +++ b/plugins/apps/triggers.go @@ -101,19 +101,5 @@ func TriggerPostDelete(appName string) error { common.LogWarn(err.Error()) } - imagesByAppLabel, err := listImagesByAppLabel(appName) - if err != nil { - common.LogWarn(err.Error()) - } - - imageRepo := common.GetAppImageRepo(appName) - imagesByRepo, err := listImagesByImageRepo(imageRepo) - if err != nil { - common.LogWarn(err.Error()) - } - - images := append(imagesByAppLabel, imagesByRepo...) - common.RemoveImages(images) - return nil } diff --git a/plugins/builder/functions.go b/plugins/builder/functions.go index eb90e10ab..e7ac81337 100644 --- a/plugins/builder/functions.go +++ b/plugins/builder/functions.go @@ -1,11 +1,64 @@ package builder import ( + "bytes" + "errors" + "fmt" "io/ioutil" "os" "path" + "strings" + + "github.com/dokku/dokku/plugins/common" ) +func listImagesByAppLabel(appName string) ([]string, error) { + command := []string{ + common.DockerBin(), + "image", + "list", + "--quiet", + "--filter", + fmt.Sprintf("label=com.dokku.app-name=%v", appName), + } + + var stderr bytes.Buffer + listCmd := common.NewShellCmd(strings.Join(command, " ")) + listCmd.ShowOutput = false + listCmd.Command.Stderr = &stderr + b, err := listCmd.Output() + + if err != nil { + return []string{}, errors.New(strings.TrimSpace(stderr.String())) + } + + output := strings.Split(strings.TrimSpace(string(b[:])), "\n") + return output, nil +} + +func listImagesByImageRepo(imageRepo string) ([]string, error) { + command := []string{ + common.DockerBin(), + "image", + "list", + "--quiet", + imageRepo, + } + + var stderr bytes.Buffer + listCmd := common.NewShellCmd(strings.Join(command, " ")) + listCmd.ShowOutput = false + listCmd.Command.Stderr = &stderr + b, err := listCmd.Output() + + if err != nil { + return []string{}, errors.New(strings.TrimSpace(stderr.String())) + } + + output := strings.Split(strings.TrimSpace(string(b[:])), "\n") + return output, nil +} + func removeAllContents(basePath string) error { dir, err := ioutil.ReadDir(basePath) if err != nil { diff --git a/plugins/builder/triggers.go b/plugins/builder/triggers.go index be1bfe560..2b4cc240b 100644 --- a/plugins/builder/triggers.go +++ b/plugins/builder/triggers.go @@ -129,5 +129,23 @@ func TriggerPostAppRenameSetup(oldAppName string, newAppName string) error { // TriggerPostDelete destroys the builder property for a given app container func TriggerPostDelete(appName string) error { - return common.PropertyDestroy("builder", appName) + if err := common.PropertyDestroy("builder", appName); err != nil { + return err + } + + imagesByAppLabel, err := listImagesByAppLabel(appName) + if err != nil { + common.LogWarn(err.Error()) + } + + imageRepo := common.GetAppImageRepo(appName) + imagesByRepo, err := listImagesByImageRepo(imageRepo) + if err != nil { + common.LogWarn(err.Error()) + } + + images := append(imagesByAppLabel, imagesByRepo...) + common.RemoveImages(images) + + return nil } diff --git a/plugins/common/docker.go b/plugins/common/docker.go index 70bae5703..9eea856d7 100644 --- a/plugins/common/docker.go +++ b/plugins/common/docker.go @@ -329,6 +329,10 @@ func ListDanglingImages(appName string) ([]string, error) { // RemoveImages removes images by ID func RemoveImages(imageIDs []string) { + if len(imageIDs) == 0 { + return + } + command := []string{ DockerBin(), "image", diff --git a/plugins/scheduler-docker-local/post-delete b/plugins/scheduler-docker-local/post-delete index dd1bce3c9..26c631ec1 100755 --- a/plugins/scheduler-docker-local/post-delete +++ b/plugins/scheduler-docker-local/post-delete @@ -12,23 +12,9 @@ trigger-scheduler-docker-local-post-delete() { fn-plugin-property-destroy "scheduler-docker-local" "$APP" rm -rf "${DOKKU_LIB_ROOT}/data/scheduler-docker-local/$APP" - local DOKKU_SCHEDULER=$(get_app_scheduler "$APP") - if [[ "$DOKKU_SCHEDULER" != "docker-local" ]]; then - return - fi - - local IMAGE_REPO=$(get_app_image_repo "$APP") - - # remove all application containers & images + # remove all application containers # shellcheck disable=SC2046 - local DOKKU_APP_CIDS=$("$DOCKER_BIN" container list --all --no-trunc | grep "dokku/${APP}:" | awk '{ print $1 }' | xargs) - if [[ -n "$DOKKU_APP_CIDS" ]]; then - # shellcheck disable=SC2086 - "$DOCKER_BIN" container rm --force $DOKKU_APP_CIDS >/dev/null 2>&1 || true - fi - - # shellcheck disable=SC2046 - "$DOCKER_BIN" image remove $("$DOCKER_BIN" image list --quiet "$IMAGE_REPO" | xargs) &>/dev/null || true + "$DOCKER_BIN" container ls --filter "label=com.dokku.app-name=${APP}" -q | xargs -n1 -I {} "$DOCKER_BIN" container rm --force {} &>/dev/null || true } trigger-scheduler-docker-local-post-delete "$@" From 4436bb20235786f4973401d3a1df774dbd01a9d6 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sat, 1 Jul 2023 03:29:00 -0400 Subject: [PATCH 05/61] chore: standardize on ls subcommand when interacting with the docker binary The ls command is what is referenced in the --help output for the subcommands, so we should just use that everywhere. --- plugins/builder/functions.go | 4 ++-- plugins/common/docker.go | 4 ++-- plugins/network/functions.go | 2 +- plugins/scheduler-docker-local/core-post-deploy | 2 +- plugins/scheduler-docker-local/scheduler-pre-restore | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/builder/functions.go b/plugins/builder/functions.go index e7ac81337..df7d2f863 100644 --- a/plugins/builder/functions.go +++ b/plugins/builder/functions.go @@ -16,7 +16,7 @@ func listImagesByAppLabel(appName string) ([]string, error) { command := []string{ common.DockerBin(), "image", - "list", + "ls", "--quiet", "--filter", fmt.Sprintf("label=com.dokku.app-name=%v", appName), @@ -40,7 +40,7 @@ func listImagesByImageRepo(imageRepo string) ([]string, error) { command := []string{ common.DockerBin(), "image", - "list", + "ls", "--quiet", imageRepo, } diff --git a/plugins/common/docker.go b/plugins/common/docker.go index 9eea856d7..9393a4a6c 100644 --- a/plugins/common/docker.go +++ b/plugins/common/docker.go @@ -303,7 +303,7 @@ func ListDanglingImages(appName string) ([]string, error) { command := []string{ DockerBin(), "image", - "list", + "ls", "--quiet", "--filter", "dangling=true", @@ -360,7 +360,7 @@ func DockerFilterContainers(filters []string) ([]string, error) { command := []string{ DockerBin(), "container", - "list", + "ls", "--quiet", "--all", } diff --git a/plugins/network/functions.go b/plugins/network/functions.go index d1fd35830..d7bc29c9d 100644 --- a/plugins/network/functions.go +++ b/plugins/network/functions.go @@ -128,7 +128,7 @@ func networkExists(networkName string) (bool, error) { // listNetworks returns a list of docker networks func listNetworks() ([]string, error) { - b, err := sh.Command(common.DockerBin(), "network", "list", "--format", "{{ .Name }}").Output() + b, err := sh.Command(common.DockerBin(), "network", "ls", "--format", "{{ .Name }}").Output() output := strings.TrimSpace(string(b[:])) networks := []string{} diff --git a/plugins/scheduler-docker-local/core-post-deploy b/plugins/scheduler-docker-local/core-post-deploy index 5100ebf84..8f7924ff1 100755 --- a/plugins/scheduler-docker-local/core-post-deploy +++ b/plugins/scheduler-docker-local/core-post-deploy @@ -37,7 +37,7 @@ trigger-scheduler-docker-local-core-post-deploy() { local NAME="$APP.$DYNO" local CURRENT_CONTAINER_ID="$(<"$container")" # TODO: Ensure these are from the current service - local PREVIOUS_CIDS=$("$DOCKER_BIN" container list --all --quiet --filter name="^.?$NAME\$" | xargs) || true + local PREVIOUS_CIDS=$("$DOCKER_BIN" container ls --all --quiet --filter name="^.?$NAME\$" | xargs) || true if [[ -n $PREVIOUS_CIDS ]]; then dokku_log_verbose_quiet "Found previous container(s) ($PREVIOUS_CIDS) named $NAME" # in case $PREVIOUS_CIDS has more than one entry diff --git a/plugins/scheduler-docker-local/scheduler-pre-restore b/plugins/scheduler-docker-local/scheduler-pre-restore index 6a88d8ea4..e04755d3a 100755 --- a/plugins/scheduler-docker-local/scheduler-pre-restore +++ b/plugins/scheduler-docker-local/scheduler-pre-restore @@ -14,7 +14,7 @@ trigger-scheduler-docker-local-scheduler-pre-restore() { # delete all "old" containers # shellcheck disable=SC2046 - "$DOCKER_BIN" container rm $("$DOCKER_BIN" container list --all --format "{{.Names}}" --filter "label=$DOKKU_CONTAINER_LABEL" --quiet | grep -E '(.+\..+\.[0-9]+\.[0-9]+$)') &>/dev/null || true + "$DOCKER_BIN" container rm $("$DOCKER_BIN" container ls --all --format "{{.Names}}" --filter "label=$DOKKU_CONTAINER_LABEL" --quiet | grep -E '(.+\..+\.[0-9]+\.[0-9]+$)') &>/dev/null || true } trigger-scheduler-docker-local-scheduler-pre-restore "$@" From dac566e75ebcfdc26636d243630fade41062ac30 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sat, 1 Jul 2023 04:21:46 -0400 Subject: [PATCH 06/61] refactor: move all shellcheck disable definitions to .shellcheckrc file This makes standard use of shellcheck work without needing to provide extra configuration anywhere. Also remove use of inline 'shellcheck disable' calls that are already defined in the .shellcheckrc and don't need to be set inline. --- .github/workflows/lint.yml | 3 -- .shellcheckrc | 34 ++++++++++++++++--- bootstrap.sh | 4 --- contrib/dokku-docker-bin.sh | 1 - contrib/dokku_client.sh | 1 - .../digitalocean/in_parts/100-image-check | 8 ----- contrib/release-dokku | 3 -- contrib/release-plugin | 3 -- debian/config | 1 - debian/postinst | 2 -- debian/postrm | 1 - debian/preinst | 1 - dokku | 4 --- plugins/20_events/install | 1 - plugins/builder-lambda/builder-build | 1 - plugins/docker-options/docker-args-deploy | 1 - plugins/docker-options/subcommands/remove | 1 - plugins/domains/functions | 2 -- plugins/git/git-from-directory | 1 - plugins/nginx-vhosts/functions | 1 - .../bin/scheduler-deploy-process | 1 - plugins/scheduler-docker-local/check-deploy | 3 -- .../scheduler-docker-local/internal-functions | 1 - plugins/scheduler-docker-local/post-delete | 1 - plugins/scheduler-docker-local/pre-deploy | 1 - .../scheduler-docker-local/scheduler-deploy | 1 - .../scheduler-docker-local/scheduler-enter | 1 - .../scheduler-docker-local/scheduler-run-stop | 1 - plugins/scheduler-docker-local/scheduler-stop | 3 -- plugins/shell/subcommands/default | 1 - tests.mk | 6 ++-- tests/ci/setup.sh | 3 -- tests/ci/unit_test_runner.sh | 1 - tests/shellcheck-exclude | 32 ----------------- tests/test_deploy | 2 -- tests/unit/test_helper.bash | 13 ------- 36 files changed, 32 insertions(+), 113 deletions(-) delete mode 100644 tests/shellcheck-exclude diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 84135ae54..0553e9a20 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -68,9 +68,6 @@ jobs: - name: Run shellcheck uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # 1.1.0 => 94e0aab03ca135d11a35e5bfc14e6746dc56e7e9 - env: - # keep in sync with tests/shellcheck-exclude - SHELLCHECK_OPTS: -e SC1001 -e SC1003 -e SC1090 -e SC1091 -e SC1117 -e SC2029 -e SC2030 -e SC2031 -e SC2034 -e SC2046 -e SC2064 -e SC2068 -e SC2086 -e SC2119 -e SC2120 -e SC2128 -e SC2148 -e SC2153 -e SC2154 -e SC2155 -e SC2162 -e SC2174 -e SC2179 -e SC2191 -e SC2199 -e SC2207 -e SC2219 -e SC2220 -e SC2230 -e SC2231 -e SC2235 -e SC2267 -e SC2295 shfmt: name: shfmt diff --git a/.shellcheckrc b/.shellcheckrc index eed21d662..dffeb4f39 100644 --- a/.shellcheckrc +++ b/.shellcheckrc @@ -1,9 +1,33 @@ +# SC1001 - This \/ will be a regular '/' in this context - https://github.com/koalaman/shellcheck/wiki/SC1001 +# SC1003 - Want to escape a single quote? echo 'This is how it'\''s done' - https://github.com/koalaman/shellcheck/wiki/SC1003 +# SC1090 - Can't follow non-constant source. Use a directive to specify location - https://github.com/koalaman/shellcheck/wiki/SC1090 # SC1091 - Not following: FILE: does not exist (No such file or directory) - https://github.com/koalaman/shellcheck/wiki/SC1091 -# - used for sourcing function files throughout the codebase +# SC1117 - Backslash is literal in "\n". Prefer explicit escaping: "\\n" - https://github.com/koalaman/shellcheck/wiki/SC1117 +# SC2029 - Note that, unescaped, this expands on the client side - https://github.com/koalaman/shellcheck/wiki/SC2029 +# SC2030 - Modification of var is local (to subshell caused by pipeline). - https://github.com/koalaman/shellcheck/wiki/SC2030 +# SC2031 - var was modified in a subshell. That change might be lost. - https://github.com/koalaman/shellcheck/wiki/SC2031 # SC2034 - VAR appears unused - https://github.com/koalaman/shellcheck/wiki/SC2034 -# - used for declaring desc and deprecated function variables +# SC2046 - Quote this to prevent word splitting - https://github.com/koalaman/shellcheck/wiki/SC2046 # SC2064 - Use single quotes, otherwise this expands now rather than when signalled. - https://github.com/koalaman/shellcheck/wiki/SC2064 -# - used for traps +# SC2068 - Double quote array expansions to avoid re-splitting elements - https://github.com/koalaman/shellcheck/wiki/SC2068 +# SC2086 - Double quote to prevent globbing and word splitting - https://github.com/koalaman/shellcheck/wiki/SC2086 +# SC2119 - Use 'function "$@"' if function's $1 should mean script's $1 - https://github.com/koalaman/shellcheck/wiki/SC2119 +# SC2120 - Function references arguments, but none are ever passed - https://github.com/koalaman/shellcheck/wiki/SC2120 +# SC2128 - Expanding an array without an index only gives the first element - https://github.com/koalaman/shellcheck/wiki/SC2128 +# SC2148 - Tips depend on target shell and yours is unknown. Add a shebang - https://github.com/koalaman/shellcheck/wiki/SC2148 +# SC2153 - Possible misspelling: DOKKU_ROOT may not be assigned, but DOKKU_PORT is - https://github.com/koalaman/shellcheck/wiki/SC2153 +# SC2154 - Var is referenced but not assigned - https://github.com/koalaman/shellcheck/wiki/SC2154 # SC2155 - Declare and assign separately to avoid masking return values - https://github.com/koalaman/shellcheck/wiki/SC2155 -# - used throughout the codebase -disable=SC1091,SC2034,SC2064,SC2155 +# SC2162 - read without -r will mangle backslashes - https://github.com/koalaman/shellcheck/wiki/SC2162 +# SC2174 - When used with -p, -m only applies to the deepest directory - https://github.com/koalaman/shellcheck/wiki/SC2174 +# SC2179 - Use array+=("item") to append items to an array - https://github.com/koalaman/shellcheck/wiki/SC2179 +# SC2191 - The = here is literal. To assign by index, use ( [index]=value ) with no spaces. To keep as literal, quote it - https://github.com/koalaman/shellcheck/wiki/SC2191 +# SC2199 - Arrays implicitly concatenate in [[ ]]. Use a loop (or explicit * instead of @) - https://github.com/koalaman/shellcheck/wiki/SC2199 +# SC2207 - Prefer mapfile or read -a to split command output (or quote to avoid splitting) - https://github.com/koalaman/shellcheck/wiki/SC2207 +# SC2219 - Instead of 'let expr', prefer (( expr )) - https://github.com/koalaman/shellcheck/wiki/SC2219 +# SC2220 - Invalid flags are not handled. Add a *) case - https://github.com/koalaman/shellcheck/wiki/SC2220 +# SC2231 - Quote expansions in this for loop glob to prevent wordsplitting, e.g. "$dir"/*.txt - https://github.com/koalaman/shellcheck/wiki/SC2231 +# SC2235 - Use { ..; } instead of (..) to avoid subshell overhead - https://github.com/koalaman/shellcheck/wiki/SC2235 +# SC2267 - GNU xargs -i is deprecated in favor of -I{} - https://github.com/koalaman/shellcheck/wiki/SC2267 +# SC2295 - Expansions inside ${..} need to be quoted separately, otherwise they match as patterns - https://github.com/koalaman/shellcheck/wiki/SC2295 +disable=SC1001,SC1003,SC1090,SC1091,SC1117,SC2029,SC2030,SC2031,SC2034,SC2046,SC2064,SC2068,SC2086,SC2119,SC2120,SC2128,SC2148,SC2153,SC2154,SC2155,SC2162,SC2174,SC2179,SC2191,SC2199,SC2207,SC2219,SC2220,SC2231,SC2235,SC2267,SC2295 diff --git a/bootstrap.sh b/bootstrap.sh index 8613cbf1d..04e4cd8cb 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -220,19 +220,15 @@ install-dokku-from-deb-package() { [[ -n $DOKKU_NGINX_ENABLE ]] && echo "dokku dokku/nginx_enable string $DOKKU_NGINX_ENABLE" | sudo debconf-set-selections if [[ -n $DOKKU_CHECKOUT ]]; then - # shellcheck disable=SC2086 apt-get -qq -y $NO_INSTALL_RECOMMENDS install "dokku=$DOKKU_CHECKOUT" else - # shellcheck disable=SC2086 apt-get -qq -y $NO_INSTALL_RECOMMENDS install dokku fi } main() { export DOKKU_DISTRO DOKKU_DISTRO_VERSION - # shellcheck disable=SC1091 DOKKU_DISTRO=$(. /etc/os-release && echo "$ID") - # shellcheck disable=SC1091 DOKKU_DISTRO_VERSION=$(. /etc/os-release && echo "$VERSION_ID") export DEBIAN_FRONTEND=noninteractive diff --git a/contrib/dokku-docker-bin.sh b/contrib/dokku-docker-bin.sh index 3bc362322..9619f4651 100755 --- a/contrib/dokku-docker-bin.sh +++ b/contrib/dokku-docker-bin.sh @@ -4,5 +4,4 @@ set -eo pipefail DOKKU_DOCKER_CONTAINER_NAME=${DOKKU_DOCKER_CONTAINER_NAME:=dokku} # TODO: handle cases where we need a tty -# shellcheck disable=SC2086 docker exec $DOKKU_DOCKER_ENV -i "$DOKKU_DOCKER_CONTAINER_NAME" dokku "$@" diff --git a/contrib/dokku_client.sh b/contrib/dokku_client.sh index 32a3dde63..e0b3ffd4c 100755 --- a/contrib/dokku_client.sh +++ b/contrib/dokku_client.sh @@ -190,7 +190,6 @@ main() { [[ -n "$APP_ARG" ]] && [[ "$APP_ARG" == "--global" ]] && unset APP [[ -n "$@" ]] && [[ -n "$APP" ]] && app_arg="--app $APP" # echo "ssh -o LogLevel=QUIET -p $DOKKU_PORT -t dokku@$DOKKU_REMOTE_HOST -- $app_arg $@" - # shellcheck disable=SC2068,SC2086 ssh -o LogLevel=QUIET -p $DOKKU_PORT -t dokku@$DOKKU_REMOTE_HOST -- $app_arg $@ || { ssh_exit_code="$?" echo " ! Failed to execute dokku command over ssh: exit code $?" 1>&2 diff --git a/contrib/images/digitalocean/in_parts/100-image-check b/contrib/images/digitalocean/in_parts/100-image-check index a14bb87c1..fde0fede7 100755 --- a/contrib/images/digitalocean/in_parts/100-image-check +++ b/contrib/images/digitalocean/in_parts/100-image-check @@ -31,7 +31,6 @@ cmdExists() { function getDistro { if [ -f /etc/os-release ]; then # freedesktop.org and systemd - # shellcheck disable=SC1091 . /etc/os-release OS=$NAME VER=$VERSION_ID @@ -41,7 +40,6 @@ function getDistro { VER=$(lsb_release -sr) elif [ -f /etc/lsb-release ]; then # For some versions of Debian/Ubuntu without lsb_release command - # shellcheck disable=SC1091 . /etc/lsb-release OS=$DISTRIB_ID VER=$DISTRIB_RELEASE @@ -255,11 +253,9 @@ function checkUsers { if [[ "${u[0]}" == "${user}" ]]; then if [[ ${u[1]} == "!" ]] || [[ ${u[1]} == "!!" ]] || [[ ${u[1]} == "*" ]]; then echo -en "\e[32m[PASS]\e[0m User ${user} has no password set.\n" - # shellcheck disable=SC2030 ((PASS++)) else echo -en "\e[41m[FAIL]\e[0m User ${user} has a password set on their account. Only system users are allowed on the image.\n" - # shellcheck disable=SC2030 ((FAIL++)) STATUS=2 fi @@ -292,7 +288,6 @@ function checkUsers { STATUS=2 else echo -en "\e[93m[WARN]\e[0m User \e[1m${user}\e[0m has empty private key file in \e[93m${key}\e[0m\n" - # shellcheck disable=SC2030 ((WARN++)) if [[ $STATUS != 2 ]]; then STATUS=1 @@ -352,11 +347,9 @@ function checkFirewall { ufwa=$(ufw status | head -1 | sed -e "s/^Status:\ //") if [[ $ufwa == "active" ]]; then FW_VER="\e[32m[PASS]\e[0m Firewall service (${fw}) is active\n" - # shellcheck disable=SC2031 ((PASS++)) else FW_VER="\e[93m[WARN]\e[0m No firewall is configured. Ensure ${fw} is installed and configured\n" - # shellcheck disable=SC2031 ((WARN++)) fi elif [[ $OS == "CentOS Linux" ]] || [[ $OS == "CentOS Stream" ]] || [[ $OS == "Rocky Linux" ]] || [[ $OS == "AlmaLinux" ]]; then @@ -451,7 +444,6 @@ function checkUpdates { sleep 2 apt-get --just-print upgrade | grep -i security | awk '{print $2}' | awk '!seen[$0]++' echo -en - # shellcheck disable=SC2031 ((FAIL++)) STATUS=2 else diff --git a/contrib/release-dokku b/contrib/release-dokku index cff7282c0..ea5935bc6 100755 --- a/contrib/release-dokku +++ b/contrib/release-dokku @@ -9,19 +9,16 @@ readonly DOKKU_GIT_REV="$(git rev-parse HEAD)" trap "rm -rf '$TMP_WORK_DIR' >/dev/null" RETURN INT TERM EXIT log-info() { - # shellcheck disable=SC2034 declare desc="Log info formatter" echo "$*" } log-error() { - # shellcheck disable=SC2034 declare desc="Log error formatter" echo "! $*" 1>&2 } log-fail() { - # shellcheck disable=SC2034 declare desc="Log fail formatter" log-error "$*" exit 1 diff --git a/contrib/release-plugin b/contrib/release-plugin index e0cbc55f2..f034414b2 100755 --- a/contrib/release-plugin +++ b/contrib/release-plugin @@ -7,19 +7,16 @@ readonly TMP_WORK_DIR="$(mktemp -d "/tmp/dokku-plugin-release.XXXXXX")" trap "rm -rf '$TMP_WORK_DIR' >/dev/null" RETURN INT TERM EXIT log-info() { - # shellcheck disable=SC2034 declare desc="Log info formatter" echo "$*" } log-error() { - # shellcheck disable=SC2034 declare desc="Log error formatter" echo "! $*" 1>&2 } log-fail() { - # shellcheck disable=SC2034 declare desc="Log fail formatter" log-error "$*" exit 1 diff --git a/debian/config b/debian/config index e4bcc25be..99bba1b23 100755 --- a/debian/config +++ b/debian/config @@ -3,7 +3,6 @@ set -eo pipefail [[ $TRACE ]] && set -x if [[ -e /usr/share/debconf/confmodule ]]; then - # shellcheck disable=SC1091 . /usr/share/debconf/confmodule fi diff --git a/debian/postinst b/debian/postinst index 0663ca651..1b755a173 100755 --- a/debian/postinst +++ b/debian/postinst @@ -3,12 +3,10 @@ set -eo pipefail [[ $TRACE ]] && set -x if [[ -e /usr/share/debconf/confmodule ]]; then - # shellcheck disable=SC1091 . /usr/share/debconf/confmodule fi if [[ -r /etc/default/dokku ]]; then - # shellcheck disable=SC1091 source /etc/default/dokku fi diff --git a/debian/postrm b/debian/postrm index 6c15a4d09..78f3b83de 100755 --- a/debian/postrm +++ b/debian/postrm @@ -3,7 +3,6 @@ set -eo pipefail [[ $TRACE ]] && set -x if [[ -e /usr/share/debconf/confmodule ]]; then - # shellcheck disable=SC1091 . /usr/share/debconf/confmodule fi diff --git a/debian/preinst b/debian/preinst index e4dfdef5e..fb7104341 100755 --- a/debian/preinst +++ b/debian/preinst @@ -3,7 +3,6 @@ set -eo pipefail [[ $TRACE ]] && set -x if [[ -e /usr/share/debconf/confmodule ]]; then - # shellcheck disable=SC1091 . /usr/share/debconf/confmodule fi diff --git a/dokku b/dokku index 23570f28d..7e37af297 100755 --- a/dokku +++ b/dokku @@ -4,7 +4,6 @@ shopt -s nullglob export DOCKER_BIN=docker if [[ -r /etc/default/dokku ]]; then - # shellcheck disable=SC1091 source /etc/default/dokku fi @@ -31,7 +30,6 @@ fi export DOKKU_HOST_ROOT=${DOKKU_HOST_ROOT:=$DOKKU_ROOT} export DOKKU_DISTRO -# shellcheck disable=SC1091 DOKKU_DISTRO=$( . /etc/os-release >/dev/null 2>&1 || true echo "$ID" @@ -109,12 +107,10 @@ fi if [[ -n "$SSH_ORIGINAL_COMMAND" ]]; then export -n SSH_ORIGINAL_COMMAND if [[ $1 =~ config-* ]] || [[ $1 =~ docker-options* ]]; then - # shellcheck disable=SC2086 xargs $0 <<<$SSH_ORIGINAL_COMMAND exit $? else set -f - # shellcheck disable=SC2086 $0 $SSH_ORIGINAL_COMMAND set +f exit $? diff --git a/plugins/20_events/install b/plugins/20_events/install index d1eea5198..003a1e89d 100755 --- a/plugins/20_events/install +++ b/plugins/20_events/install @@ -14,7 +14,6 @@ trigger-events-install() { # This can be done unconditionally as mkdir -p # exits gracefully if the path already exists - # shellcheck disable=SC2174 mkdir -m 775 -p "$DOKKU_LOGS_DIR" case "$DOKKU_DISTRO" in arch | debian | raspbian) diff --git a/plugins/builder-lambda/builder-build b/plugins/builder-lambda/builder-build index eb13cd21b..2c1b7a107 100755 --- a/plugins/builder-lambda/builder-build +++ b/plugins/builder-lambda/builder-build @@ -20,7 +20,6 @@ trigger-builder-lambda-builder-build() { plugn trigger pre-build-lambda "$APP" - # shellcheck disable=SC2086 lambda-builder build --generate-image --write-procfile --image-env=DOCKER_LAMBDA_STAY_OPEN=1 --label=org.label-schema.schema-version=1.0 --label=org.label-schema.vendor=dokku --label=com.dokku.image-stage=build --label=com.dokku.builder-type=lambda "--label=com.dokku.app-name=$APP" $DOKKU_GLOBAL_BUILD_ARGS --port 5000 --tag "$IMAGE" --working-directory "$SOURCECODE_WORK_DIR" if [[ ! -f "$SOURCECODE_WORK_DIR/lambda.zip" ]]; then dokku_log_warn "Compressed lambda.zip not detected, failed to build lambda function" diff --git a/plugins/docker-options/docker-args-deploy b/plugins/docker-options/docker-args-deploy index 9bc2ec673..9c4d8c2ec 100755 --- a/plugins/docker-options/docker-args-deploy +++ b/plugins/docker-options/docker-args-deploy @@ -33,7 +33,6 @@ trigger-docker-options-docker-args() { [[ -z "$line" ]] && continue - # shellcheck disable=SC1001 case "$line" in \#*) continue diff --git a/plugins/docker-options/subcommands/remove b/plugins/docker-options/subcommands/remove index f258a0b34..bddbbcdbd 100755 --- a/plugins/docker-options/subcommands/remove +++ b/plugins/docker-options/subcommands/remove @@ -13,7 +13,6 @@ cmd-docker-options-remove() { verify_app_name "$APP" read -ra passed_phases <<<"$(get_phases "$2")" shift 2 # everything else passed is the docker option - # shellcheck disable=SC2154 [[ -z ${passed_docker_option="$@"} ]] && dokku_log_fail "Please specify docker options to remove from the phase" remove_passed_docker_option passed_phases[@] "${passed_docker_option[@]}" } diff --git a/plugins/domains/functions b/plugins/domains/functions index 17c59c7aa..9b6e481ee 100755 --- a/plugins/domains/functions +++ b/plugins/domains/functions @@ -16,7 +16,6 @@ disable_app_vhost() { fi [[ "$RESTART_APP" == "--no-restart" ]] && local CONFIG_SET_ARGS=$RESTART_APP - # shellcheck disable=SC2086 DOKKU_QUIET_OUTPUT=1 config_set $CONFIG_SET_ARGS $APP NO_VHOST=1 } @@ -196,7 +195,6 @@ enable_app_vhost() { plugn trigger pre-enable-vhost "$APP" [[ "$RESTART_APP" == "--no-restart" ]] && local CONFIG_SET_ARGS=$RESTART_APP - # shellcheck disable=SC2086 DOKKU_QUIET_OUTPUT=1 config_set $CONFIG_SET_ARGS "$APP" NO_VHOST=0 } diff --git a/plugins/git/git-from-directory b/plugins/git/git-from-directory index 9f94568c5..03e7058de 100755 --- a/plugins/git/git-from-directory +++ b/plugins/git/git-from-directory @@ -15,7 +15,6 @@ trigger-git-git-from-directory() { local REV local TMP_WORK_DIR=$(mktemp -d "/tmp/dokku-${DOKKU_PID}-${FUNCNAME[0]}.XXXXXX") local TMP_WORK_DIR_2=$(mktemp -d "/tmp/dokku-${DOKKU_PID}-${FUNCNAME[0]}.XXXXXX") - # shellcheck disable=SC2086 trap "rm -rf '$TMP_WORK_DIR' '$TMP_WORK_DIR_2' >/dev/null" RETURN INT TERM EXIT local has_code=true diff --git a/plugins/nginx-vhosts/functions b/plugins/nginx-vhosts/functions index 25ccdd4e7..299e84aab 100755 --- a/plugins/nginx-vhosts/functions +++ b/plugins/nginx-vhosts/functions @@ -406,7 +406,6 @@ nginx_build_config() { local NGINX_BUILD_CONFIG_TMP_WORK_DIR=$(mktemp -d "/tmp/dokku-${DOKKU_PID}-${FUNCNAME[0]}.XXXXXX") local NGINX_CONF=$(mktemp --tmpdir="${NGINX_BUILD_CONFIG_TMP_WORK_DIR}" "nginx.conf.XXXXXX") local CUSTOM_NGINX_TEMPLATE="$NGINX_BUILD_CONFIG_TMP_WORK_DIR/$NGINX_TEMPLATE_NAME" - # shellcheck disable=SC2086 trap "rm -rf '$NGINX_CONF' '$NGINX_BUILD_CONFIG_TMP_WORK_DIR' >/dev/null" RETURN INT TERM EXIT get_custom_nginx_template "$APP" "$CUSTOM_NGINX_TEMPLATE" 2>/dev/null diff --git a/plugins/scheduler-docker-local/bin/scheduler-deploy-process b/plugins/scheduler-docker-local/bin/scheduler-deploy-process index 27d59889e..a088ee9df 100755 --- a/plugins/scheduler-docker-local/bin/scheduler-deploy-process +++ b/plugins/scheduler-docker-local/bin/scheduler-deploy-process @@ -24,7 +24,6 @@ main() { # Disable the container restart policy "$DOCKER_BIN" container update --restart=no "$cid" &>/dev/null || true - # shellcheck disable=SC2086 "$DOCKER_BIN" container stop $DOCKER_STOP_TIME_ARG "$cid" &>/dev/null done fi diff --git a/plugins/scheduler-docker-local/check-deploy b/plugins/scheduler-docker-local/check-deploy index 310b5c9a3..363b0c168 100755 --- a/plugins/scheduler-docker-local/check-deploy +++ b/plugins/scheduler-docker-local/check-deploy @@ -147,10 +147,8 @@ trigger-scheduler-docker-local-check-deploy() { local FAILEDCHECKS=0 while read -r CHECK_URL EXPECTED; do # Ignore empty lines and lines starting with # - # shellcheck disable=SC1001 [[ -z "$CHECK_URL" || "$CHECK_URL" =~ ^\# ]] && continue # Ignore if it's not a URL in a supported format - # shellcheck disable=SC1001 ! [[ "$CHECK_URL" =~ ^(http(s)?:)?\/.* ]] && continue if [[ "$CHECK_URL" =~ ^https?: ]]; then @@ -204,7 +202,6 @@ trigger-scheduler-docker-local-check-deploy() { sleep "$WAIT" # Capture HTTP response or CURL error message - # shellcheck disable=SC2086 if OUTPUT=$(curl -# $CURL_ARGS 2>&1); then # OUTPUT contains the HTTP response # shellcheck disable=SC2076 diff --git a/plugins/scheduler-docker-local/internal-functions b/plugins/scheduler-docker-local/internal-functions index 1a44deae9..de854b4f9 100755 --- a/plugins/scheduler-docker-local/internal-functions +++ b/plugins/scheduler-docker-local/internal-functions @@ -88,7 +88,6 @@ fn-scheduler-docker-local-retire-container() { # Attempt to stop, if that fails, then force a kill as docker seems # to not send SIGKILL as the docs would indicate. If that fails, move # on to the next. - # shellcheck disable=SC2086 "$DOCKER_BIN" container stop $DOCKER_STOP_TIME_ARG "$CID" \ || "$DOCKER_BIN" container kill "$CID" \ || dokku_log_warn "Unable to kill container ${CID}" diff --git a/plugins/scheduler-docker-local/post-delete b/plugins/scheduler-docker-local/post-delete index 26c631ec1..109b07748 100755 --- a/plugins/scheduler-docker-local/post-delete +++ b/plugins/scheduler-docker-local/post-delete @@ -13,7 +13,6 @@ trigger-scheduler-docker-local-post-delete() { rm -rf "${DOKKU_LIB_ROOT}/data/scheduler-docker-local/$APP" # remove all application containers - # shellcheck disable=SC2046 "$DOCKER_BIN" container ls --filter "label=com.dokku.app-name=${APP}" -q | xargs -n1 -I {} "$DOCKER_BIN" container rm --force {} &>/dev/null || true } diff --git a/plugins/scheduler-docker-local/pre-deploy b/plugins/scheduler-docker-local/pre-deploy index 6dcd94fae..b6d1bb197 100755 --- a/plugins/scheduler-docker-local/pre-deploy +++ b/plugins/scheduler-docker-local/pre-deploy @@ -58,7 +58,6 @@ scheduler-docker-local-pre-deploy-chown-app() { return fi - # shellcheck disable=SC2086 "$DOCKER_BIN" container run --rm "${DOCKER_RUN_LABEL_ARGS[@]}" $DOKKU_GLOBAL_RUN_ARGS "${ARG_ARRAY[@]}" $IMAGE /bin/bash -c "find $CONTAINER_PATHS -not -user $DOKKU_APP_USER -print0 | xargs -0 -r chown -R $DOKKU_APP_USER" || true } diff --git a/plugins/scheduler-docker-local/scheduler-deploy b/plugins/scheduler-docker-local/scheduler-deploy index b9fbdae7b..3f742a01b 100755 --- a/plugins/scheduler-docker-local/scheduler-deploy +++ b/plugins/scheduler-docker-local/scheduler-deploy @@ -88,7 +88,6 @@ trigger-scheduler-docker-local-scheduler-deploy() { # Attempt to stop, if that fails, then force a kill as docker seems # to not send SIGKILL as the docs would indicate. If that fails, move # on to the next. - # shellcheck disable=SC2086 "$DOCKER_BIN" container stop $DOCKER_STOP_TIME_ARG "$oldid" \ || "$DOCKER_BIN" container kill "$oldid" \ || plugn trigger retire-container-failed "$APP" "$oldid" # plugin trigger for event logging diff --git a/plugins/scheduler-docker-local/scheduler-enter b/plugins/scheduler-docker-local/scheduler-enter index b6b2a024d..35cc65ff7 100755 --- a/plugins/scheduler-docker-local/scheduler-enter +++ b/plugins/scheduler-docker-local/scheduler-enter @@ -74,7 +74,6 @@ trigger-scheduler-docker-local-scheduler-enter() { local IMAGE="$("$DOCKER_BIN" container inspect --format '{{ .Config.Image }}' "$CONTAINER_ID")" is_image_herokuish_based "$IMAGE" "$APP" && EXEC_CMD="/exec" is_image_cnb_based "$IMAGE" && EXEC_CMD="" && DOKKU_RUN_OPTS+=" -w /workspace" - # shellcheck disable=SC2086 "$DOCKER_BIN" container exec $DOKKU_RUN_OPTS "$CONTAINER_ID" $EXEC_CMD "${@:-$DOKKU_APP_SHELL}" } diff --git a/plugins/scheduler-docker-local/scheduler-run-stop b/plugins/scheduler-docker-local/scheduler-run-stop index 3b9f730d4..d46f9ad72 100755 --- a/plugins/scheduler-docker-local/scheduler-run-stop +++ b/plugins/scheduler-docker-local/scheduler-run-stop @@ -13,7 +13,6 @@ fn-scheduler-docker-local-stop-container() { fi [[ $DOKKU_DOCKER_STOP_TIMEOUT ]] && DOCKER_STOP_TIME_ARG="--time=${DOKKU_DOCKER_STOP_TIMEOUT}" - # shellcheck disable=SC2086 if "$DOCKER_BIN" container stop $DOCKER_STOP_TIME_ARG "$CONTAINER_ID_OR_NAME"; then return fi diff --git a/plugins/scheduler-docker-local/scheduler-stop b/plugins/scheduler-docker-local/scheduler-stop index 32ab1e5ed..a6f2bafd9 100755 --- a/plugins/scheduler-docker-local/scheduler-stop +++ b/plugins/scheduler-docker-local/scheduler-stop @@ -20,10 +20,8 @@ trigger-scheduler-docker-local-scheduler-stop() { if [[ -n "$DOKKU_APP_RUNNING_CONTAINER_IDS" ]]; then # Disable the container restart policy - # shellcheck disable=SC2086 "$DOCKER_BIN" container update --restart=no $DOKKU_APP_RUNNING_CONTAINER_IDS &>/dev/null || true - # shellcheck disable=SC2086 "$DOCKER_BIN" container stop $DOCKER_STOP_TIME_ARG $DOKKU_APP_RUNNING_CONTAINER_IDS >/dev/null || true fi @@ -35,7 +33,6 @@ trigger-scheduler-docker-local-scheduler-stop() { plugn trigger scheduler-register-retired "$APP" "$CID" done - # shellcheck disable=SC2086 "$DOCKER_BIN" container rm --force $DOKKU_APP_CIDS >/dev/null 2>&1 || true fi fi diff --git a/plugins/shell/subcommands/default b/plugins/shell/subcommands/default index 2a8b13abf..00602e160 100755 --- a/plugins/shell/subcommands/default +++ b/plugins/shell/subcommands/default @@ -44,7 +44,6 @@ cmd-shell-default() { # Not a built-in, run as regular dokku command *) - # shellcheck disable=SC2086 dokku $line || true ;; esac diff --git a/tests.mk b/tests.mk index 495d5c80f..a1cd915d3 100644 --- a/tests.mk +++ b/tests.mk @@ -113,13 +113,13 @@ prime-ssh-known-hosts: lint-setup: @mkdir -p test-results/shellcheck tmp/shellcheck @find . -not -path '*/\.*' -not -path './debian/*' -not -path './docs/*' -not -path './tests/*' -not -path './vendor/*' -type f | xargs file | grep text | awk -F ':' '{ print $$1 }' | xargs head -n1 | grep -B1 "bash" | grep "==>" | awk '{ print $$2 }' > tmp/shellcheck/test-files - @cat tests/shellcheck-exclude | sed -n -e '/^# SC/p' | cut -d' ' -f2 | paste -d, -s > tmp/shellcheck/exclude + @cat .shellcheckrc | sed -n -e '/^# SC/p' | cut -d' ' -f2 | paste -d, -s > tmp/shellcheck/exclude lint-ci: lint-setup # these are disabled due to their expansive existence in the codebase. we should clean it up though - @cat tests/shellcheck-exclude | sed -n -e '/^# SC/p' + @cat .shellcheckrc | sed -n -e '/^# SC/p' @echo linting... - @cat tmp/shellcheck/test-files | xargs shellcheck -e $(shell cat tmp/shellcheck/exclude) | tests/shellcheck-to-junit --output test-results/shellcheck/results.xml --files tmp/shellcheck/test-files --exclude $(shell cat tmp/shellcheck/exclude) + @cat tmp/shellcheck/test-files | xargs shellcheck | tests/shellcheck-to-junit --output test-results/shellcheck/results.xml --files tmp/shellcheck/test-files --exclude $(shell cat tmp/shellcheck/exclude) lint-shfmt: shfmt # verifying via shfmt diff --git a/tests/ci/setup.sh b/tests/ci/setup.sh index a8ebd8448..46f2a490e 100755 --- a/tests/ci/setup.sh +++ b/tests/ci/setup.sh @@ -159,7 +159,6 @@ create_package() { build_dokku } -# shellcheck disable=SC2120 setup_circle() { install_dependencies install_dokku @@ -188,12 +187,10 @@ case "$1" in run_dokku_container ;; build) - # shellcheck disable=SC2119 create_package exit $? ;; *) - # shellcheck disable=SC2119 setup_circle exit $? ;; diff --git a/tests/ci/unit_test_runner.sh b/tests/ci/unit_test_runner.sh index 53f18d6d2..4d39c10c8 100755 --- a/tests/ci/unit_test_runner.sh +++ b/tests/ci/unit_test_runner.sh @@ -21,7 +21,6 @@ is_number "$BATCH_NUM" || usage TESTS=$(find "$(dirname "$0")/../unit" -maxdepth 1 -name "${BATCH_NUM}0*.bats" | sort -n | xargs) echo "running the following tests $TESTS" -# shellcheck disable=SC2086 for test in $TESTS; do echo $test starttest=$(date +%s) diff --git a/tests/shellcheck-exclude b/tests/shellcheck-exclude deleted file mode 100644 index 2049f4b80..000000000 --- a/tests/shellcheck-exclude +++ /dev/null @@ -1,32 +0,0 @@ -# SC1001 - This \/ will be a regular '/' in this context - https://github.com/koalaman/shellcheck/wiki/SC1001 -# SC1003 - Want to escape a single quote? echo 'This is how it'\''s done' - https://github.com/koalaman/shellcheck/wiki/SC1003 -# SC1090 - Can't follow non-constant source. Use a directive to specify location - https://github.com/koalaman/shellcheck/wiki/SC1090 -# SC1091 - Not following: FILE: does not exist (No such file or directory) - https://github.com/koalaman/shellcheck/wiki/SC1091 -# SC1117 - Backslash is literal in "\n". Prefer explicit escaping: "\\n" - https://github.com/koalaman/shellcheck/wiki/SC1117 -# SC2029 - Note that, unescaped, this expands on the client side - https://github.com/koalaman/shellcheck/wiki/SC2029 -# SC2030 - Modification of var is local (to subshell caused by pipeline). - https://github.com/koalaman/shellcheck/wiki/SC2030 -# SC2031 - var was modified in a subshell. That change might be lost. - https://github.com/koalaman/shellcheck/wiki/SC2031 -# SC2034 - VAR appears unused - https://github.com/koalaman/shellcheck/wiki/SC2034 -# SC2046 - Quote this to prevent word splitting - https://github.com/koalaman/shellcheck/wiki/SC2046 -# SC2064 - Use single quotes, otherwise this expands now rather than when signalled. - https://github.com/koalaman/shellcheck/wiki/SC2064 -# SC2068 - Double quote array expansions to avoid re-splitting elements - https://github.com/koalaman/shellcheck/wiki/SC2068 -# SC2086 - Double quote to prevent globbing and word splitting - https://github.com/koalaman/shellcheck/wiki/SC2086 -# SC2119 - Use 'function "$@"' if function's $1 should mean script's $1 - https://github.com/koalaman/shellcheck/wiki/SC2119 -# SC2120 - Function references arguments, but none are ever passed - https://github.com/koalaman/shellcheck/wiki/SC2120 -# SC2128 - Expanding an array without an index only gives the first element - https://github.com/koalaman/shellcheck/wiki/SC2128 -# SC2148 - Tips depend on target shell and yours is unknown. Add a shebang - https://github.com/koalaman/shellcheck/wiki/SC2148 -# SC2153 - Possible misspelling: DOKKU_ROOT may not be assigned, but DOKKU_PORT is - https://github.com/koalaman/shellcheck/wiki/SC2153 -# SC2154 - passed_docker_option is referenced but not assigned - https://github.com/koalaman/shellcheck/wiki/SC2154 -# SC2155 - Declare and assign separately to avoid masking return values - https://github.com/koalaman/shellcheck/wiki/SC2155 -# SC2162 - read without -r will mangle backslashes - https://github.com/koalaman/shellcheck/wiki/SC2162 -# SC2174 - When used with -p, -m only applies to the deepest directory - https://github.com/koalaman/shellcheck/wiki/SC2174 -# SC2179 - Use array+=("item") to append items to an array - https://github.com/koalaman/shellcheck/wiki/SC2179 -# SC2191 - The = here is literal. To assign by index, use ( [index]=value ) with no spaces. To keep as literal, quote it - https://github.com/koalaman/shellcheck/wiki/SC2191 -# SC2199 - Arrays implicitly concatenate in [[ ]]. Use a loop (or explicit * instead of @) - https://github.com/koalaman/shellcheck/wiki/SC2199 -# SC2207 - Prefer mapfile or read -a to split command output (or quote to avoid splitting) - https://github.com/koalaman/shellcheck/wiki/SC2207 -# SC2219 - Instead of 'let expr', prefer (( expr )) - https://github.com/koalaman/shellcheck/wiki/SC2219 -# SC2220 - Invalid flags are not handled. Add a *) case - https://github.com/koalaman/shellcheck/wiki/SC2220 -# SC2231 - Quote expansions in this for loop glob to prevent wordsplitting, e.g. "$dir"/*.txt - https://github.com/koalaman/shellcheck/wiki/SC2231 -# SC2235 - Use { ..; } instead of (..) to avoid subshell overhead - https://github.com/koalaman/shellcheck/wiki/SC2235 -# SC2267 - GNU xargs -i is deprecated in favor of -I{} - https://github.com/koalaman/shellcheck/wiki/SC2267 -# SC2295 - Expansions inside ${..} need to be quoted separately, otherwise they match as patterns - https://github.com/koalaman/shellcheck/wiki/SC2295 diff --git a/tests/test_deploy b/tests/test_deploy index fdc67866d..01ecbda18 100755 --- a/tests/test_deploy +++ b/tests/test_deploy @@ -13,7 +13,6 @@ REPO="test-$(basename "$APP")-$RANDOM" ssh "$REMOTE" events:on destroy_app() { - # shellcheck disable=SC2029 ssh "$REMOTE" apps:destroy "$REPO" --force ssh "$REMOTE" events:off } @@ -63,7 +62,6 @@ fi ssh "$REMOTE" events ssh "$REMOTE" apps:list -# shellcheck disable=SC2029 URL=$(ssh "$REMOTE" url "$REPO")$FORWARDED_PORT sleep 2 if (./check_deploy "$URL"); then diff --git a/tests/unit/test_helper.bash b/tests/unit/test_helper.bash index 7d39ec012..c3c242807 100644 --- a/tests/unit/test_helper.bash +++ b/tests/unit/test_helper.bash @@ -57,8 +57,6 @@ flunk() { } # ShellCheck doesn't know about $status from Bats -# shellcheck disable=SC2154 -# shellcheck disable=SC2120 assert_success() { if [[ "$status" -ne 0 ]]; then flunk "command failed with exit status $status" @@ -68,8 +66,6 @@ assert_success() { } # ShellCheck doesn't know about $status from Bats -# shellcheck disable=SC2154 -# shellcheck disable=SC2120 assert_failure() { if [[ "$status" -eq 0 ]]; then flunk "expected failed exit status" @@ -97,7 +93,6 @@ assert_not_equal() { } # ShellCheck doesn't know about $output from Bats -# shellcheck disable=SC2154 assert_output() { local expected if [[ $# -eq 0 ]]; then @@ -109,7 +104,6 @@ assert_output() { } # ShellCheck doesn't know about $output from Bats -# shellcheck disable=SC2154 assert_not_output() { local expected if [[ $# -eq 0 ]]; then @@ -121,19 +115,16 @@ assert_not_output() { } # ShellCheck doesn't know about $output from Bats -# shellcheck disable=SC2154 assert_output_exists() { [[ -n "$output" ]] || flunk "expected output, found none" } # ShellCheck doesn't know about $output from Bats -# shellcheck disable=SC2154 assert_output_not_exists() { [[ -z "$output" ]] || flunk "expected no output, found some" } # ShellCheck doesn't know about $output from Bats -# shellcheck disable=SC2154 assert_output_contains() { local input="$output" local expected="$1" @@ -147,7 +138,6 @@ assert_output_contains() { } # ShellCheck doesn't know about $lines from Bats -# shellcheck disable=SC2154 assert_line() { if [[ "$1" -ge 0 ]] 2>/dev/null; then assert_equal "$2" "${lines[$1]}" @@ -161,7 +151,6 @@ assert_line() { } # ShellCheck doesn't know about $lines from Bats -# shellcheck disable=SC2154 assert_line_count() { declare EXPECTED="$1" local num_lines="${#lines[@]}" @@ -218,7 +207,6 @@ destroy_key() { rm -f /tmp/testkey* &>/dev/null || true } -# shellcheck disable=SC2119 check_urls() { local PATTERN="$1" run /bin/bash -c "dokku urls $TEST_APP | grep -E \"${PATTERN}\"" @@ -320,7 +308,6 @@ deploy_app() { rmdir "$TMP" && cp -r "${BATS_TEST_DIRNAME}/../../tests/apps/$APP_TYPE" "$TMP" - # shellcheck disable=SC2086 [[ -n "$CUSTOM_TEMPLATE" ]] && $CUSTOM_TEMPLATE $TEST_APP $TMP/$CUSTOM_PATH pushd "$TMP" &>/dev/null || exit 1 From ef25a9b0d816c3703a710de30e5693fa3ab01708 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sat, 1 Jul 2023 03:48:45 -0400 Subject: [PATCH 07/61] refactor: standardize on shorthand for redirecting all output to /dev/null The shorthand is more prevalent in this codebase, and is something that bash supports, so we should just use the same thing everywhere. Note that we do not use shorthand redirect in Makefile as shell parsing is a bit different in Make and the shorthand redirect doesn't seem to be properly supported, causing CI errors. --- bootstrap.sh | 2 +- contrib/dokku_client.sh | 4 ++-- docs/development/plugin-creation.md | 2 +- docs/development/plugin-triggers.md | 2 +- dokku | 2 +- plugins/20_events/install | 2 +- plugins/builder-herokuish/builder-build | 2 +- plugins/builder-herokuish/post-app-rename-setup | 2 +- plugins/common/functions | 2 +- plugins/git/functions | 8 ++++---- plugins/git/internal-functions | 6 +++--- plugins/nginx-vhosts/functions | 4 ++-- plugins/scheduler-docker-local/core-post-deploy | 2 +- plugins/scheduler-docker-local/internal-functions | 6 +++--- plugins/scheduler-docker-local/scheduler-deploy | 2 +- plugins/scheduler-docker-local/scheduler-logs-failed | 2 +- plugins/scheduler-docker-local/scheduler-stop | 2 +- tests.mk | 4 ++-- tests/ci/setup.sh | 2 +- tests/unit/test_helper.bash | 2 +- 20 files changed, 30 insertions(+), 30 deletions(-) diff --git a/bootstrap.sh b/bootstrap.sh index 04e4cd8cb..d5de53341 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -28,7 +28,7 @@ ensure-environment() { echo "Preparing to install $DOKKU_TAG from $DOKKU_REPO..." fi - hostname -f >/dev/null 2>&1 || { + hostname -f &>/dev/null || { log-fail "This installation script requires that you have a hostname set for the instance. Please set a hostname for 127.0.0.1 in your /etc/hosts" } diff --git a/contrib/dokku_client.sh b/contrib/dokku_client.sh index e0b3ffd4c..590d3d0dc 100755 --- a/contrib/dokku_client.sh +++ b/contrib/dokku_client.sh @@ -49,7 +49,7 @@ fn-dokku-host() { declare DOKKU_GIT_REMOTE="$1" DOKKU_HOST="$2" if [[ -z "$DOKKU_HOST" ]]; then - if [[ -d .git ]] || git rev-parse --git-dir >/dev/null 2>&1; then + if [[ -d .git ]] || git rev-parse --git-dir &>/dev/null; then DOKKU_HOST=$(git remote -v 2>/dev/null | grep -Ei "^${DOKKU_GIT_REMOTE}\s" | head -n 1 | cut -f1 -d' ' | cut -f2 -d '@' | cut -f1 -d':' 2>/dev/null || true) fi fi @@ -109,7 +109,7 @@ main() { fi if [[ -z "$APP" ]]; then - if [[ -d .git ]] || git rev-parse --git-dir >/dev/null 2>&1; then + if [[ -d .git ]] || git rev-parse --git-dir &>/dev/null; then set +e APP=$(git remote -v 2>/dev/null | grep -Ei "^${DOKKU_GIT_REMOTE}\s" | grep -Ei "dokku@$DOKKU_REMOTE_HOST" | head -n 1 | cut -f2 -d'@' | cut -f1 -d' ' | cut -f2 -d':' 2>/dev/null) set -e diff --git a/docs/development/plugin-creation.md b/docs/development/plugin-creation.md index 6cbd9b627..edebfc649 100644 --- a/docs/development/plugin-creation.md +++ b/docs/development/plugin-creation.md @@ -103,7 +103,7 @@ if ! command -v "nginx" &>/dev/null; then fi # `which` example -if ! which nginx >/dev/null 2>&1; then +if ! which nginx &>/dev/null; then dokku_log_fail "Missing nginx, install it" fi ``` diff --git a/docs/development/plugin-triggers.md b/docs/development/plugin-triggers.md index 796dcae93..a141b115f 100644 --- a/docs/development/plugin-triggers.md +++ b/docs/development/plugin-triggers.md @@ -1715,7 +1715,7 @@ APP="$1"; IMAGE_TAG="$2"; IMAGE=$(get_app_image_name $APP $IMAGE_TAG) dokku_log_info1 "Installing GraphicsMagick..." CMD="cat > gm && \ - dpkg -s graphicsmagick >/dev/null 2>&1 || \ + dpkg -s graphicsmagick &>/dev/null || \ (apt-get update -qq && apt-get -qq -y --no-install-recommends install graphicsmagick && apt-get clean)" CID=$(docker run $DOKKU_GLOBAL_RUN_ARGS -i -a stdin $IMAGE /bin/bash -c "$CMD") diff --git a/dokku b/dokku index 7e37af297..0a2086471 100755 --- a/dokku +++ b/dokku @@ -31,7 +31,7 @@ export DOKKU_HOST_ROOT=${DOKKU_HOST_ROOT:=$DOKKU_ROOT} export DOKKU_DISTRO DOKKU_DISTRO=$( - . /etc/os-release >/dev/null 2>&1 || true + . /etc/os-release &>/dev/null || true echo "$ID" ) diff --git a/plugins/20_events/install b/plugins/20_events/install index 003a1e89d..9a352e198 100755 --- a/plugins/20_events/install +++ b/plugins/20_events/install @@ -63,7 +63,7 @@ $DOKKU_LOGS_DIR/*.log { compress delaycompress postrotate - reload rsyslog >/dev/null 2>&1 || true + reload rsyslog &>/dev/null || true endscript create 664 syslog dokku } diff --git a/plugins/builder-herokuish/builder-build b/plugins/builder-herokuish/builder-build index 7505a992d..8ebc672bf 100755 --- a/plugins/builder-herokuish/builder-build +++ b/plugins/builder-herokuish/builder-build @@ -8,7 +8,7 @@ fn-builder-herokuish-ensure-cache() { existing_cache="$("$DOCKER_BIN" volume ls --quiet --filter label=com.dokku.app-name=test --filter label=com.dokku.builder-type=herokuish)" if [[ "$existing_cache" != "cache-$APP" ]]; then - "$DOCKER_BIN" volume rm "cache-$APP" >/dev/null 2>&1 || true + "$DOCKER_BIN" volume rm "cache-$APP" &>/dev/null || true "$DOCKER_BIN" volume create "--label=org.label-schema.schema-version=1.0" "--label=org.label-schema.vendor=dokku" "--label=com.dokku.app-name=$APP" "--label=com.dokku.builder-type=herokuish" "cache-$APP" >/dev/null fi } diff --git a/plugins/builder-herokuish/post-app-rename-setup b/plugins/builder-herokuish/post-app-rename-setup index 2c96153a8..26c5a6dbb 100755 --- a/plugins/builder-herokuish/post-app-rename-setup +++ b/plugins/builder-herokuish/post-app-rename-setup @@ -12,7 +12,7 @@ trigger-builder-herokuish-post-app-clone-setup() { local OLD_CACHE_DIR="$DOKKU_ROOT/$OLD_APP/cache" local OLD_CACHE_HOST_DIR="$DOKKU_HOST_ROOT/$OLD_APP/cache" - if [[ -d "$OLD_CACHE_DIR" ]] && ! rmdir "$OLD_CACHE_DIR" >/dev/null 2>&1; then + if [[ -d "$OLD_CACHE_DIR" ]] && ! rmdir "$OLD_CACHE_DIR" &>/dev/null; then local DOCKER_RUN_LABEL_ARGS="--label=com.dokku.app-name=$NEW_APP" "$DOCKER_BIN" container run "${DOCKER_RUN_LABEL_ARGS[@]}" $DOKKU_GLOBAL_RUN_ARGS --rm --volume "$OLD_CACHE_HOST_DIR:/cache" "dokku/$OLD_APP" chmod 777 -R /cache fi diff --git a/plugins/common/functions b/plugins/common/functions index e3887fc48..f1f49bc83 100755 --- a/plugins/common/functions +++ b/plugins/common/functions @@ -480,7 +480,7 @@ copy_from_image() { fi TMP_FILE_COMMAND_OUTPUT=$(mktemp "/tmp/dokku-${DOKKU_PID}-${FUNCNAME[0]}.XXXXXX") - trap "rm -rf '$TMP_FILE_COMMAND_OUTPUT' >/dev/null 2>&1 || true" RETURN + trap "rm -rf '$TMP_FILE_COMMAND_OUTPUT' &>/dev/null || true" RETURN local CID=$("$DOCKER_BIN" container create "${DOCKER_CREATE_LABEL_ARGS[@]}" $DOKKU_GLOBAL_RUN_ARGS "$IMAGE") "$DOCKER_BIN" container cp "$CID:$SRC_FILE" "$TMP_FILE_COMMAND_OUTPUT" 2>/dev/null || true diff --git a/plugins/git/functions b/plugins/git/functions index 6773e320f..c19c8397e 100755 --- a/plugins/git/functions +++ b/plugins/git/functions @@ -20,7 +20,7 @@ git_build_app_repo() { chmod 755 "$GIT_BUILD_APP_REPO_TMP_WORK_DIR" unset GIT_DIR GIT_QUARANTINE_PATH GIT_WORK_TREE - ! plugn trigger app-exists "$APP" >/dev/null 2>&1 && plugn trigger app-maybe-create "$APP" + ! plugn trigger app-exists "$APP" &>/dev/null && plugn trigger app-maybe-create "$APP" fn-git-setup-build-dir "$APP" "$GIT_BUILD_APP_REPO_TMP_WORK_DIR" "$REV" pushd "$GIT_BUILD_APP_REPO_TMP_WORK_DIR" >/dev/null @@ -79,7 +79,7 @@ cmd-git-hook() { plugn trigger app-exists "$APP" 2>/dev/null || is_valid_app_name "$APP" DOKKU_DEPLOY_BRANCH="$(fn-git-deploy-branch "$APP")" - if ! git check-ref-format --branch "$DOKKU_DEPLOY_BRANCH" >/dev/null 2>&1; then + if ! git check-ref-format --branch "$DOKKU_DEPLOY_BRANCH" &>/dev/null; then echo $'\e[1G\e[K'"-----> WARNING: Invalid branch name '$DOKKU_DEPLOY_BRANCH' specified via deploy-branch setting." echo $'\e[1G\e[K'"-----> For more details, please see the man page for 'git-check-ref-format.'" return @@ -165,7 +165,7 @@ cmd-git-upload-pack() { APP="$(echo "$APP" | perl -pe 's/(?/dev/null 2>&1 && plugn trigger app-maybe-create "$APP" + ! plugn trigger app-exists "$APP" &>/dev/null && plugn trigger app-maybe-create "$APP" plugn trigger git-pre-pull "$APP" cat | git-upload-pack "$DOKKU_ROOT/$APP" @@ -180,7 +180,7 @@ cmd-git-glob() { plugn trigger app-exists "$APP" 2>/dev/null || is_valid_app_name "$APP" if [[ $1 == "git-receive-pack" && ! -d "$APP_PATH/refs" ]]; then - ! plugn trigger app-exists "$APP" >/dev/null 2>&1 && plugn trigger app-maybe-create "$APP" + ! plugn trigger app-exists "$APP" &>/dev/null && plugn trigger app-maybe-create "$APP" fn-git-create-hook "$APP" fi diff --git a/plugins/git/internal-functions b/plugins/git/internal-functions index 531eb66dd..b84d070a8 100755 --- a/plugins/git/internal-functions +++ b/plugins/git/internal-functions @@ -408,7 +408,7 @@ fn-git-clone() { fn-git-cmd "$APP_CLONE_ROOT" checkout -qq "$GIT_REF" else GIT_TERMINAL_PROMPT=0 suppress_output git clone -n --branch "$GIT_REF" "$GIT_REMOTE" "$APP_CLONE_ROOT" - if fn-git-cmd "$APP_CLONE_ROOT" show-ref --verify "refs/heads/$GIT_REF" >/dev/null 2>&1; then + if fn-git-cmd "$APP_CLONE_ROOT" show-ref --verify "refs/heads/$GIT_REF" &>/dev/null; then dokku_log_verbose "Detected branch, setting deploy-branch to $GIT_REF" fn-plugin-property-write "git" "$APP" "deploy-branch" "$GIT_REF" fi @@ -450,13 +450,13 @@ fn-git-fetch() { fi DOKKU_DEPLOY_BRANCH="$(fn-git-deploy-branch "$APP")" - if ! fn-git-cmd "$APP_ROOT" check-ref-format --branch "$DOKKU_DEPLOY_BRANCH" >/dev/null 2>&1; then + if ! fn-git-cmd "$APP_ROOT" check-ref-format --branch "$DOKKU_DEPLOY_BRANCH" &>/dev/null; then dokku_log_warn "Invalid branch name '$DOKKU_DEPLOY_BRANCH' specified via deploy-branch setting." dokku_log_warn "For more details, please see the man page for 'git-check-ref-format.'" return fi - fn-git-cmd "$APP_ROOT" remote rm remote >/dev/null 2>&1 || true + fn-git-cmd "$APP_ROOT" remote rm remote &>/dev/null || true fn-git-cmd "$APP_ROOT" remote add --mirror=fetch --no-tags remote "$GIT_REMOTE" if [[ -z "$GIT_REF" ]]; then GIT_TERMINAL_PROMPT=0 fn-git-cmd "$APP_ROOT" fetch --update-head-ok remote "$DOKKU_DEPLOY_BRANCH" diff --git a/plugins/nginx-vhosts/functions b/plugins/nginx-vhosts/functions index 299e84aab..1e3d2f1e2 100755 --- a/plugins/nginx-vhosts/functions +++ b/plugins/nginx-vhosts/functions @@ -98,7 +98,7 @@ validate_nginx() { fi set +e - sudo "$NGINX_LOCATION" -t >/dev/null 2>&1 + sudo "$NGINX_LOCATION" -t &>/dev/null EXIT_CODE=$? set -e if [[ "$EXIT_CODE" -eq "0" ]]; then @@ -115,7 +115,7 @@ validate_nginx() { fi set +e - sudo "$NGINX_LOCATION" -t >/dev/null 2>&1 + sudo "$NGINX_LOCATION" -t &>/dev/null EXIT_CODE=$? set -e if [[ "$EXIT_CODE" -eq "0" ]]; then diff --git a/plugins/scheduler-docker-local/core-post-deploy b/plugins/scheduler-docker-local/core-post-deploy index 8f7924ff1..bd02b2d44 100755 --- a/plugins/scheduler-docker-local/core-post-deploy +++ b/plugins/scheduler-docker-local/core-post-deploy @@ -48,7 +48,7 @@ trigger-scheduler-docker-local-core-post-deploy() { if [[ "$PREVIOUS_CONTAINER_STATUS" != "dead" ]]; then local CONTAINER_DATE_NAME="$NAME.$(date +%s)" dokku_log_verbose_quiet "Renaming container ($cid) ${NAME} to $CONTAINER_DATE_NAME" - "$DOCKER_BIN" container rename "$NAME" "$CONTAINER_DATE_NAME" >/dev/null 2>&1 || dokku_log_warn "Unable to rename container" + "$DOCKER_BIN" container rename "$NAME" "$CONTAINER_DATE_NAME" &>/dev/null || dokku_log_warn "Unable to rename container" fi done fi diff --git a/plugins/scheduler-docker-local/internal-functions b/plugins/scheduler-docker-local/internal-functions index de854b4f9..a8985eef2 100755 --- a/plugins/scheduler-docker-local/internal-functions +++ b/plugins/scheduler-docker-local/internal-functions @@ -81,7 +81,7 @@ fn-scheduler-docker-local-retire-container() { [[ $DOKKU_DOCKER_STOP_TIMEOUT ]] && DOCKER_STOP_TIME_ARG="--time=${DOKKU_DOCKER_STOP_TIMEOUT}" if [[ "$STATE" == "restarting" ]]; then - "$DOCKER_BIN" container update --restart=no "$CID" >/dev/null 2>&1 + "$DOCKER_BIN" container update --restart=no "$CID" &>/dev/null fi if [[ "$STATE" != "dead" ]] && [[ "$STATE" != "exited" ]]; then @@ -142,8 +142,8 @@ fn-scheduler-docker-local-retire-containers() { continue fi - "$DOCKER_BIN" container rm --force "$CID" >/dev/null 2>&1 || true - if "$DOCKER_BIN" container inspect "${CID}" >/dev/null 2>&1; then + "$DOCKER_BIN" container rm --force "$CID" &>/dev/null || true + if "$DOCKER_BIN" container inspect "${CID}" &>/dev/null; then dokku_log_warn "Container ${CID} still running" continue fi diff --git a/plugins/scheduler-docker-local/scheduler-deploy b/plugins/scheduler-docker-local/scheduler-deploy index 3f742a01b..0372bf139 100755 --- a/plugins/scheduler-docker-local/scheduler-deploy +++ b/plugins/scheduler-docker-local/scheduler-deploy @@ -79,7 +79,7 @@ trigger-scheduler-docker-local-scheduler-deploy() { trap '' INT HUP sleep "$DOKKU_WAIT_TO_RETIRE" for oldid in $oldids; do - if ! docker container inspect "$oldid" >/dev/null 2>&1; then + if ! docker container inspect "$oldid" &>/dev/null; then continue fi # Disable the container restart policy diff --git a/plugins/scheduler-docker-local/scheduler-logs-failed b/plugins/scheduler-docker-local/scheduler-logs-failed index ed0d45349..d3cc93346 100755 --- a/plugins/scheduler-docker-local/scheduler-logs-failed +++ b/plugins/scheduler-docker-local/scheduler-logs-failed @@ -24,7 +24,7 @@ trigger-scheduler-docker-local-scheduler-logs-failed() { while read -r LINE || [[ -n "$LINE" ]]; do CID="$(echo "$LINE" | cut -d ' ' -f1)" PREFIX="$(echo "$LINE" | cut -d ' ' -f2)" - if "$DOCKER_BIN" container inspect "${CID}" >/dev/null 2>&1; then + if "$DOCKER_BIN" container inspect "${CID}" &>/dev/null; then RUNNING_CONTAINERS+=("$CID") else DEAD_CONTAINERS+=("$CID") diff --git a/plugins/scheduler-docker-local/scheduler-stop b/plugins/scheduler-docker-local/scheduler-stop index a6f2bafd9..88dd43091 100755 --- a/plugins/scheduler-docker-local/scheduler-stop +++ b/plugins/scheduler-docker-local/scheduler-stop @@ -33,7 +33,7 @@ trigger-scheduler-docker-local-scheduler-stop() { plugn trigger scheduler-register-retired "$APP" "$CID" done - "$DOCKER_BIN" container rm --force $DOKKU_APP_CIDS >/dev/null 2>&1 || true + "$DOCKER_BIN" container rm --force $DOKKU_APP_CIDS &>/dev/null || true fi fi } diff --git a/tests.mk b/tests.mk index a1cd915d3..2b57677e5 100644 --- a/tests.mk +++ b/tests.mk @@ -13,7 +13,7 @@ else endif shellcheck: -ifneq ($(shell shellcheck --version >/dev/null 2>&1 ; echo $$?),0) +ifneq ($(shell shellcheck --version >/dev/null 2>&1; echo $$?),0) ifeq ($(SYSTEM),Darwin) brew install shellcheck else @@ -22,7 +22,7 @@ endif endif shfmt: -ifneq ($(shell shfmt --version >/dev/null 2>&1 ; echo $$?),0) +ifneq ($(shell shfmt --version >/dev/null 2>&1; echo $$?),0) ifeq ($(shfmt),Darwin) brew install shfmt else diff --git a/tests/ci/setup.sh b/tests/ci/setup.sh index 46f2a490e..31bdfbaff 100755 --- a/tests/ci/setup.sh +++ b/tests/ci/setup.sh @@ -136,7 +136,7 @@ check_container() { while [ $cnt -lt 100 ]; do echo "$(date) [count: $cnt]: waiting for dokku startup" is_up=$( - docker exec dokku ps -ef | grep "/usr/sbin/sshd -D" >/dev/null 2>&1 + docker exec dokku ps -ef | grep "/usr/sbin/sshd -D" &>/dev/null echo $? ) echo "" && docker logs dokku diff --git a/tests/unit/test_helper.bash b/tests/unit/test_helper.bash index c3c242807..657567b01 100644 --- a/tests/unit/test_helper.bash +++ b/tests/unit/test_helper.bash @@ -344,7 +344,7 @@ setup_test_tls() { local TLS_TYPE="$1" local TLS="/home/dokku/$TEST_APP/tls" - if ! dokku apps:exists "$TEST_APP" >/dev/null 2>&1; then + if ! dokku apps:exists "$TEST_APP" &>/dev/null; then create_app "$TEST_APP" fi mkdir -p "$TLS" From bbbe0e763d5ac47b5aa7b00de67005b6e0cb7ebb Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sat, 1 Jul 2023 06:57:57 -0400 Subject: [PATCH 08/61] fix: do not start proxy implementations during server restore if they weren't manually started via the :start command This change ensures users don't have unnecessary proxies starting on their servers and potentially have apps not routing correctly due to another proxy squatting on a port. Closes #5846 --- plugins/caddy-vhosts/pre-restore | 2 +- plugins/haproxy-vhosts/pre-restore | 2 +- plugins/nginx-vhosts/pre-restore | 2 +- plugins/traefik-vhosts/pre-restore | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/caddy-vhosts/pre-restore b/plugins/caddy-vhosts/pre-restore index 78f5cae59..4990a7d8e 100755 --- a/plugins/caddy-vhosts/pre-restore +++ b/plugins/caddy-vhosts/pre-restore @@ -8,7 +8,7 @@ trigger-caddy-pre-restore() { declare desc="pre-restore the caddy proxy" declare trigger="install" - if [[ "$(fn-plugin-property-get "caddy" "--global" "proxy-status")" == "stopped" ]]; then + if [[ "$(fn-plugin-property-get "caddy" "--global" "proxy-status")" != "started" ]]; then return fi diff --git a/plugins/haproxy-vhosts/pre-restore b/plugins/haproxy-vhosts/pre-restore index 8e3086210..ed637bd96 100755 --- a/plugins/haproxy-vhosts/pre-restore +++ b/plugins/haproxy-vhosts/pre-restore @@ -8,7 +8,7 @@ trigger-haproxy-pre-restore() { declare desc="pre-restore the caddy proxy" declare trigger="install" - if [[ "$(fn-plugin-property-get "haproxy" "--global" "proxy-status")" == "stopped" ]]; then + if [[ "$(fn-plugin-property-get "haproxy" "--global" "proxy-status")" != "started" ]]; then return fi diff --git a/plugins/nginx-vhosts/pre-restore b/plugins/nginx-vhosts/pre-restore index b7de63b37..40565f897 100755 --- a/plugins/nginx-vhosts/pre-restore +++ b/plugins/nginx-vhosts/pre-restore @@ -8,7 +8,7 @@ trigger-nginx-pre-restore() { declare desc="pre-restore the nginx proxy" declare trigger="install" - if [[ "$(fn-plugin-property-get "nginx" "--global" "proxy-status")" == "stopped" ]]; then + if [[ "$(fn-plugin-property-get "nginx" "--global" "proxy-status")" != "started" ]]; then return fi diff --git a/plugins/traefik-vhosts/pre-restore b/plugins/traefik-vhosts/pre-restore index 7b522d76f..698d97788 100755 --- a/plugins/traefik-vhosts/pre-restore +++ b/plugins/traefik-vhosts/pre-restore @@ -8,7 +8,7 @@ trigger-traefik-pre-restore() { declare desc="pre-restore the traefik proxy" declare trigger="install" - if [[ "$(fn-plugin-property-get "traefik" "--global" "proxy-status")" == "stopped" ]]; then + if [[ "$(fn-plugin-property-get "traefik" "--global" "proxy-status")" != "started" ]]; then return fi From f7c46a46f4e7c25e3be69b4f443f5a56b7e96dac Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sat, 1 Jul 2023 19:37:47 -0400 Subject: [PATCH 09/61] fix: mount the traefik data directory instead of the acme.json file when starting traefik Without this change, Docker - at least on arm64 - appears to mount the file as 0755 instead of 0600. We can trick docker by mounting the folder and then referencing _that_ instead. --- plugins/traefik-vhosts/templates/compose.yml.sigil | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/traefik-vhosts/templates/compose.yml.sigil b/plugins/traefik-vhosts/templates/compose.yml.sigil index 7102f374e..6bddb9938 100644 --- a/plugins/traefik-vhosts/templates/compose.yml.sigil +++ b/plugins/traefik-vhosts/templates/compose.yml.sigil @@ -21,7 +21,7 @@ services: {{ if $.TRAEFIK_LETSENCRYPT_EMAIL }} - --certificatesresolvers.leresolver.acme.caserver={{ $.TRAEFIK_LETSENCRYPT_SERVER }} - --certificatesresolvers.leresolver.acme.email={{ $.TRAEFIK_LETSENCRYPT_EMAIL }} - - --certificatesresolvers.leresolver.acme.storage=/acme.json + - --certificatesresolvers.leresolver.acme.storage=/data/acme.json - --certificatesresolvers.leresolver.acme.tlschallenge=true {{ end }} @@ -59,4 +59,4 @@ services: volumes: - "/var/run/docker.sock:/var/run/docker.sock:ro" - - "{{ $.TRAEFIK_DATA_DIR }}/traefik-acme.json:/acme.json" + - "{{ $.TRAEFIK_DATA_DIR }}:/data" From a0b84d52ef8b968c788c036087ffeb598be4b829 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 2 Jul 2023 02:03:41 -0400 Subject: [PATCH 10/61] docs: clarify that a branch can be specified when updating a plugin Installing/Updating a plugin to a particular commit object will ensure future calls to plugin:update are pinned to that object. Closes #4551 --- docs/advanced-usage/plugin-management.md | 10 ++++++++-- plugins/plugin/help-functions | 4 ++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/advanced-usage/plugin-management.md b/docs/advanced-usage/plugin-management.md index 99438774e..ea0b80b09 100644 --- a/docs/advanced-usage/plugin-management.md +++ b/docs/advanced-usage/plugin-management.md @@ -5,13 +5,13 @@ ``` plugin:disable # Disable an installed plugin (third-party only) plugin:enable # Enable a previously disabled plugin -plugin:install [--core|git-url [--committish tag|branch|commit|--name custom-plugin-name]] # Optionally download git-url (with custom tag/committish) & run install trigger for active plugins (or only core ones) +plugin:install [--core|--git-url] [--committish branch|commit|tag] [--name custom-plugin-name] # Optionally download git-url (and pin to the specified branch/commit/tag) & run install trigger for active plugins (or only core ones) plugin:installed # Checks if a plugin is installed plugin:install-dependencies [--core] # Run install-dependencies trigger for active plugins (or only core ones) plugin:list # Print active plugins plugin:trigger . # Trigger an arbitrary plugin hook plugin:uninstall # Uninstall a plugin (third-party only) -plugin:update [name [committish]] # Optionally update named plugin from git (with custom tag/committish) & run update trigger for active plugins +plugin:update [name [branch|commit|tag]] # Optionally update named plugin from git (and pin to the specified branch/commit/tag) & run update trigger for active plugins ``` ```shell @@ -165,6 +165,12 @@ An optional commit SHA-like object may be specified. dokku plugin:update postgres 2.0.0 ``` +Any future invocation of `plugin:update` will respect the previously specified SHA-like object. To follow a particular branch again, specify that branch: + +```shell +dokku plugin:update postgres main +``` + ### Uninstalling a plugin Third party plugins can be uninstalled using the `plugin:uninstall` command: diff --git a/plugins/plugin/help-functions b/plugins/plugin/help-functions index 909045612..29087e0e7 100755 --- a/plugins/plugin/help-functions +++ b/plugins/plugin/help-functions @@ -29,11 +29,11 @@ fn-help-content() { cat <, Disable an installed plugin (third-party only) plugin:enable , Enable a previously disabled plugin - plugin:install [--core|git-url [--committish tag|branch|commit|--name custom-plugin-name]], Optionally download git-url (with custom tag/committish) & run install trigger for active plugins (or only core ones) + plugin:install [--core|--git-url] [--committish branch|commit|commit] [--name custom-plugin-name], Optionally download git-url (and pin to the specified branch/commit/tag) & run install trigger for active plugins (or only core ones) plugin:install-dependencies [--core], Run install-dependencies trigger for active plugins (or only core ones) plugin:list, Print active plugins plugin:trigger , Trigger an arbitrary plugin hook plugin:uninstall , Uninstall a plugin (third-party only) - plugin:update [name [committish]], Optionally update named plugin from git (with custom tag/committish) & run update trigger for active plugins + plugin:update [name [branch|commit|tag]], Optionally update named plugin from git (and pin to the specified branch/commit/tag) & run update trigger for active plugins help_content } From 64f0f2674db9dd309916a326a8c239c54cfc5d4e Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 2 Jul 2023 01:29:01 -0400 Subject: [PATCH 11/61] refactor: move all port management code to standalone ports plugin This change makes interacting with port mappings more clear - folks might previously set the port mapping to the proxy type or vice-versa. The existing proxy:ports* commands still exist but will show a deprecation warning for a single minor release. --- docs/appendices/0.31.0-migration-guide.md | 3 +- docs/configuration/environment-variables.md | 2 +- docs/configuration/ssl.md | 2 +- docs/development/plugin-triggers.md | 6 +- docs/getting-started/troubleshooting.md | 16 +- docs/networking/port-management.md | 101 +++++++--- docs/networking/proxies/nginx.md | 2 +- docs/networking/proxy-management.md | 31 +-- plugins/ports/.gitignore | 7 + plugins/ports/Makefile | 6 + plugins/ports/functions.go | 188 ++++++++++++++++++ plugins/ports/go.mod | 23 +++ plugins/ports/go.sum | 22 ++ plugins/ports/plugin.toml | 4 + .../{proxy => ports}/proxy-configure-ports | 11 +- plugins/ports/proxy.go | 21 ++ plugins/ports/report.go | 37 ++++ plugins/ports/src/commands/commands.go | 60 ++++++ plugins/ports/src/subcommands/subcommands.go | 65 ++++++ plugins/ports/src/triggers/triggers.go | 37 ++++ plugins/ports/subcommands.go | 116 +++++++++++ plugins/ports/triggers.go | 71 +++++++ plugins/proxy/.gitignore | 2 +- plugins/proxy/Makefile | 2 +- plugins/proxy/functions.go | 183 ----------------- plugins/proxy/go.mod | 5 +- plugins/proxy/proxy.go | 18 -- plugins/proxy/report.go | 16 +- plugins/proxy/src/commands/commands.go | 5 - plugins/proxy/src/subcommands/subcommands.go | 16 +- plugins/proxy/src/triggers/triggers.go | 6 - plugins/proxy/subcommands.go | 90 --------- plugins/proxy/triggers.go | 68 ------- plugins/repo/go.mod | 2 - tests/unit/apps_2.bats | 2 +- tests/unit/caddy.bats | 2 +- tests/unit/haproxy.bats | 2 +- tests/unit/nginx-vhosts_3.bats | 6 +- tests/unit/nginx-vhosts_8.bats | 4 +- tests/unit/ports.bats | 106 ++++++++++ tests/unit/proxied-app.bats | 2 +- tests/unit/proxy.bats | 77 ------- tests/unit/traefik.bats | 2 +- 43 files changed, 894 insertions(+), 553 deletions(-) create mode 100644 plugins/ports/.gitignore create mode 100644 plugins/ports/Makefile create mode 100644 plugins/ports/functions.go create mode 100644 plugins/ports/go.mod create mode 100644 plugins/ports/go.sum create mode 100644 plugins/ports/plugin.toml rename plugins/{proxy => ports}/proxy-configure-ports (90%) create mode 100644 plugins/ports/proxy.go create mode 100644 plugins/ports/report.go create mode 100644 plugins/ports/src/commands/commands.go create mode 100644 plugins/ports/src/subcommands/subcommands.go create mode 100644 plugins/ports/src/triggers/triggers.go create mode 100644 plugins/ports/subcommands.go create mode 100644 plugins/ports/triggers.go create mode 100644 tests/unit/ports.bats diff --git a/docs/appendices/0.31.0-migration-guide.md b/docs/appendices/0.31.0-migration-guide.md index 547cbc6ce..e81819399 100644 --- a/docs/appendices/0.31.0-migration-guide.md +++ b/docs/appendices/0.31.0-migration-guide.md @@ -1,5 +1,6 @@ -# 0.30.0 Migration Guide +# 0.31.0 Migration Guide ## Changes - Herokuish build cache is now mounted from a docker volume - eg. `cache-node-js-app` - instead of the local filesystem. All existing app cache will be cleared upon upgrading past 0.29.0. +- The `proxy:ports*` commands have been replaced with the new `ports` plugin. Users will be able to use the old `proxy:ports*` commands for a single minor release, and they will be removed in the next minor release. diff --git a/docs/configuration/environment-variables.md b/docs/configuration/environment-variables.md index c4498062f..e48084557 100644 --- a/docs/configuration/environment-variables.md +++ b/docs/configuration/environment-variables.md @@ -116,7 +116,7 @@ The following config variables have special meanings and can be set in a variety | `DOKKU_PARALLEL_ARGUMENTS`. | none | `dokku config:set` | Allows passing custom arguments to parallel for `ps:*all` commands | | `DOKKU_PROXY_PORT` | automatically assigned | `/etc/environment`
`~dokku/.dokkurc`
`~dokku/.dokkurc/*`
`dokku config:set` | | | `DOKKU_PROXY_SSL_PORT` | automatically assigned | `/etc/environment`
`~dokku/.dokkurc`
`~dokku/.dokkurc/*`
`dokku config:set` | | -| `DOKKU_PROXY_PORT_MAP` | automatically assigned | `dokku proxy:ports-add`
`dokku proxy:ports-remove`, `dokku proxy:ports-clear` | | +| `DOKKU_PROXY_PORT_MAP` | automatically assigned | `dokku ports:add`
`dokku ports:remove`, `dokku ports:clear` | | | `DOKKU_SKIP_ALL_CHECKS` | none | `dokku config:set` | | | `DOKKU_SKIP_CLEANUP` | | `/etc/environment`
`~dokku/.dokkurc`
`~dokku/.dokkurc/*` | When a deploy is triggered, if this is set to a non-empty value, then old docker containers and images will not be removed. | | `DOKKU_SKIP_DEFAULT_CHECKS` | | `dokku config:set` | | diff --git a/docs/configuration/ssl.md b/docs/configuration/ssl.md index 42664a749..abb0f9f62 100644 --- a/docs/configuration/ssl.md +++ b/docs/configuration/ssl.md @@ -134,7 +134,7 @@ Certain versions of nginx have bugs that prevent [HTTP/2](https://nginx.org/en/d ### SSL Port Exposure -When your app is served from port `80` then the `/home/dokku/APP/nginx.conf` file will automatically be updated to instruct nginx to respond to ssl on port 443 as a new cert is added. If your app uses a non-standard port (perhaps you have a dockerfile deploy exposing port `99999`) you may need to manually expose an ssl port via `dokku proxy:ports-add https:443:99999`. +When your app is served from port `80` then the `/home/dokku/APP/nginx.conf` file will automatically be updated to instruct nginx to respond to ssl on port 443 as a new cert is added. If your app uses a non-standard port (perhaps you have a dockerfile deploy exposing port `99999`) you may need to manually expose an ssl port via `dokku ports:add https:443:99999`. ## Other diff --git a/docs/development/plugin-triggers.md b/docs/development/plugin-triggers.md index a141b115f..f74e6afb1 100644 --- a/docs/development/plugin-triggers.md +++ b/docs/development/plugin-triggers.md @@ -1498,8 +1498,8 @@ popd &>/dev/null ### `post-proxy-ports-update` -- Description: Allows you to run commands once the proxy port mappings for an app have been updated. It also sends the invoking command. This can be "add", "clear" or "remove". -- Invoked by: `dokku proxy:ports-add`, `dokku proxy:ports-clear`, `dokku proxy:ports-remove` +- Description: Allows you to run commands once the port mappings for an app have been updated. It also sends the invoking command. This can be "add", "clear" or "remove". +- Invoked by: `dokku ports:add`, `dokku ports:clear`, `dokku ports:remove` - Arguments: `$APP` `action name` - Example: @@ -1842,7 +1842,7 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x ### `proxy-configure-ports` -- Description: Configures the proxy port mapping +- Description: Configures the port mapping - Invoked by: `internally triggered by proxy plugins` - Arguments: `$APP` - Example: diff --git a/docs/getting-started/troubleshooting.md b/docs/getting-started/troubleshooting.md index 49f9953b1..0b876d5e1 100644 --- a/docs/getting-started/troubleshooting.md +++ b/docs/getting-started/troubleshooting.md @@ -72,12 +72,12 @@ For example, if you have an `EXPOSE` directive like so: EXPOSE 8000 ``` -The proxy port mapping will be `http:8000:8000`. +The port mapping will be `http:8000:8000`. To avoid this issue, either of the following can be done: -- Remove `EXPOSE` directive: This will require respecting the `$PORT` environment variable (automatically set by Dokku). Once that change is deployed, the port mapping should be cleared via the `dokku proxy:ports-clear $APP` command (where `$APP` is your app name). -- Update the port mapping: Updating the port mapping to redirect port `80` to your app's exposed port via `dokku proxy:ports-set $APP http:80:$EXPOSED_PORT` can also fix the issue. This will also allow certificate management and the letsencrypt plugin to work correctly. +- Remove `EXPOSE` directive: This will require respecting the `$PORT` environment variable (automatically set by Dokku). Once that change is deployed, the port mapping should be cleared via the `dokku ports:clear $APP` command (where `$APP` is your app name). +- Update the port mapping: Updating the port mapping to redirect port `80` to your app's exposed port via `dokku ports:set $APP http:80:$EXPOSED_PORT` can also fix the issue. This will also allow certificate management and the letsencrypt plugin to work correctly. See the [port management documentation](/docs/networking/port-management.md) for more information on how Dokku exposes ports for applications and how you can configure these for your app. @@ -233,16 +233,14 @@ If this solves the issue temporarily, longer term you should consider [configuri ### My application deploys properly, but won't load in browser "connection refused" -This could be a result of a bad proxy configuration (`http:5000:5000` may be incorrect). Run `dokku proxy:report myapp` to check if your app has the correct proxy configuration. It should show something like the following. +This could be a result of a bad proxy configuration (`http:5000:5000` may be incorrect). Run `dokku ports:report node-js-app` to check if your app has the correct proxy configuration. It should show something like the following. ``` -=====> myapp proxy information - Proxy enabled: true - Proxy port map: http:80:5000 https:443:5000 - Proxy type: nginx +=====> node-js-app ports information + Port map: http:80:5000 https:443:5000 ``` -Set `dokku proxy:ports-set front http:80:5000` to get proxy correctly configured for http endpoint. +Set `dokku ports:set node-js-app http:80:5000` to get proxy correctly configured for http endpoint. ### I deployed a new app but now subdomains are miss-routed diff --git a/docs/networking/port-management.md b/docs/networking/port-management.md index d8946b220..9c0be5967 100644 --- a/docs/networking/port-management.md +++ b/docs/networking/port-management.md @@ -1,37 +1,52 @@ # Port Management -> New as of 0.5.0, Enhanced in 0.6.0 +> New as of 0.31.0, replaces the previous `proxy:ports*` commands ``` -proxy:ports # List proxy port mappings for an app -proxy:ports-add :: [::...] # Add proxy port mappings to an app -proxy:ports-clear # Clear all proxy port mappings for an app -proxy:ports-remove [|::...] # Remove specific proxy port mappings from an app -proxy:ports-set :: [::...] # Set proxy port mappings for an app +ports:list # List port mappings for an app +ports:add :: [::...] # Add port mappings to an app +ports:clear # Clear all port mappings for an app +ports:remove [|::...] # Remove specific port mappings from an app +ports:set :: [::...] # Set port mappings for an app ``` -In Dokku 0.5.0, port proxying was decoupled from the `nginx-vhosts` plugin into the proxy plugin. Dokku 0.6.0 introduced the ability to map host ports to specific container ports. In the future this will allow other proxy software - such as HAProxy or Caddy - to be used in place of nginx. - ## Usage > Warning: Mapping alternative ports may conflict with the active firewall installed on your server or hosting provider. Such software includes - but is not limited to - AWS Security Groups, iptables, and UFW. Please consult the documentation for those softwares as applicable. > > Users should also avoid setting the `PORT` environment variable. Dokku will use port mappings to set this value. Overriding this manually may cause issues in application routing. -> New as of 0.6.0 - -You can now configure `host -> container` port mappings with the `proxy:ports-*` commands. This mapping is currently supported by the built-in nginx-vhosts plugin. +You can now configure `host -> container` port mappings with the `ports:*` commands. This mapping is currently supported by the built-in nginx-vhosts plugin. By default, buildpack apps and dockerfile apps **without** explicitly exposed ports (i.e. using the `EXPOSE` directive) will be configured with a listener on port `80` (and additionally a listener on 443 if ssl is enabled) that will proxy to the application container on port `5000`. Dockerfile apps **with** explicitly exposed ports will be configured with a listener on each exposed port and will proxy to that same port of the deployed application container. -> Note: This default behavior **will not** be automatically changed on subsequent pushes and must be manipulated with the `proxy:ports-*` commands detailed below. +> Note: This default behavior **will not** be automatically changed on subsequent pushes and must be manipulated with the `ports:*` commands detailed below. + +### Port Scheme + +The proxy port scheme is as follows: + +- `SCHEME:HOST_PORT:CONTAINER_PORT` + +The scheme metadata can be used by proxy implementations in order to properly handle proxying of requests. For example, the built-in `nginx-vhosts` proxy implementation supports the `http`, `https`, `grpc` and `grpcs` schemes. +For the `grpc` and `grpcs` see [nginx blog post on grpc](https://www.nginx.com/blog/nginx-1-13-10-grpc/). + +Developers of proxy implementations are encouraged to use whatever schemes make the most sense, and ignore configurations which they do not support. For instance, a `udp` proxy implementation can safely ignore `http` and `https` port mappings. + +To change the proxy implementation in use for an application, use the `proxy:set` command: + +```shell +# no validation will be performed against +# the specified proxy implementation +dokku proxy:set node-js-app nginx +``` ### Listing port mappings -To inspect the port mapping for a given application, use the `proxy:ports` command: +To inspect the port mapping for a given application, use the `ports:list` command: ```shell -dokku proxy:ports node-js-app +dokku ports:list node-js-app ``` ``` @@ -62,10 +77,10 @@ curl http://node-js-app.dokku.me:8080 curl: (7) Failed to connect to node-js-app.dokku.me port 8080: Connection refused ``` -However, we can use the `proxy:ports-add` command to add a second external port mapping - `8080` - to our application's port `5000`. +However, we can use the `ports:add` command to add a second external port mapping - `8080` - to our application's port `5000`. ```shell -dokku proxy:ports-add node-js-app http:8080:5000 +dokku ports:add node-js-app http:8080:5000 ``` ``` @@ -98,10 +113,10 @@ Hello World! ### Setting all port mappings at once -Port mappings can also be force set using the `proxy:ports-set` command. +Port mappings can also be force set using the `ports:set` command. ```shell -dokku proxy:ports-set node-js-app http:8080:5000 +dokku ports:set node-js-app http:8080:5000 ``` ``` @@ -114,21 +129,21 @@ dokku proxy:ports-set node-js-app http:8080:5000 ### Removing a port mapping -A port mapping can be removed using the `proxy:ports-remove` command if it no longer necessary: +A port mapping can be removed using the `ports:remove` command if it no longer necessary: ```shell -dokku proxy:ports-remove node-js-app http:80:5000 +dokku ports:remove node-js-app http:80:5000 ``` Ports may also be removed by specifying only the `host-port` value. This effectively acts as a wildcard and removes all mappings for that particular host port. ```shell -dokku proxy:ports-remove node-js-app http:80 +dokku ports:remove node-js-app http:80 ``` ## Port management by Deployment Method -> Warning: If you set a proxy port map but _do not have a global domain set_, Dokku will reset that map upon first deployment. +> Warning: If you set a port map but _do not have a global domain set_, Dokku will reset that map upon first deployment. ### Buildpacks @@ -156,10 +171,10 @@ The application would be exposed to the user at `node-js-app.dokku.me:1234`. If ```shell # add a port mapping to port 80 -dokku proxy:ports-add node-js-app http:80:1234 +dokku ports:add node-js-app http:80:1234 # remove the incorrect port mapping -dokku proxy:ports-remove node-js-app http:1234:1234 +dokku ports:remove node-js-app http:1234:1234 ``` #### Applications not using EXPOSE @@ -176,7 +191,7 @@ When switching between `EXPOSE` usage modes, it is important to reset your port ```shell # assuming your application is called `node-js-app` dokku config:unset --no-restart node-js-app DOKKU_DOCKERFILE_PORTS PORT -dokku proxy:ports-clear node-js-app +dokku ports:clear node-js-app ``` ### Docker Image @@ -186,7 +201,41 @@ When deploying an image, we will use `docker inspect` to extract the `ExposedPor ```shell # assuming your application is called `node-js-app` dokku config:set node-js-app DOKKU_DOCKERFILE_PORTS="1234/tcp 80/tcp" -dokku proxy:ports-clear node-js-app +dokku ports:clear node-js-app ``` All other port-related behavior is the same as when deploying via Dockerfile. + +### Displaying ports reports for an app + +You can get a report about the app's ports status using the `ports:report` command: + +```shell +dokku ports:report +``` + +``` +=====> node-js-app ports information + Port map: http:80:5000 https:443:5000 +=====> python-sample ports information + Port map: http:80:5000 +=====> ruby-sample ports information + Port map: http:80:5000 +``` + +You can run the command for a specific app also. + +```shell +dokku ports:report node-js-app +``` + +``` +=====> node-js-app ports information + Port map: http:80:5000 https:443:5000 +``` + +You can pass flags which will output only the value of the specific information you want. For example: + +```shell +dokku ports:report node-js-app --ports-map +``` diff --git a/docs/networking/proxies/nginx.md b/docs/networking/proxies/nginx.md index 2bb1ecca6..748d87f3c 100644 --- a/docs/networking/proxies/nginx.md +++ b/docs/networking/proxies/nginx.md @@ -506,7 +506,7 @@ See the [proxy documentation](/docs/networking/proxy-management.md) for more inf ### Managing Proxy Port mappings -See the [proxy documentation](/docs/networking/proxy-management.md#proxy-port-mapping) for more information on how to manage ports proxied for your app. +See the [ports documentation](/docs/networking/port-management.md) for more information on how to manage ports proxied for your app. ### Regenerating nginx config diff --git a/docs/networking/proxy-management.md b/docs/networking/proxy-management.md index a716952fb..4366acbe4 100644 --- a/docs/networking/proxy-management.md +++ b/docs/networking/proxy-management.md @@ -73,15 +73,12 @@ dokku proxy:report =====> node-js-app proxy information Proxy enabled: true Proxy type: nginx - Proxy port map: http:80:5000 https:443:5000 =====> python-sample proxy information Proxy enabled: true Proxy type: nginx - Proxy port map: http:80:5000 =====> ruby-sample proxy information Proxy enabled: true Proxy type: nginx - Proxy port map: http:80:5000 ``` You can run the command for a specific app also. @@ -94,7 +91,6 @@ dokku proxy:report node-js-app =====> node-js-app proxy information Proxy enabled: true Proxy type: nginx - Proxy port map: http:80:5000 https:443:5000 ``` You can pass flags which will output only the value of the specific information you want. For example: @@ -105,22 +101,7 @@ dokku proxy:report node-js-app --proxy-type #### Proxy Port Scheme -The proxy port scheme is as follows: - -- `SCHEME:HOST_PORT:CONTAINER_PORT` - -The scheme metadata can be used by proxy implementations in order to properly handle proxying of requests. For example, the built-in `nginx-vhosts` proxy implementation supports the `http`, `https`, `grpc` and `grpcs` schemes. -For the `grpc` and `grpcs` see [nginx blog post on grpc](https://www.nginx.com/blog/nginx-1-13-10-grpc/). - -Developers of proxy implementations are encouraged to use whatever schemes make the most sense, and ignore configurations which they do not support. For instance, a `udp` proxy implementation can safely ignore `http` and `https` port mappings. - -To change the proxy implementation in use for an application, use the `proxy:set` command: - -```shell -# no validation will be performed against -# the specified proxy implementation -dokku proxy:set node-js-app nginx -``` +See the [port scheme documentation](/docs/networking/port-management.md#port-scheme) for more information on the port mapping scheme used by dokku. ### Proxy port mapping @@ -136,7 +117,7 @@ From Dokku versions `0.5.0` until `0.11.0`, enabling or disabling an application Custom plugins names _must_ have the suffix `-vhosts` or scheduler overriding via `proxy:set` may not function as expected. -At this time, the following dokku commands are used to implement a complete proxy implementation. +At this time, the following dokku commands are used to interact with a complete proxy implementation. - `domains:add`: Adds a given domain to an app. - triggers: `post-domains-update` @@ -158,13 +139,13 @@ At this time, the following dokku commands are used to implement a complete prox - triggers: `proxy-disable` - `proxy:enable`: Enables the proxy configuration for an app. - triggers: `proxy-enable` -- `proxy:ports-add`: Adds one or more port mappings to an app +- `ports:add`: Adds one or more port mappings to an app - triggers: `post-proxy-ports-update` -- `proxy:ports-clear`: Clears out all port mappings for an app. +- `ports:clear`: Clears out all port mappings for an app. - triggers: `post-proxy-ports-update` -- `proxy:ports-remove`: Removes one or more port mappings from an app. +- `ports:remove`: Removes one or more port mappings from an app. - triggers: `post-proxy-ports-update` -- `proxy:ports-set`: Sets all port mappings for an app. +- `ports:set`: Sets all port mappings for an app. - triggers: `post-proxy-ports-update` Proxy implementations may decide to omit some functionality here, or use plugin triggers to supplement config with information from other plugins. diff --git a/plugins/ports/.gitignore b/plugins/ports/.gitignore new file mode 100644 index 000000000..291036bd4 --- /dev/null +++ b/plugins/ports/.gitignore @@ -0,0 +1,7 @@ +/commands +/subcommands/* +/triggers/* +/triggers +/install +/post-* +/report diff --git a/plugins/ports/Makefile b/plugins/ports/Makefile new file mode 100644 index 000000000..2149ff854 --- /dev/null +++ b/plugins/ports/Makefile @@ -0,0 +1,6 @@ +SUBCOMMANDS = subcommands/list subcommands/add subcommands/clear subcommands/remove subcommands/set subcommands/report +TRIGGERS = triggers/post-certs-remove triggers/post-certs-update triggers/report +BUILD = commands subcommands triggers +PLUGIN_NAME = ports + +include ../../common.mk diff --git a/plugins/ports/functions.go b/plugins/ports/functions.go new file mode 100644 index 000000000..06fdc1641 --- /dev/null +++ b/plugins/ports/functions.go @@ -0,0 +1,188 @@ +package ports + +import ( + "errors" + "fmt" + "os" + "sort" + "strconv" + "strings" + + "github.com/dokku/dokku/plugins/common" + "github.com/dokku/dokku/plugins/config" + "github.com/ryanuber/columnize" +) + +func addPortMaps(appName string, portMaps []PortMap) error { + allPortMaps := getPortMaps(appName) + allPortMaps = append(allPortMaps, portMaps...) + + return setPortMaps(appName, allPortMaps) +} + +func filterAppPortMaps(appName string, scheme string, hostPort int) []PortMap { + var filteredPortMaps []PortMap + for _, portMap := range getPortMaps(appName) { + if portMap.Scheme == scheme && portMap.HostPort == hostPort { + filteredPortMaps = append(filteredPortMaps, portMap) + } + } + + return filteredPortMaps +} + +func getPortMaps(appName string) []PortMap { + value := config.GetWithDefault(appName, "DOKKU_PROXY_PORT_MAP", "") + portMaps, _ := parsePortMapString(value) + return portMaps +} + +func inRange(value int, min int, max int) bool { + return min < value && value < max +} + +func listAppPortMaps(appName string) error { + portMaps := getPortMaps(appName) + + if len(portMaps) == 0 { + return errors.New("No port mappings configured for app") + } + + var lines []string + if os.Getenv("DOKKU_QUIET_OUTPUT") == "" { + lines = append(lines, "-----> scheme:host port:container port") + } + + for _, portMap := range portMaps { + lines = append(lines, portMap.String()) + } + + sort.Strings(lines) + common.LogInfo1Quiet(fmt.Sprintf("Port mappings for %s", appName)) + config := columnize.DefaultConfig() + config.Delim = ":" + config.Prefix = " " + config.Empty = "" + fmt.Println(columnize.Format(lines, config)) + return nil +} + +func parsePortMapString(stringPortMap string) ([]PortMap, error) { + var portMaps []PortMap + + for _, v := range strings.Split(strings.TrimSpace(stringPortMap), " ") { + parts := strings.SplitN(v, ":", 3) + if len(parts) == 1 { + hostPort, err := strconv.Atoi(v) + if err != nil { + return portMaps, fmt.Errorf("Invalid port map %s [err=%s]", v, err.Error()) + } + + if !inRange(hostPort, 0, 65536) { + return portMaps, fmt.Errorf("Invalid port map %s [hostPort=%d]", v, hostPort) + } + + portMaps = append(portMaps, PortMap{ + HostPort: hostPort, + Scheme: "__internal__", + }) + continue + } + + if len(parts) != 3 { + return portMaps, fmt.Errorf("Invalid port map %s [len=%d]", v, len(parts)) + } + + hostPort, err := strconv.Atoi(parts[1]) + if err != nil { + return portMaps, fmt.Errorf("Invalid port map %s [err=%s]", v, err.Error()) + } + + containerPort, err := strconv.Atoi(parts[2]) + if err != nil { + return portMaps, fmt.Errorf("Invalid port map %s [err=%s]", v, err.Error()) + } + + if !inRange(hostPort, 0, 65536) { + return portMaps, fmt.Errorf("Invalid port map %s [hostPort=%d]", v, hostPort) + } + + if !inRange(containerPort, 0, 65536) { + return portMaps, fmt.Errorf("Invalid port map %s [containerPort=%d]", v, containerPort) + } + + portMaps = append(portMaps, PortMap{ + ContainerPort: containerPort, + HostPort: hostPort, + Scheme: parts[0], + }) + } + + return uniquePortMaps(portMaps), nil +} + +func removePortMaps(appName string, portMaps []PortMap) error { + toRemove := map[string]bool{} + toRemoveByPort := map[int]bool{} + + for _, portMap := range portMaps { + if portMap.AllowsPersistence() { + toRemoveByPort[portMap.HostPort] = true + continue + } + toRemove[portMap.String()] = true + } + + var toSet []PortMap + for _, portMap := range getPortMaps(appName) { + if toRemove[portMap.String()] { + continue + } + + if toRemoveByPort[portMap.HostPort] { + continue + } + + toSet = append(toSet, portMap) + } + + if len(toSet) == 0 { + keys := []string{"DOKKU_PROXY_PORT_MAP"} + return config.UnsetMany(appName, keys, false) + } + + return setPortMaps(appName, toSet) +} + +func setPortMaps(appName string, portMaps []PortMap) error { + var value []string + for _, portMap := range uniquePortMaps(portMaps) { + if portMap.AllowsPersistence() { + continue + } + + value = append(value, portMap.String()) + } + + sort.Strings(value) + entries := map[string]string{ + "DOKKU_PROXY_PORT_MAP": strings.Join(value, " "), + } + return config.SetMany(appName, entries, false) +} + +func uniquePortMaps(portMaps []PortMap) []PortMap { + var unique []PortMap + existingPortMaps := map[string]bool{} + + for _, portMap := range portMaps { + if existingPortMaps[portMap.String()] { + continue + } + + existingPortMaps[portMap.String()] = true + unique = append(unique, portMap) + } + + return unique +} diff --git a/plugins/ports/go.mod b/plugins/ports/go.mod new file mode 100644 index 000000000..2ab83576b --- /dev/null +++ b/plugins/ports/go.mod @@ -0,0 +1,23 @@ +module github.com/dokku/dokku/plugins/ports + +go 1.19 + +require ( + github.com/dokku/dokku/plugins/common v0.0.0-00010101000000-000000000000 + github.com/dokku/dokku/plugins/config v0.0.0-00010101000000-000000000000 + github.com/ryanuber/columnize v2.1.2+incompatible + github.com/spf13/pflag v1.0.5 +) + +require ( + github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 // indirect + github.com/codeskyblue/go-sh v0.0.0-20190412065543-76bd3d59ff27 // indirect + github.com/joho/godotenv v1.2.0 // indirect + github.com/otiai10/copy v1.12.0 // indirect + golang.org/x/sync v0.3.0 // indirect + golang.org/x/sys v0.8.0 // indirect +) + +replace github.com/dokku/dokku/plugins/common => ../common + +replace github.com/dokku/dokku/plugins/config => ../config diff --git a/plugins/ports/go.sum b/plugins/ports/go.sum new file mode 100644 index 000000000..1a7ae7db2 --- /dev/null +++ b/plugins/ports/go.sum @@ -0,0 +1,22 @@ +github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 h1:sDMmm+q/3+BukdIpxwO365v/Rbspp2Nt5XntgQRXq8Q= +github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= +github.com/codeskyblue/go-sh v0.0.0-20190412065543-76bd3d59ff27 h1:HHUr4P/aKh4quafGxDT9LDasjGdlGkzLbfmmrlng3kA= +github.com/codeskyblue/go-sh v0.0.0-20190412065543-76bd3d59ff27/go.mod h1:VQx0hjo2oUeQkQUET7wRwradO6f+fN5jzXgB/zROxxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/joho/godotenv v1.2.0 h1:vGTvz69FzUFp+X4/bAkb0j5BoLC+9bpqTWY8mjhA9pc= +github.com/joho/godotenv v1.2.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= +github.com/otiai10/copy v1.12.0 h1:cLMgSQnXBs1eehF0Wy/FAGsgDTDmAqFR7rQylBb1nDY= +github.com/otiai10/copy v1.12.0/go.mod h1:rSaLseMUsZFFbsFGc7wCJnnkTAvdc5L6VWxPE4308Ww= +github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks= +github.com/ryanuber/columnize v2.1.2+incompatible h1:C89EOx/XBWwIXl8wm8OPJBd7kPF25UfsK2X7Ph/zCAk= +github.com/ryanuber/columnize v2.1.2+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/plugins/ports/plugin.toml b/plugins/ports/plugin.toml new file mode 100644 index 000000000..a43ba8fb5 --- /dev/null +++ b/plugins/ports/plugin.toml @@ -0,0 +1,4 @@ +[plugin] +description = "dokku core ports plugin" +version = "0.30.8" +[plugin.config] diff --git a/plugins/proxy/proxy-configure-ports b/plugins/ports/proxy-configure-ports similarity index 90% rename from plugins/proxy/proxy-configure-ports rename to plugins/ports/proxy-configure-ports index 21c94a69e..deee9b39d 100755 --- a/plugins/proxy/proxy-configure-ports +++ b/plugins/ports/proxy-configure-ports @@ -4,8 +4,8 @@ source "$PLUGIN_AVAILABLE_PATH/config/functions" set -eo pipefail [[ $DOKKU_TRACE ]] && set -x -trigger-proxy-proxy-configure-ports() { - declare desc="proxy proxy-configure-ports plugin trigger" +trigger-ports-proxy-configure-ports() { + declare desc="ports proxy-configure-ports plugin trigger" declare trigger="proxy-configure-ports" declare APP="$1" local RAW_TCP_PORTS="$(get_app_raw_tcp_ports "$APP")" @@ -19,7 +19,7 @@ trigger-proxy-proxy-configure-ports() { if [[ -z "$DOKKU_PROXY_PORT" ]] && [[ -z "$RAW_TCP_PORTS" ]]; then if [[ "$IS_APP_VHOST_ENABLED" == "false" ]]; then - dokku_log_info1 "No proxy port set, setting to random open high port" + dokku_log_info1 "No port set, setting to random open high port" local PROXY_PORT=$(get_available_port) else local PROXY_PORT=$(config_get --global DOKKU_PROXY_PORT) @@ -32,7 +32,7 @@ trigger-proxy-proxy-configure-ports() { local PROXY_SSL_PORT=$(config_get --global DOKKU_PROXY_SSL_PORT) PROXY_SSL_PORT=${PROXY_SSL_PORT:=443} if [[ -z "$RAW_TCP_PORTS" ]] && [[ "$IS_APP_VHOST_ENABLED" == "false" ]]; then - dokku_log_info1 "No proxy ssl port set, setting to random open high port" + dokku_log_info1 "No ssl port set, setting to random open high port" PROXY_SSL_PORT=$(get_available_port) fi DOKKU_QUIET_OUTPUT=1 config_set --no-restart "$APP" DOKKU_PROXY_SSL_PORT="$PROXY_SSL_PORT" @@ -61,4 +61,5 @@ trigger-proxy-proxy-configure-ports() { fi fi } -trigger-proxy-proxy-configure-ports "$@" + +trigger-ports-proxy-configure-ports "$@" diff --git a/plugins/ports/proxy.go b/plugins/ports/proxy.go new file mode 100644 index 000000000..1918a8c53 --- /dev/null +++ b/plugins/ports/proxy.go @@ -0,0 +1,21 @@ +package ports + +import ( + "fmt" +) + +// PortMap is a struct that contains a scheme:host-port:container-port mapping +type PortMap struct { + ContainerPort int + HostPort int + Scheme string +} + +func (p PortMap) String() string { + return fmt.Sprintf("%s:%d:%d", p.Scheme, p.HostPort, p.ContainerPort) +} + +// AllowsPersistence returns true if the port map is not to be persisted +func (p PortMap) AllowsPersistence() bool { + return p.Scheme == "__internal__" +} diff --git a/plugins/ports/report.go b/plugins/ports/report.go new file mode 100644 index 000000000..bd820554d --- /dev/null +++ b/plugins/ports/report.go @@ -0,0 +1,37 @@ +package ports + +import ( + "strings" + + "github.com/dokku/dokku/plugins/common" +) + +// ReportSingleApp is an internal function that displays the ports report for one or more apps +func ReportSingleApp(appName string, format string, infoFlag string) error { + if err := common.VerifyAppName(appName); err != nil { + return err + } + + flags := map[string]common.ReportFunc{ + "--ports-map": reportPortMap, + } + + flagKeys := []string{} + for flagKey := range flags { + flagKeys = append(flagKeys, flagKey) + } + + trimPrefix := false + uppercaseFirstCharacter := true + infoFlags := common.CollectReport(appName, infoFlag, flags) + return common.ReportSingleApp("ports", appName, infoFlag, infoFlags, flagKeys, format, trimPrefix, uppercaseFirstCharacter) +} + +func reportPortMap(appName string) string { + var portMaps []string + for _, portMap := range getPortMaps(appName) { + portMaps = append(portMaps, portMap.String()) + } + + return strings.Join(portMaps, " ") +} diff --git a/plugins/ports/src/commands/commands.go b/plugins/ports/src/commands/commands.go new file mode 100644 index 000000000..9231d27bf --- /dev/null +++ b/plugins/ports/src/commands/commands.go @@ -0,0 +1,60 @@ +package main + +import ( + "flag" + "fmt" + "os" + "strconv" + "strings" + + "github.com/dokku/dokku/plugins/common" +) + +const ( + helpHeader = `Usage: dokku ports[:COMMAND] + +Manage ports for an app + +Additional commands:` + + helpContent = ` + ports:list , List port mappings for app + ports:add [::...], Add port mappings to an app + ports:clear , Clear all port mappings for an app + ports:remove [|::...], Remove specific port mappings from an app + ports:set [::...], Set port mappings for an app + ports:report [] [], Displays a ports report for one or more apps +` +) + +func main() { + flag.Usage = usage + flag.Parse() + + cmd := flag.Arg(0) + switch cmd { + case "ports", "ports:help": + usage() + case "help": + command := common.NewShellCmd(fmt.Sprintf("ps -o command= %d", os.Getppid())) + command.ShowOutput = false + output, err := command.Output() + + if err == nil && strings.Contains(string(output), "--all") { + fmt.Println(helpContent) + } else { + fmt.Print("\n ports, Manage ports for an app\n") + } + default: + dokkuNotImplementExitCode, err := strconv.Atoi(os.Getenv("DOKKU_NOT_IMPLEMENTED_EXIT")) + if err != nil { + fmt.Println("failed to retrieve DOKKU_NOT_IMPLEMENTED_EXIT environment variable") + dokkuNotImplementExitCode = 10 + } + os.Exit(dokkuNotImplementExitCode) + } +} + +func usage() { + common.CommandUsage(helpHeader, helpContent) +} diff --git a/plugins/ports/src/subcommands/subcommands.go b/plugins/ports/src/subcommands/subcommands.go new file mode 100644 index 000000000..14f97ed94 --- /dev/null +++ b/plugins/ports/src/subcommands/subcommands.go @@ -0,0 +1,65 @@ +package main + +import ( + "fmt" + "os" + "strings" + + "github.com/dokku/dokku/plugins/common" + "github.com/dokku/dokku/plugins/ports" + + flag "github.com/spf13/pflag" +) + +// main entrypoint to all subcommands +func main() { + parts := strings.Split(os.Args[0], "/") + subcommand := parts[len(parts)-1] + + var err error + switch subcommand { + case "list": + args := flag.NewFlagSet("ports:list", flag.ExitOnError) + args.Parse(os.Args[2:]) + appName := args.Arg(0) + err = ports.CommandList(appName) + case "add": + args := flag.NewFlagSet("ports:add", flag.ExitOnError) + args.Parse(os.Args[2:]) + appName := args.Arg(0) + _, portMaps := common.ShiftString(args.Args()) + err = ports.CommandAdd(appName, portMaps) + case "clear": + args := flag.NewFlagSet("ports:clear", flag.ExitOnError) + args.Parse(os.Args[2:]) + appName := args.Arg(0) + err = ports.CommandClear(appName) + case "remove": + args := flag.NewFlagSet("ports:remove", flag.ExitOnError) + args.Parse(os.Args[2:]) + appName := args.Arg(0) + _, portMaps := common.ShiftString(args.Args()) + err = ports.CommandRemove(appName, portMaps) + case "set": + args := flag.NewFlagSet("ports:set", flag.ExitOnError) + args.Parse(os.Args[2:]) + appName := args.Arg(0) + _, portMaps := common.ShiftString(args.Args()) + err = ports.CommandSet(appName, portMaps) + case "report": + args := flag.NewFlagSet("ports:report", flag.ExitOnError) + format := args.String("format", "stdout", "format: [ stdout | json ]") + osArgs, infoFlag, flagErr := common.ParseReportArgs("ports", os.Args[2:]) + if flagErr == nil { + args.Parse(osArgs) + appName := args.Arg(0) + err = ports.CommandReport(appName, *format, infoFlag) + } + default: + err = fmt.Errorf("Invalid plugin subcommand call: %s", subcommand) + } + + if err != nil { + common.LogFailWithError(err) + } +} diff --git a/plugins/ports/src/triggers/triggers.go b/plugins/ports/src/triggers/triggers.go new file mode 100644 index 000000000..f159dbf57 --- /dev/null +++ b/plugins/ports/src/triggers/triggers.go @@ -0,0 +1,37 @@ +package main + +import ( + "flag" + "fmt" + "os" + "strings" + + "github.com/dokku/dokku/plugins/common" + "github.com/dokku/dokku/plugins/ports" +) + +// main entrypoint to all triggers +func main() { + parts := strings.Split(os.Args[0], "/") + trigger := parts[len(parts)-1] + flag.Parse() + + var err error + switch trigger { + case "post-certs-remove": + appName := flag.Arg(0) + err = ports.TriggerPostCertsRemove(appName) + case "post-certs-update": + appName := flag.Arg(0) + err = ports.TriggerPostCertsUpdate(appName) + case "report": + appName := flag.Arg(0) + err = ports.ReportSingleApp(appName, "", "") + default: + err = fmt.Errorf("Invalid plugin trigger call: %s", trigger) + } + + if err != nil { + common.LogFailWithError(err) + } +} diff --git a/plugins/ports/subcommands.go b/plugins/ports/subcommands.go new file mode 100644 index 000000000..70ebf9057 --- /dev/null +++ b/plugins/ports/subcommands.go @@ -0,0 +1,116 @@ +package ports + +import ( + "errors" + "strings" + + "github.com/dokku/dokku/plugins/common" + "github.com/dokku/dokku/plugins/config" +) + +// CommandList is a cmd wrapper to list port mappings for an app +func CommandList(appName string) error { + if err := common.VerifyAppName(appName); err != nil { + return err + } + + return listAppPortMaps(appName) +} + +// CommandAdd adds port mappings to an app +func CommandAdd(appName string, portMapStrings []string) error { + if err := common.VerifyAppName(appName); err != nil { + return err + } + + if len(portMapStrings) == 0 { + return errors.New("No port mapping specified") + } + + portMaps, err := parsePortMapString(strings.Join(portMapStrings, " ")) + if err != nil { + return err + } + + if err := addPortMaps(appName, portMaps); err != nil { + return err + } + + return common.PlugnTrigger("post-proxy-ports-update", []string{appName, "add"}...) +} + +// CommandClear clears all port mappings for an app +func CommandClear(appName string) error { + if err := common.VerifyAppName(appName); err != nil { + return err + } + + keys := []string{"DOKKU_PROXY_PORT_MAP"} + if err := config.UnsetMany(appName, keys, false); err != nil { + return err + } + + return common.PlugnTrigger("post-proxy-ports-update", []string{appName, "clear"}...) +} + +// CommandRemove removes specific port mappings from an app +func CommandRemove(appName string, portMapStrings []string) error { + if err := common.VerifyAppName(appName); err != nil { + return err + } + + if len(portMapStrings) == 0 { + return errors.New("No port mapping specified") + } + + portMaps, err := parsePortMapString(strings.Join(portMapStrings, " ")) + if err != nil { + return err + } + + if err := removePortMaps(appName, portMaps); err != nil { + return err + } + + return common.PlugnTrigger("post-proxy-ports-update", []string{appName, "remove"}...) +} + +// CommandSet sets port mappings for an app +func CommandSet(appName string, portMapStrings []string) error { + if err := common.VerifyAppName(appName); err != nil { + return err + } + + if len(portMapStrings) == 0 { + return errors.New("No port mapping specified") + } + + portMaps, err := parsePortMapString(strings.Join(portMapStrings, " ")) + if err != nil { + return err + } + + if err := setPortMaps(appName, portMaps); err != nil { + return err + } + + return common.PlugnTrigger("post-proxy-ports-update", []string{appName, "set"}...) +} + +// CommandReport displays a ports report for one or more apps +func CommandReport(appName string, format string, infoFlag string) error { + if len(appName) == 0 { + apps, err := common.DokkuApps() + if err != nil { + return err + } + for _, appName := range apps { + if err := ReportSingleApp(appName, format, infoFlag); err != nil { + return err + } + } + return nil + } + + return ReportSingleApp(appName, format, infoFlag) +} diff --git a/plugins/ports/triggers.go b/plugins/ports/triggers.go new file mode 100644 index 000000000..aa209200a --- /dev/null +++ b/plugins/ports/triggers.go @@ -0,0 +1,71 @@ +package ports + +import ( + "github.com/dokku/dokku/plugins/config" +) + +// TriggerPostCertsRemove unsets port config vars after SSL cert is added +func TriggerPostCertsRemove(appName string) error { + keys := []string{"DOKKU_PROXY_SSL_PORT"} + if err := config.UnsetMany(appName, keys, false); err != nil { + return err + } + + return removePortMaps(appName, filterAppPortMaps(appName, "https", 443)) +} + +// TriggerPostCertsUpdate sets port config vars after SSL cert is added +func TriggerPostCertsUpdate(appName string) error { + port := config.GetWithDefault(appName, "DOKKU_PROXY_PORT", "") + sslPort := config.GetWithDefault(appName, "DOKKU_PROXY_SSL_PORT", "") + portMaps := getPortMaps(appName) + + toUnset := []string{} + if port == "80" { + toUnset = append(toUnset, "DOKKU_PROXY_PORT") + } + if sslPort == "443" { + toUnset = append(toUnset, "DOKKU_PROXY_SSL_PORT") + } + + if len(toUnset) > 0 { + if err := config.UnsetMany(appName, toUnset, false); err != nil { + return err + } + } + + var http80Ports []PortMap + for _, portMap := range portMaps { + if portMap.Scheme == "http" && portMap.HostPort == 80 { + http80Ports = append(http80Ports, portMap) + } + } + + if len(http80Ports) > 0 { + var https443Ports []PortMap + for _, portMap := range portMaps { + if portMap.Scheme == "https" && portMap.HostPort == 443 { + https443Ports = append(https443Ports, portMap) + } + } + + if err := removePortMaps(appName, https443Ports); err != nil { + return err + } + + var toAdd []PortMap + for _, portMap := range http80Ports { + toAdd = append(toAdd, PortMap{ + Scheme: "https", + HostPort: 443, + ContainerPort: portMap.ContainerPort, + }) + } + + if err := addPortMaps(appName, toAdd); err != nil { + return err + } + } + + return nil +} diff --git a/plugins/proxy/.gitignore b/plugins/proxy/.gitignore index 3b20f2e1c..7d9f06e65 100644 --- a/plugins/proxy/.gitignore +++ b/plugins/proxy/.gitignore @@ -6,4 +6,4 @@ /install /post-* /report -!/proxy-configure-ports + diff --git a/plugins/proxy/Makefile b/plugins/proxy/Makefile index 21cc25c91..6b127937f 100644 --- a/plugins/proxy/Makefile +++ b/plugins/proxy/Makefile @@ -1,5 +1,5 @@ SUBCOMMANDS = subcommands/build-config subcommands/clear-config subcommands/disable subcommands/enable subcommands/ports subcommands/ports-add subcommands/ports-clear subcommands/ports-remove subcommands/ports-set subcommands/report subcommands/set -TRIGGERS = triggers/proxy-is-enabled triggers/proxy-type triggers/post-certs-remove triggers/post-certs-update triggers/report +TRIGGERS = triggers/proxy-is-enabled triggers/proxy-type triggers/report BUILD = commands subcommands triggers PLUGIN_NAME = proxy diff --git a/plugins/proxy/functions.go b/plugins/proxy/functions.go index bc12f32a9..4b5467f50 100644 --- a/plugins/proxy/functions.go +++ b/plugins/proxy/functions.go @@ -1,192 +1,9 @@ package proxy import ( - "errors" - "fmt" - "os" - "sort" - "strconv" - "strings" - - "github.com/dokku/dokku/plugins/common" "github.com/dokku/dokku/plugins/config" - "github.com/ryanuber/columnize" ) -func addProxyPorts(appName string, proxyPortMap []PortMap) error { - allPortMaps := getProxyPortMap(appName) - allPortMaps = append(allPortMaps, proxyPortMap...) - - return setProxyPorts(appName, allPortMaps) -} - -func filterAppProxyPorts(appName string, scheme string, hostPort int) []PortMap { - var filteredProxyMaps []PortMap - for _, portMap := range getProxyPortMap(appName) { - if portMap.Scheme == scheme && portMap.HostPort == hostPort { - filteredProxyMaps = append(filteredProxyMaps, portMap) - } - } - - return filteredProxyMaps -} - func getAppProxyType(appName string) string { return config.GetWithDefault(appName, "DOKKU_APP_PROXY_TYPE", "nginx") } - -func getProxyPortMap(appName string) []PortMap { - value := config.GetWithDefault(appName, "DOKKU_PROXY_PORT_MAP", "") - portMaps, _ := parseProxyPortMapString(value) - return portMaps -} - -func inRange(value int, min int, max int) bool { - return min < value && value < max -} - -func listAppProxyPorts(appName string) error { - proxyPortMap := getProxyPortMap(appName) - - if len(proxyPortMap) == 0 { - return errors.New("No port mappings configured for app") - } - - var lines []string - if os.Getenv("DOKKU_QUIET_OUTPUT") == "" { - lines = append(lines, "-----> scheme:host port:container port") - } - - for _, portMap := range proxyPortMap { - lines = append(lines, portMap.String()) - } - - sort.Strings(lines) - common.LogInfo1Quiet(fmt.Sprintf("Port mappings for %s", appName)) - config := columnize.DefaultConfig() - config.Delim = ":" - config.Prefix = " " - config.Empty = "" - fmt.Println(columnize.Format(lines, config)) - return nil -} - -func parseProxyPortMapString(stringPortMap string) ([]PortMap, error) { - var proxyPortMap []PortMap - - for _, v := range strings.Split(strings.TrimSpace(stringPortMap), " ") { - parts := strings.SplitN(v, ":", 3) - if len(parts) == 1 { - hostPort, err := strconv.Atoi(v) - if err != nil { - return proxyPortMap, fmt.Errorf("Invalid port map %s [err=%s]", v, err.Error()) - } - - if !inRange(hostPort, 0, 65536) { - return proxyPortMap, fmt.Errorf("Invalid port map %s [hostPort=%d]", v, hostPort) - } - - proxyPortMap = append(proxyPortMap, PortMap{ - HostPort: hostPort, - Scheme: "__internal__", - }) - continue - } - - if len(parts) != 3 { - return proxyPortMap, fmt.Errorf("Invalid port map %s [len=%d]", v, len(parts)) - } - - hostPort, err := strconv.Atoi(parts[1]) - if err != nil { - return proxyPortMap, fmt.Errorf("Invalid port map %s [err=%s]", v, err.Error()) - } - - containerPort, err := strconv.Atoi(parts[2]) - if err != nil { - return proxyPortMap, fmt.Errorf("Invalid port map %s [err=%s]", v, err.Error()) - } - - if !inRange(hostPort, 0, 65536) { - return proxyPortMap, fmt.Errorf("Invalid port map %s [hostPort=%d]", v, hostPort) - } - - if !inRange(containerPort, 0, 65536) { - return proxyPortMap, fmt.Errorf("Invalid port map %s [containerPort=%d]", v, containerPort) - } - - proxyPortMap = append(proxyPortMap, PortMap{ - ContainerPort: containerPort, - HostPort: hostPort, - Scheme: parts[0], - }) - } - - return uniqueProxyPortMap(proxyPortMap), nil -} - -func removeProxyPorts(appName string, proxyPortMap []PortMap) error { - toRemove := map[string]bool{} - toRemoveByPort := map[int]bool{} - - for _, portMap := range proxyPortMap { - if portMap.AllowsPersistence() { - toRemoveByPort[portMap.HostPort] = true - continue - } - toRemove[portMap.String()] = true - } - - var toSet []PortMap - for _, portMap := range getProxyPortMap(appName) { - if toRemove[portMap.String()] { - continue - } - - if toRemoveByPort[portMap.HostPort] { - continue - } - - toSet = append(toSet, portMap) - } - - if len(toSet) == 0 { - keys := []string{"DOKKU_PROXY_PORT_MAP"} - return config.UnsetMany(appName, keys, false) - } - - return setProxyPorts(appName, toSet) -} - -func setProxyPorts(appName string, proxyPortMap []PortMap) error { - var value []string - for _, portMap := range uniqueProxyPortMap(proxyPortMap) { - if portMap.AllowsPersistence() { - continue - } - - value = append(value, portMap.String()) - } - - sort.Strings(value) - entries := map[string]string{ - "DOKKU_PROXY_PORT_MAP": strings.Join(value, " "), - } - return config.SetMany(appName, entries, false) -} - -func uniqueProxyPortMap(proxyPortMap []PortMap) []PortMap { - var unique []PortMap - existingPortMaps := map[string]bool{} - - for _, portMap := range proxyPortMap { - if existingPortMaps[portMap.String()] { - continue - } - - existingPortMaps[portMap.String()] = true - unique = append(unique, portMap) - } - - return unique -} diff --git a/plugins/proxy/go.mod b/plugins/proxy/go.mod index 5da99b906..46c474818 100644 --- a/plugins/proxy/go.mod +++ b/plugins/proxy/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( github.com/dokku/dokku/plugins/common v0.0.0-00010101000000-000000000000 github.com/dokku/dokku/plugins/config v0.0.0-00010101000000-000000000000 - github.com/ryanuber/columnize v2.1.2+incompatible + github.com/dokku/dokku/plugins/ports v0.0.0-00010101000000-000000000000 github.com/spf13/pflag v1.0.5 ) @@ -14,6 +14,7 @@ require ( github.com/codeskyblue/go-sh v0.0.0-20190412065543-76bd3d59ff27 // indirect github.com/joho/godotenv v1.2.0 // indirect github.com/otiai10/copy v1.12.0 // indirect + github.com/ryanuber/columnize v2.1.2+incompatible // indirect golang.org/x/sync v0.3.0 // indirect golang.org/x/sys v0.10.0 // indirect ) @@ -21,3 +22,5 @@ require ( replace github.com/dokku/dokku/plugins/common => ../common replace github.com/dokku/dokku/plugins/config => ../config + +replace github.com/dokku/dokku/plugins/ports => ../ports diff --git a/plugins/proxy/proxy.go b/plugins/proxy/proxy.go index ec87bf48c..26cfee2ff 100644 --- a/plugins/proxy/proxy.go +++ b/plugins/proxy/proxy.go @@ -1,8 +1,6 @@ package proxy import ( - "fmt" - "github.com/dokku/dokku/plugins/common" "github.com/dokku/dokku/plugins/config" ) @@ -11,22 +9,6 @@ import ( // and defaults to -1 (false) const RunInSerial = 0 -// PortMap is a struct that contains a scheme:host-port:container-port mapping -type PortMap struct { - ContainerPort int - HostPort int - Scheme string -} - -func (p PortMap) String() string { - return fmt.Sprintf("%s:%d:%d", p.Scheme, p.HostPort, p.ContainerPort) -} - -// AllowsPersistence returns true if the port map is not to be persisted -func (p PortMap) AllowsPersistence() bool { - return p.Scheme == "__internal__" -} - // BuildConfig rebuilds the proxy config for the specified app func BuildConfig(appName string) error { return common.PlugnTrigger("proxy-build-config", []string{appName}...) diff --git a/plugins/proxy/report.go b/plugins/proxy/report.go index bf4c5c917..5db7792a2 100644 --- a/plugins/proxy/report.go +++ b/plugins/proxy/report.go @@ -1,8 +1,6 @@ package proxy import ( - "strings" - "github.com/dokku/dokku/plugins/common" ) @@ -13,9 +11,8 @@ func ReportSingleApp(appName string, format string, infoFlag string) error { } flags := map[string]common.ReportFunc{ - "--proxy-enabled": reportEnabled, - "--proxy-type": reportType, - "--proxy-port-map": reportPortMap, + "--proxy-enabled": reportEnabled, + "--proxy-type": reportType, } flagKeys := []string{} @@ -41,12 +38,3 @@ func reportEnabled(appName string) string { func reportType(appName string) string { return getAppProxyType(appName) } - -func reportPortMap(appName string) string { - var proxyPortMap []string - for _, portMap := range getProxyPortMap(appName) { - proxyPortMap = append(proxyPortMap, portMap.String()) - } - - return strings.Join(proxyPortMap, " ") -} diff --git a/plugins/proxy/src/commands/commands.go b/plugins/proxy/src/commands/commands.go index e2f6c016a..129cc2820 100644 --- a/plugins/proxy/src/commands/commands.go +++ b/plugins/proxy/src/commands/commands.go @@ -22,11 +22,6 @@ Additional commands:` proxy:clear-config [--all|], Clears config for a given app proxy:disable , Disable proxy for app proxy:enable , Enable proxy for app - proxy:ports , List proxy port mappings for app - proxy:ports-add [::...], Add proxy port mappings to an app - proxy:ports-clear , Clear all proxy port mappings for an app - proxy:ports-remove [|::...], Remove specific proxy port mappings from an app - proxy:ports-set [::...], Set proxy port mappings for an app proxy:report [] [], Displays a proxy report for one or more apps proxy:set , Set proxy type for app ` diff --git a/plugins/proxy/src/subcommands/subcommands.go b/plugins/proxy/src/subcommands/subcommands.go index c25db395c..0dd972857 100644 --- a/plugins/proxy/src/subcommands/subcommands.go +++ b/plugins/proxy/src/subcommands/subcommands.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/dokku/dokku/plugins/common" + "github.com/dokku/dokku/plugins/ports" "github.com/dokku/dokku/plugins/proxy" flag "github.com/spf13/pflag" @@ -49,30 +50,35 @@ func main() { args := flag.NewFlagSet("proxy:ports", flag.ExitOnError) args.Parse(os.Args[2:]) appName := args.Arg(0) - err = proxy.CommandPorts(appName) + common.LogWarn("Deprecated: Use 'ports:list' instead") + err = ports.CommandList(appName) case "ports-add": args := flag.NewFlagSet("proxy:ports-add", flag.ExitOnError) args.Parse(os.Args[2:]) appName := args.Arg(0) _, portMaps := common.ShiftString(args.Args()) - err = proxy.CommandPortsAdd(appName, portMaps) + common.LogWarn("Deprecated: Use 'ports:add' instead") + err = ports.CommandAdd(appName, portMaps) case "ports-clear": args := flag.NewFlagSet("proxy:ports-clear", flag.ExitOnError) args.Parse(os.Args[2:]) appName := args.Arg(0) - err = proxy.CommandPortsClear(appName) + common.LogWarn("Deprecated: Use 'ports:clear' instead") + err = ports.CommandClear(appName) case "ports-remove": args := flag.NewFlagSet("proxy:ports-remove", flag.ExitOnError) args.Parse(os.Args[2:]) appName := args.Arg(0) _, portMaps := common.ShiftString(args.Args()) - err = proxy.CommandPortsRemove(appName, portMaps) + common.LogWarn("Deprecated: Use 'ports:remove' instead") + err = ports.CommandRemove(appName, portMaps) case "ports-set": args := flag.NewFlagSet("proxy:ports-set", flag.ExitOnError) args.Parse(os.Args[2:]) appName := args.Arg(0) _, portMaps := common.ShiftString(args.Args()) - err = proxy.CommandPortsSet(appName, portMaps) + common.LogWarn("Deprecated: Use 'ports:set' instead") + err = ports.CommandSet(appName, portMaps) case "report": args := flag.NewFlagSet("proxy:report", flag.ExitOnError) format := args.String("format", "stdout", "format: [ stdout | json ]") diff --git a/plugins/proxy/src/triggers/triggers.go b/plugins/proxy/src/triggers/triggers.go index 11c5bd277..713e1976d 100644 --- a/plugins/proxy/src/triggers/triggers.go +++ b/plugins/proxy/src/triggers/triggers.go @@ -24,12 +24,6 @@ func main() { case "proxy-type": appName := flag.Arg(0) err = proxy.TriggerProxyType(appName) - case "post-certs-remove": - appName := flag.Arg(0) - err = proxy.TriggerPostCertsRemove(appName) - case "post-certs-update": - appName := flag.Arg(0) - err = proxy.TriggerPostCertsUpdate(appName) case "report": appName := flag.Arg(0) err = proxy.ReportSingleApp(appName, "", "") diff --git a/plugins/proxy/subcommands.go b/plugins/proxy/subcommands.go index 1dcb66558..58a57a870 100644 --- a/plugins/proxy/subcommands.go +++ b/plugins/proxy/subcommands.go @@ -2,7 +2,6 @@ package proxy import ( "errors" - "strings" "github.com/dokku/dokku/plugins/common" "github.com/dokku/dokku/plugins/config" @@ -60,95 +59,6 @@ func CommandEnable(appName string, allApps bool, parallelCount int) error { return Enable(appName) } -// CommandPorts is a cmd wrapper to list proxy port mappings for an app -func CommandPorts(appName string) error { - if err := common.VerifyAppName(appName); err != nil { - return err - } - - return listAppProxyPorts(appName) -} - -// CommandPortsAdd adds proxy port mappings to an app -func CommandPortsAdd(appName string, portMaps []string) error { - if err := common.VerifyAppName(appName); err != nil { - return err - } - - if len(portMaps) == 0 { - return errors.New("No port mapping specified") - } - - proxyPortMap, err := parseProxyPortMapString(strings.Join(portMaps, " ")) - if err != nil { - return err - } - - if err := addProxyPorts(appName, proxyPortMap); err != nil { - return err - } - - return common.PlugnTrigger("post-proxy-ports-update", []string{appName, "add"}...) -} - -// CommandPortsClear clears all proxy port mappings for an app -func CommandPortsClear(appName string) error { - if err := common.VerifyAppName(appName); err != nil { - return err - } - - keys := []string{"DOKKU_PROXY_PORT_MAP"} - if err := config.UnsetMany(appName, keys, false); err != nil { - return err - } - - return common.PlugnTrigger("post-proxy-ports-update", []string{appName, "clear"}...) -} - -// CommandPortsRemove removes specific proxy port mappings from an app -func CommandPortsRemove(appName string, portMaps []string) error { - if err := common.VerifyAppName(appName); err != nil { - return err - } - - if len(portMaps) == 0 { - return errors.New("No port mapping specified") - } - - proxyPortMap, err := parseProxyPortMapString(strings.Join(portMaps, " ")) - if err != nil { - return err - } - - if err := removeProxyPorts(appName, proxyPortMap); err != nil { - return err - } - - return common.PlugnTrigger("post-proxy-ports-update", []string{appName, "remove"}...) -} - -// CommandPortsSet sets proxy port mappings for an app -func CommandPortsSet(appName string, portMaps []string) error { - if err := common.VerifyAppName(appName); err != nil { - return err - } - - if len(portMaps) == 0 { - return errors.New("No port mapping specified") - } - - proxyPortMap, err := parseProxyPortMapString(strings.Join(portMaps, " ")) - if err != nil { - return err - } - - if err := setProxyPorts(appName, proxyPortMap); err != nil { - return err - } - - return common.PlugnTrigger("post-proxy-ports-update", []string{appName, "set"}...) -} - // CommandReport displays a proxy report for one or more apps func CommandReport(appName string, format string, infoFlag string) error { if len(appName) == 0 { diff --git a/plugins/proxy/triggers.go b/plugins/proxy/triggers.go index 626f95a30..49c0fef06 100644 --- a/plugins/proxy/triggers.go +++ b/plugins/proxy/triggers.go @@ -2,8 +2,6 @@ package proxy import ( "fmt" - - "github.com/dokku/dokku/plugins/config" ) // TriggerProxyIsEnabled prints true or false depending on whether the proxy is enabled @@ -24,69 +22,3 @@ func TriggerProxyType(appName string) error { return nil } - -// TriggerPostCertsRemove unsets port config vars after SSL cert is added -func TriggerPostCertsRemove(appName string) error { - keys := []string{"DOKKU_PROXY_SSL_PORT"} - if err := config.UnsetMany(appName, keys, false); err != nil { - return err - } - - return removeProxyPorts(appName, filterAppProxyPorts(appName, "https", 443)) -} - -// TriggerPostCertsUpdate sets port config vars after SSL cert is added -func TriggerPostCertsUpdate(appName string) error { - port := config.GetWithDefault(appName, "DOKKU_PROXY_PORT", "") - sslPort := config.GetWithDefault(appName, "DOKKU_PROXY_SSL_PORT", "") - proxyPortMap := getProxyPortMap(appName) - - toUnset := []string{} - if port == "80" { - toUnset = append(toUnset, "DOKKU_PROXY_PORT") - } - if sslPort == "443" { - toUnset = append(toUnset, "DOKKU_PROXY_SSL_PORT") - } - - if len(toUnset) > 0 { - if err := config.UnsetMany(appName, toUnset, false); err != nil { - return err - } - } - - var http80Ports []PortMap - for _, portMap := range proxyPortMap { - if portMap.Scheme == "http" && portMap.HostPort == 80 { - http80Ports = append(http80Ports, portMap) - } - } - - if len(http80Ports) > 0 { - var https443Ports []PortMap - for _, portMap := range proxyPortMap { - if portMap.Scheme == "https" && portMap.HostPort == 443 { - https443Ports = append(https443Ports, portMap) - } - } - - if err := removeProxyPorts(appName, https443Ports); err != nil { - return err - } - - var toAdd []PortMap - for _, portMap := range http80Ports { - toAdd = append(toAdd, PortMap{ - Scheme: "https", - HostPort: 443, - ContainerPort: portMap.ContainerPort, - }) - } - - if err := addProxyPorts(appName, toAdd); err != nil { - return err - } - } - - return nil -} diff --git a/plugins/repo/go.mod b/plugins/repo/go.mod index 952593012..bafa0d555 100644 --- a/plugins/repo/go.mod +++ b/plugins/repo/go.mod @@ -4,14 +4,12 @@ go 1.19 require ( github.com/dokku/dokku/plugins/common v0.0.0-00010101000000-000000000000 - github.com/dokku/dokku/plugins/config v0.0.0-00010101000000-000000000000 github.com/spf13/pflag v1.0.5 ) require ( github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 // indirect github.com/codeskyblue/go-sh v0.0.0-20190412065543-76bd3d59ff27 // indirect - github.com/joho/godotenv v1.2.0 // indirect github.com/otiai10/copy v1.12.0 // indirect github.com/ryanuber/columnize v2.1.2+incompatible // indirect golang.org/x/sync v0.3.0 // indirect diff --git a/tests/unit/apps_2.bats b/tests/unit/apps_2.bats index 2afb61fa8..ce91b5e78 100644 --- a/tests/unit/apps_2.bats +++ b/tests/unit/apps_2.bats @@ -114,7 +114,7 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "dokku --quiet proxy:ports app-without-ssl | xargs" + run /bin/bash -c "dokku --quiet ports:list app-without-ssl | xargs" echo "output: $output" echo "status: $status" assert_output "http 80 5000" diff --git a/tests/unit/caddy.bats b/tests/unit/caddy.bats index 2362ababd..7a3c38651 100644 --- a/tests/unit/caddy.bats +++ b/tests/unit/caddy.bats @@ -137,7 +137,7 @@ teardown() { assert_success assert_output "$TEST_APP.dokku.me" - run /bin/bash -c "dokku proxy:report $TEST_APP --proxy-port-map" + run /bin/bash -c "dokku ports:report $TEST_APP --ports-map" echo "output: $output" echo "status: $status" assert_output "http:80:5000" diff --git a/tests/unit/haproxy.bats b/tests/unit/haproxy.bats index cf74e6f67..faa757bf1 100644 --- a/tests/unit/haproxy.bats +++ b/tests/unit/haproxy.bats @@ -154,7 +154,7 @@ teardown() { assert_success assert_output "true" - run /bin/bash -c "dokku proxy:report $TEST_APP --proxy-port-map" + run /bin/bash -c "dokku ports:report $TEST_APP --ports-map" echo "output: $output" echo "status: $status" assert_output "http:80:5000" diff --git a/tests/unit/nginx-vhosts_3.bats b/tests/unit/nginx-vhosts_3.bats index babc690c5..0a7ad5fa7 100644 --- a/tests/unit/nginx-vhosts_3.bats +++ b/tests/unit/nginx-vhosts_3.bats @@ -18,14 +18,14 @@ teardown() { @test "(nginx-vhosts) grpc endpoint" { deploy_app gogrpc - dokku proxy:ports-add "$TEST_APP" "grpc:80:50051" + dokku ports:add "$TEST_APP" "grpc:80:50051" run /bin/bash -c "docker run --rm ${TEST_APP}-docker-image /go/bin/greeter_client -address ${TEST_APP}.dokku.me:80 -name grpc" assert_output "Greeting: Hello grpc" } @test "(nginx-vhosts) grpc endpoint on a port other than 80" { deploy_app gogrpc - dokku proxy:ports-add "$TEST_APP" "grpc:8080:50051" + dokku ports:add "$TEST_APP" "grpc:8080:50051" run /bin/bash -c "docker run --rm ${TEST_APP}-docker-image /go/bin/greeter_client -address ${TEST_APP}.dokku.me:8080 -name grpc8080" assert_output "Greeting: Hello grpc8080" } @@ -33,7 +33,7 @@ teardown() { @test "(nginx-vhosts) grpcs endpoint" { setup_test_tls deploy_app gogrpc - dokku proxy:ports-add "$TEST_APP" "grpcs:443:50051" + dokku ports:add "$TEST_APP" "grpcs:443:50051" run /bin/bash -c "docker run --rm ${TEST_APP}-docker-image /go/bin/greeter_client -address ${TEST_APP}.dokku.me:443 -name grpcs -tls" assert_output "Greeting: Hello grpcs" } diff --git a/tests/unit/nginx-vhosts_8.bats b/tests/unit/nginx-vhosts_8.bats index 248296737..d0b02ae4c 100644 --- a/tests/unit/nginx-vhosts_8.bats +++ b/tests/unit/nginx-vhosts_8.bats @@ -125,7 +125,7 @@ teardown() { assert_output_contains "Ignoring detected https port mapping without an accompanying ssl certificate" 0 teardown_test_tls - run /bin/bash -c "dokku proxy:report $TEST_APP --proxy-port-map" + run /bin/bash -c "dokku ports:report $TEST_APP --ports-map" echo "output: $output" echo "status: $status" assert_output "http:80:5000 https:443:5000" @@ -135,7 +135,7 @@ teardown() { echo "status: $status" assert_output_contains "Ignoring detected https port mapping without an accompanying ssl certificate" 1 - run /bin/bash -c "dokku proxy:report $TEST_APP --proxy-port-map" + run /bin/bash -c "dokku ports:report $TEST_APP --ports-map" echo "output: $output" echo "status: $status" assert_output "http:80:5000 https:443:5000" diff --git a/tests/unit/ports.bats b/tests/unit/ports.bats new file mode 100644 index 000000000..2bef9aa78 --- /dev/null +++ b/tests/unit/ports.bats @@ -0,0 +1,106 @@ +#!/usr/bin/env bats + +load test_helper + +setup() { + global_setup + [[ -f "$DOKKU_ROOT/VHOST" ]] && cp -fp "$DOKKU_ROOT/VHOST" "$DOKKU_ROOT/VHOST.bak" + create_app +} + +teardown() { + destroy_app 0 $TEST_APP + [[ -f "$DOKKU_ROOT/VHOST.bak" ]] && mv "$DOKKU_ROOT/VHOST.bak" "$DOKKU_ROOT/VHOST" && chown dokku:dokku "$DOKKU_ROOT/VHOST" + global_teardown +} + +@test "(ports) ports:help" { + run /bin/bash -c "dokku ports" + echo "output: $output" + echo "status: $status" + assert_output_contains "Manage ports for an app" + help_output="$output" + + run /bin/bash -c "dokku ports:help" + echo "output: $output" + echo "status: $status" + assert_output_contains "Manage ports for an app" + assert_output "$help_output" +} + +@test "(ports) ports subcommands (list/add/set/remove/clear)" { + run /bin/bash -c "dokku ports:set $TEST_APP http:1234:5001" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku --quiet ports:list $TEST_APP | xargs" + echo "output: $output" + echo "status: $status" + assert_output "http 1234 5001" + + run /bin/bash -c "dokku ports:add $TEST_APP http:8080:5002 https:8443:5003" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku --quiet ports:list $TEST_APP | xargs" + echo "output: $output" + echo "status: $status" + assert_output "http 1234 5001 http 8080 5002 https 8443 5003" + + run /bin/bash -c "dokku ports:set $TEST_APP http:8080:5000 https:8443:5000 http:1234:5001" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku --quiet ports:list $TEST_APP | xargs" + echo "output: $output" + echo "status: $status" + assert_output "http 1234 5001 http 8080 5000 https 8443 5000" + + run /bin/bash -c "dokku ports:remove $TEST_APP 8080" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku --quiet ports:list $TEST_APP | xargs" + echo "output: $output" + echo "status: $status" + assert_output "http 1234 5001 https 8443 5000" + + run /bin/bash -c "dokku ports:remove $TEST_APP http:1234:5001" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku --quiet ports:list $TEST_APP | xargs" + echo "output: $output" + echo "status: $status" + assert_output "https 8443 5000" + + run /bin/bash -c "dokku ports:clear $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku --quiet ports:list $TEST_APP | xargs" + echo "output: $output" + echo "status: $status" + assert_output "http 80 5000" +} + +@test "(ports) ports:add (post-deploy add)" { + deploy_app + run /bin/bash -c "dokku ports:add $TEST_APP http:8080:5000 http:8081:5000" + echo "output: $output" + echo "status: $status" + assert_success + + URLS="$(dokku --quiet urls "$TEST_APP")" + for URL in $URLS; do + assert_http_success $URL + done + assert_http_success "http://$TEST_APP.dokku.me:8080" + assert_http_success "http://$TEST_APP.dokku.me:8081" +} diff --git a/tests/unit/proxied-app.bats b/tests/unit/proxied-app.bats index e16928634..3d4ed4548 100644 --- a/tests/unit/proxied-app.bats +++ b/tests/unit/proxied-app.bats @@ -29,7 +29,7 @@ teardown() { echo "status: $status" assert_success - run /bin/bash -c "dokku proxy:ports-set $TEST_APP http:80:8080" + run /bin/bash -c "dokku ports:set $TEST_APP http:80:8080" echo "output: $output" echo "status: $status" assert_success diff --git a/tests/unit/proxy.bats b/tests/unit/proxy.bats index be69693fd..1de3c9db0 100644 --- a/tests/unit/proxy.bats +++ b/tests/unit/proxy.bats @@ -113,80 +113,3 @@ teardown() { assert_not_external_port $(<$CID_FILE) done } - -@test "(proxy) proxy:ports (list/add/set/remove/clear)" { - run /bin/bash -c "dokku proxy:ports-set $TEST_APP http:1234:5001" - echo "output: $output" - echo "status: $status" - assert_success - - run /bin/bash -c "dokku --quiet proxy:ports $TEST_APP | xargs" - echo "output: $output" - echo "status: $status" - assert_output "http 1234 5001" - - run /bin/bash -c "dokku proxy:ports-add $TEST_APP http:8080:5002 https:8443:5003" - echo "output: $output" - echo "status: $status" - assert_success - - run /bin/bash -c "dokku --quiet proxy:ports $TEST_APP | xargs" - echo "output: $output" - echo "status: $status" - assert_output "http 1234 5001 http 8080 5002 https 8443 5003" - - run /bin/bash -c "dokku proxy:ports-set $TEST_APP http:8080:5000 https:8443:5000 http:1234:5001" - echo "output: $output" - echo "status: $status" - assert_success - - run /bin/bash -c "dokku --quiet proxy:ports $TEST_APP | xargs" - echo "output: $output" - echo "status: $status" - assert_output "http 1234 5001 http 8080 5000 https 8443 5000" - - run /bin/bash -c "dokku proxy:ports-remove $TEST_APP 8080" - echo "output: $output" - echo "status: $status" - assert_success - - run /bin/bash -c "dokku --quiet proxy:ports $TEST_APP | xargs" - echo "output: $output" - echo "status: $status" - assert_output "http 1234 5001 https 8443 5000" - - run /bin/bash -c "dokku proxy:ports-remove $TEST_APP http:1234:5001" - echo "output: $output" - echo "status: $status" - assert_success - - run /bin/bash -c "dokku --quiet proxy:ports $TEST_APP | xargs" - echo "output: $output" - echo "status: $status" - assert_output "https 8443 5000" - - run /bin/bash -c "dokku proxy:ports-clear $TEST_APP" - echo "output: $output" - echo "status: $status" - assert_success - - run /bin/bash -c "dokku --quiet proxy:ports $TEST_APP | xargs" - echo "output: $output" - echo "status: $status" - assert_output "http 80 5000" -} - -@test "(proxy) proxy:ports (post-deploy add)" { - deploy_app - run /bin/bash -c "dokku proxy:ports-add $TEST_APP http:8080:5000 http:8081:5000" - echo "output: $output" - echo "status: $status" - assert_success - - URLS="$(dokku --quiet urls "$TEST_APP")" - for URL in $URLS; do - assert_http_success $URL - done - assert_http_success "http://$TEST_APP.dokku.me:8080" - assert_http_success "http://$TEST_APP.dokku.me:8081" -} diff --git a/tests/unit/traefik.bats b/tests/unit/traefik.bats index 4885a2aa4..4ff1eca63 100644 --- a/tests/unit/traefik.bats +++ b/tests/unit/traefik.bats @@ -233,7 +233,7 @@ teardown() { assert_success assert_output "5000" - run /bin/bash -c "dokku proxy:report $TEST_APP --proxy-port-map" + run /bin/bash -c "dokku ports:report $TEST_APP --ports-map" echo "output: $output" echo "status: $status" assert_output "http:80:5000" From eb6f85f08393a624d5bfaa4d5f100e1875ad0b36 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 28 May 2023 23:22:17 -0400 Subject: [PATCH 12/61] feat: skip scaled processes that are missing in the Procfile This allows folks to deploy apps that don't have a web process without needing to scale that process down before/after the first deploy. Note that the formations key in the app.json or a manual scale of other processes will be necessary to start anything non-web. Closes #5700 --- docs/development/plugin-triggers.md | 15 +++++++++++++++ plugins/20_events/procfile-exists | 1 + plugins/ps/Makefile | 2 +- plugins/ps/src/triggers/triggers.go | 3 +++ plugins/ps/triggers.go | 10 ++++++++++ .../scheduler-docker-local/scheduler-deploy | 11 +++++++++++ tests/apps/python/worker.Procfile | 1 + tests/unit/scheduler-docker-local.bats | 18 ++++++++++++++++++ 8 files changed, 60 insertions(+), 1 deletion(-) create mode 120000 plugins/20_events/procfile-exists create mode 100644 tests/apps/python/worker.Procfile diff --git a/docs/development/plugin-triggers.md b/docs/development/plugin-triggers.md index f74e6afb1..91e8887c6 100644 --- a/docs/development/plugin-triggers.md +++ b/docs/development/plugin-triggers.md @@ -1810,6 +1810,21 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x # TODO ``` +### `procfile-exists` + +- Description: Checks if a procfile exists for the specified app +- Invoked by: `internally` +- Arguments: `$APP` +- Example: + +```shell +#!/usr/bin/env bash + +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x + +# TODO +``` + ### `proxy-build-config` - Description: Builds the proxy implementation configuration for a given app diff --git a/plugins/20_events/procfile-exists b/plugins/20_events/procfile-exists new file mode 120000 index 000000000..5178a749f --- /dev/null +++ b/plugins/20_events/procfile-exists @@ -0,0 +1 @@ +hook \ No newline at end of file diff --git a/plugins/ps/Makefile b/plugins/ps/Makefile index dbf6e3221..fa9285662 100644 --- a/plugins/ps/Makefile +++ b/plugins/ps/Makefile @@ -1,5 +1,5 @@ SUBCOMMANDS = subcommands/inspect subcommands/rebuild subcommands/report subcommands/restart subcommands/restore subcommands/retire subcommands/scale subcommands/set subcommands/start subcommands/stop -TRIGGERS = triggers/app-restart triggers/core-post-deploy triggers/core-post-extract triggers/install triggers/post-app-clone triggers/post-app-clone-setup triggers/post-app-rename triggers/post-app-rename-setup triggers/post-create triggers/post-delete triggers/post-stop triggers/pre-deploy triggers/procfile-get-command triggers/ps-can-scale triggers/ps-current-scale triggers/ps-set-scale triggers/report +TRIGGERS = triggers/app-restart triggers/core-post-deploy triggers/core-post-extract triggers/install triggers/post-app-clone triggers/post-app-clone-setup triggers/post-app-rename triggers/post-app-rename-setup triggers/post-create triggers/post-delete triggers/post-stop triggers/pre-deploy triggers/procfile-get-command triggers/procfile-exists triggers/ps-can-scale triggers/ps-current-scale triggers/ps-set-scale triggers/report BUILD = commands subcommands triggers PLUGIN_NAME = ps diff --git a/plugins/ps/src/triggers/triggers.go b/plugins/ps/src/triggers/triggers.go index c4170d297..d0acb17b2 100644 --- a/plugins/ps/src/triggers/triggers.go +++ b/plugins/ps/src/triggers/triggers.go @@ -64,6 +64,9 @@ func main() { processType := flag.Arg(1) port := common.ToInt(flag.Arg(2), 5000) err = ps.TriggerProcfileGetCommand(appName, processType, port) + case "procfile-exists": + appName := flag.Arg(0) + err = ps.TriggerProcfileExists(appName) case "ps-can-scale": appName := flag.Arg(0) canScale := common.ToBool(flag.Arg(1)) diff --git a/plugins/ps/triggers.go b/plugins/ps/triggers.go index 160274d22..a36810c0b 100644 --- a/plugins/ps/triggers.go +++ b/plugins/ps/triggers.go @@ -1,6 +1,7 @@ package ps import ( + "errors" "fmt" "os" "path" @@ -259,6 +260,15 @@ func TriggerPreDeploy(appName string, imageTag string) error { return nil } +// TriggerProcfileExists checks if a procfile exists +func TriggerProcfileExists(appName string) error { + if hasProcfile(appName) { + return nil + } + + return errors.New("Procfile does not exist") +} + // TriggerProcfileGetCommand fetches a command from the procfile func TriggerProcfileGetCommand(appName string, processType string, port int) error { if !hasProcfile(appName) { diff --git a/plugins/scheduler-docker-local/scheduler-deploy b/plugins/scheduler-docker-local/scheduler-deploy index 0372bf139..aa69eedc6 100755 --- a/plugins/scheduler-docker-local/scheduler-deploy +++ b/plugins/scheduler-docker-local/scheduler-deploy @@ -37,6 +37,11 @@ trigger-scheduler-docker-local-scheduler-deploy() { DOKKU_START_CMD="$(config_get "$APP" DOKKU_START_CMD || true)" + local PROCFILE_EXISTS=false + if plugn trigger procfile-exists "$APP" 2>/dev/null; then + PROCFILE_EXISTS=true + fi + local DOKKU_WAIT_TO_RETIRE="$(plugn trigger checks-get-property "$APP" wait-to-retire)" export DOKKU_WAIT_TO_RETIRE @@ -54,6 +59,12 @@ trigger-scheduler-docker-local-scheduler-deploy() { continue fi + local PROC_CMD=$(plugn trigger procfile-get-command "$APP" "$PROC_TYPE" "5000" 2>/dev/null || echo '') + if [[ "$PROCFILE_EXISTS" == true ]] && [[ "$PROC_CMD" == "" ]]; then + dokku_log_warn "Skipping $PROC_TYPE as it is missing from the current Procfile" + continue + fi + if [[ "$PROC_TYPE" != "web" ]]; then echo "$PLUGIN_AVAILABLE_PATH/scheduler-docker-local/bin/scheduler-deploy-process $APP $IMAGE_SOURCE_TYPE $IMAGE $IMAGE_TAG $PROC_TYPE $PROC_COUNT" >>"$TMP_FILE" continue diff --git a/tests/apps/python/worker.Procfile b/tests/apps/python/worker.Procfile new file mode 100644 index 000000000..9eed80588 --- /dev/null +++ b/tests/apps/python/worker.Procfile @@ -0,0 +1 @@ +worker: python3 -u worker.py diff --git a/tests/unit/scheduler-docker-local.bats b/tests/unit/scheduler-docker-local.bats index 42c906634..8bce5e89c 100644 --- a/tests/unit/scheduler-docker-local.bats +++ b/tests/unit/scheduler-docker-local.bats @@ -56,6 +56,24 @@ teardown() { assert_success } +@test "(scheduler-docker-local) no-web" { + run create_app + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku ps:set $TEST_APP procfile-path worker.Procfile" + echo "output: $output" + echo "status: $status" + assert_success + + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success + assert_output_contains "Skipping web as it is missing from the current Procfile" +} + @test "(scheduler-docker-local) init-process" { run create_app echo "output: $output" From afc318526cc8764b24e374faf6bd288e3e48baf5 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 2 Jul 2023 02:43:55 -0400 Subject: [PATCH 13/61] refactor: move crontab writing code to scheduler-docker-local plugin The implementation was very heavily docker-local specific and thus didn't make a ton of sense to keep in the cron plugin. This also re-introduces changes from #5908 to parallelize the crontab generation (which is also specific to the docker-local scheduler) that were lost during a rebase. The impetus for this change was implementing a `cron:run` command. The logic for actually running the command isn't scheduler specific - merely invoking `dokku run` with the appropriate arguments - but all the logic around how the commands were retrieved were, so the change was made pre-emptively. --- plugins/cron/Makefile | 3 +- plugins/cron/cron.go | 78 +++++++ plugins/cron/functions.go | 193 ------------------ plugins/cron/report.go | 2 +- plugins/cron/subcommands.go | 2 +- plugins/cron/triggers.go | 26 --- plugins/scheduler-docker-local/.gitignore | 3 + plugins/scheduler-docker-local/Makefile | 5 + plugins/scheduler-docker-local/functions.go | 163 +++++++++++++++ plugins/scheduler-docker-local/go.mod | 27 +++ plugins/scheduler-docker-local/go.sum | 24 +++ plugins/scheduler-docker-local/install | 3 + plugins/scheduler-docker-local/post-delete | 2 + plugins/scheduler-docker-local/post-deploy | 13 ++ .../src/triggers/triggers.go | 13 +- plugins/scheduler-docker-local/triggers.go | 6 + 16 files changed, 329 insertions(+), 234 deletions(-) create mode 100644 plugins/cron/cron.go delete mode 100644 plugins/cron/functions.go delete mode 100644 plugins/cron/triggers.go create mode 100644 plugins/scheduler-docker-local/.gitignore create mode 100644 plugins/scheduler-docker-local/Makefile create mode 100644 plugins/scheduler-docker-local/functions.go create mode 100644 plugins/scheduler-docker-local/go.mod create mode 100644 plugins/scheduler-docker-local/go.sum create mode 100755 plugins/scheduler-docker-local/post-deploy rename plugins/{cron => scheduler-docker-local}/src/triggers/triggers.go (56%) create mode 100644 plugins/scheduler-docker-local/triggers.go diff --git a/plugins/cron/Makefile b/plugins/cron/Makefile index e0d6a5320..e2196f4dd 100644 --- a/plugins/cron/Makefile +++ b/plugins/cron/Makefile @@ -1,6 +1,5 @@ SUBCOMMANDS = subcommands/list subcommands/report -TRIGGERS = triggers/cron-write triggers/install triggers/post-delete triggers/post-deploy triggers/report -BUILD = commands subcommands triggers +BUILD = commands subcommands PLUGIN_NAME = cron include ../../common.mk diff --git a/plugins/cron/cron.go b/plugins/cron/cron.go new file mode 100644 index 000000000..d315583df --- /dev/null +++ b/plugins/cron/cron.go @@ -0,0 +1,78 @@ +package cron + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "strings" + + appjson "github.com/dokku/dokku/plugins/app-json" + "github.com/dokku/dokku/plugins/common" + + "github.com/multiformats/go-base36" + cronparser "github.com/robfig/cron/v3" +) + +type TemplateCommand struct { + ID string + App string + Command string + Schedule string + AltCommand string + LogFile string +} + +func (t TemplateCommand) CronCommand() string { + if t.AltCommand != "" { + if t.LogFile != "" { + return fmt.Sprintf("%s &>> %s", t.AltCommand, t.LogFile) + } + return t.AltCommand + } + + return fmt.Sprintf("dokku run --cron-id %s %s %s", t.ID, t.App, t.Command) +} + +func FetchCronEntries(appName string) ([]TemplateCommand, error) { + commands := []TemplateCommand{} + appjsonPath := appjson.GetAppjsonPath(appName) + if !common.FileExists(appjsonPath) { + return commands, nil + } + + b, err := ioutil.ReadFile(appjsonPath) + if err != nil { + return commands, fmt.Errorf("Cannot read app.json file for %s: %v", appName, err) + } + + if strings.TrimSpace(string(b)) == "" { + return commands, nil + } + + var appJSON appjson.AppJSON + if err = json.Unmarshal(b, &appJSON); err != nil { + return commands, fmt.Errorf("Cannot parse app.json for %s: %v", appName, err) + } + + for _, c := range appJSON.Cron { + parser := cronparser.NewParser(cronparser.Minute | cronparser.Hour | cronparser.Dom | cronparser.Month | cronparser.Dow | cronparser.Descriptor) + _, err := parser.Parse(c.Schedule) + if err != nil { + return commands, err + } + + commands = append(commands, TemplateCommand{ + App: appName, + Command: c.Command, + Schedule: c.Schedule, + ID: GenerateCommandID(appName, c), + }) + } + + return commands, nil +} + +// GenerateCommandID creates a unique ID for a given app/command/schedule combination +func GenerateCommandID(appName string, c appjson.CronCommand) string { + return base36.EncodeToStringLc([]byte(appName + "===" + c.Command + "===" + c.Schedule)) +} diff --git a/plugins/cron/functions.go b/plugins/cron/functions.go deleted file mode 100644 index 1ce09d794..000000000 --- a/plugins/cron/functions.go +++ /dev/null @@ -1,193 +0,0 @@ -package cron - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "os" - "path/filepath" - "strings" - "text/template" - - appjson "github.com/dokku/dokku/plugins/app-json" - "github.com/dokku/dokku/plugins/common" - - base36 "github.com/multiformats/go-base36" - cronparser "github.com/robfig/cron/v3" -) - -type templateCommand struct { - ID string - App string - Command string - Schedule string - AltCommand string - LogFile string -} - -func (t templateCommand) CronCommand() string { - if t.AltCommand != "" { - if t.LogFile != "" { - return fmt.Sprintf("%s &>> %s", t.AltCommand, t.LogFile) - } - return t.AltCommand - } - - return fmt.Sprintf("dokku run --cron-id %s %s %s", t.ID, t.App, t.Command) -} - -func fetchCronEntries(appName string) ([]templateCommand, error) { - commands := []templateCommand{} - scheduler := common.GetAppScheduler(appName) - if scheduler != "docker-local" { - return commands, nil - } - - appjsonPath := appjson.GetAppjsonPath(appName) - if !common.FileExists(appjsonPath) { - return commands, nil - } - - b, err := ioutil.ReadFile(appjsonPath) - if err != nil { - return commands, fmt.Errorf("Cannot read app.json file for %s: %v", appName, err) - } - - if strings.TrimSpace(string(b)) == "" { - return commands, nil - } - - var appJSON appjson.AppJSON - if err = json.Unmarshal(b, &appJSON); err != nil { - return commands, fmt.Errorf("Cannot parse app.json for %s: %v", appName, err) - } - - for _, c := range appJSON.Cron { - parser := cronparser.NewParser(cronparser.Minute | cronparser.Hour | cronparser.Dom | cronparser.Month | cronparser.Dow | cronparser.Descriptor) - _, err := parser.Parse(c.Schedule) - if err != nil { - return commands, err - } - - commands = append(commands, templateCommand{ - App: appName, - Command: c.Command, - Schedule: c.Schedule, - ID: generateCommandID(appName, c), - }) - } - - return commands, nil -} - -func deleteCrontab() error { - command := common.NewShellCmd("sudo /usr/bin/crontab -l -u dokku") - command.ShowOutput = false - if !command.Execute() { - return nil - } - - command = common.NewShellCmd("sudo /usr/bin/crontab -r -u dokku") - command.ShowOutput = false - out, err := command.CombinedOutput() - if err != nil { - return fmt.Errorf("Unable to remove schedule file: %v", string(out)) - } - - common.LogInfo1("Removed") - return nil -} - -func writeCronEntries() error { - apps, _ := common.UnfilteredDokkuApps() - commands := []templateCommand{} - for _, appName := range apps { - scheduler := common.GetAppScheduler(appName) - if scheduler != "docker-local" { - continue - } - - c, err := fetchCronEntries(appName) - if err != nil { - return err - } - - commands = append(commands, c...) - } - - b, _ := common.PlugnTriggerOutput("cron-entries", "docker-local") - for _, line := range strings.Split(strings.TrimSpace(string(b[:])), "\n") { - if strings.TrimSpace(line) == "" { - continue - } - - parts := strings.Split(line, ";") - if len(parts) != 2 && len(parts) != 3 { - return fmt.Errorf("Invalid injected cron task: %v", line) - } - - id := base36.EncodeToStringLc([]byte(strings.Join(parts, ";;;"))) - command := templateCommand{ - ID: id, - Schedule: parts[0], - AltCommand: parts[1], - } - if len(parts) == 3 { - command.LogFile = parts[2] - } - commands = append(commands, command) - } - - if len(commands) == 0 { - return deleteCrontab() - } - - data := map[string]interface{}{ - "Commands": commands, - } - - t, err := getCronTemplate() - if err != nil { - return err - } - - tmpFile, err := ioutil.TempFile(os.TempDir(), fmt.Sprintf("dokku-%s-%s", common.MustGetEnv("DOKKU_PID"), "WriteCronEntries")) - if err != nil { - return fmt.Errorf("Cannot create temporary schedule file: %v", err) - } - - defer tmpFile.Close() - defer os.Remove(tmpFile.Name()) - - if err := t.Execute(tmpFile, data); err != nil { - return fmt.Errorf("Unable to template out schedule file: %v", err) - } - - command := common.NewShellCmd(fmt.Sprintf("sudo /usr/bin/crontab -u dokku %s", tmpFile.Name())) - command.ShowOutput = false - out, err := command.CombinedOutput() - if err != nil { - return fmt.Errorf("Unable to update schedule file: %s", string(out)) - } - - common.LogInfo1("Updated schedule file") - - return nil -} - -func getCronTemplate() (*template.Template, error) { - t := template.New("cron") - - templatePath := filepath.Join(common.MustGetEnv("PLUGIN_ENABLED_PATH"), "cron", "templates", "cron.tmpl") - b, err := ioutil.ReadFile(templatePath) - if err != nil { - return t, fmt.Errorf("Cannot read template file: %v", err) - } - - s := strings.TrimSpace(string(b)) - return t.Parse(s) -} - -func generateCommandID(appName string, c appjson.CronCommand) string { - return base36.EncodeToStringLc([]byte(appName + "===" + c.Command + "===" + c.Schedule)) -} diff --git a/plugins/cron/report.go b/plugins/cron/report.go index 7e81b673f..da87de902 100644 --- a/plugins/cron/report.go +++ b/plugins/cron/report.go @@ -28,6 +28,6 @@ func ReportSingleApp(appName string, format string, infoFlag string) error { } func reportTasks(appName string) string { - c, _ := fetchCronEntries(appName) + c, _ := FetchCronEntries(appName) return strconv.Itoa(len(c)) } diff --git a/plugins/cron/subcommands.go b/plugins/cron/subcommands.go index 88bca6231..72a13f89b 100644 --- a/plugins/cron/subcommands.go +++ b/plugins/cron/subcommands.go @@ -13,7 +13,7 @@ func CommandList(appName string) error { return err } - entries, err := fetchCronEntries(appName) + entries, err := FetchCronEntries(appName) if err != nil { return err } diff --git a/plugins/cron/triggers.go b/plugins/cron/triggers.go deleted file mode 100644 index 07dcbe84b..000000000 --- a/plugins/cron/triggers.go +++ /dev/null @@ -1,26 +0,0 @@ -package cron - -import ( - "github.com/dokku/dokku/plugins/common" -) - -// TriggerInstall installs a sudoers file so we can execute crontab via sudo -func TriggerInstall() error { - lines := []string{"%dokku ALL=(ALL) NOPASSWD:/usr/bin/crontab"} - return common.WriteSliceToFile("/etc/sudoers.d/dokku-cron", lines) -} - -// TriggerPostDelete updates the cron entries for all apps -func TriggerPostDelete() error { - return writeCronEntries() -} - -// TriggerPostDeploy updates the cron entries for all apps -func TriggerPostDeploy() error { - return writeCronEntries() -} - -// TriggerCronWrite force updates the cron file for all apps -func TriggerCronWrite() error { - return writeCronEntries() -} diff --git a/plugins/scheduler-docker-local/.gitignore b/plugins/scheduler-docker-local/.gitignore new file mode 100644 index 000000000..637119bb8 --- /dev/null +++ b/plugins/scheduler-docker-local/.gitignore @@ -0,0 +1,3 @@ +/triggers/* +/triggers +/cron-* diff --git a/plugins/scheduler-docker-local/Makefile b/plugins/scheduler-docker-local/Makefile new file mode 100644 index 000000000..09f04eabb --- /dev/null +++ b/plugins/scheduler-docker-local/Makefile @@ -0,0 +1,5 @@ +TRIGGERS = triggers/cron-write +BUILD = triggers +PLUGIN_NAME = scheduler-docker-local + +include ../../common.mk diff --git a/plugins/scheduler-docker-local/functions.go b/plugins/scheduler-docker-local/functions.go new file mode 100644 index 000000000..89d1f49e4 --- /dev/null +++ b/plugins/scheduler-docker-local/functions.go @@ -0,0 +1,163 @@ +package schedulerdockerlocal + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + "text/template" + + "github.com/dokku/dokku/plugins/common" + "github.com/dokku/dokku/plugins/cron" + "golang.org/x/sync/errgroup" + + base36 "github.com/multiformats/go-base36" +) + +func deleteCrontab() error { + command := common.NewShellCmd("sudo /usr/bin/crontab -l -u dokku") + command.ShowOutput = false + if !command.Execute() { + return nil + } + + command = common.NewShellCmd("sudo /usr/bin/crontab -r -u dokku") + command.ShowOutput = false + out, err := command.CombinedOutput() + if err != nil { + return fmt.Errorf("Unable to remove schedule file: %v", string(out)) + } + + common.LogInfo1("Removed") + return nil +} + +func generateCronEntries() ([]cron.TemplateCommand, error) { + apps, _ := common.UnfilteredDokkuApps() + + g := new(errgroup.Group) + results := make(chan []cron.TemplateCommand, len(apps)+1) + for _, appName := range apps { + appName := appName + g.Go(func() error { + scheduler := common.GetAppScheduler(appName) + if scheduler != "docker-local" { + results <- []cron.TemplateCommand{} + return nil + } + + c, err := cron.FetchCronEntries(appName) + if err != nil { + results <- []cron.TemplateCommand{} + return err + } + + results <- c + return nil + }) + } + + g.Go(func() error { + commands := []cron.TemplateCommand{} + b, _ := common.PlugnTriggerOutput("cron-entries", "docker-local") + for _, line := range strings.Split(strings.TrimSpace(string(b[:])), "\n") { + if strings.TrimSpace(line) == "" { + results <- []cron.TemplateCommand{} + return nil + } + + parts := strings.Split(line, ";") + if len(parts) != 2 && len(parts) != 3 { + results <- []cron.TemplateCommand{} + return fmt.Errorf("Invalid injected cron task: %v", line) + } + + id := base36.EncodeToStringLc([]byte(strings.Join(parts, ";;;"))) + command := cron.TemplateCommand{ + ID: id, + Schedule: parts[0], + AltCommand: parts[1], + } + if len(parts) == 3 { + command.LogFile = parts[2] + } + commands = append(commands, command) + } + results <- commands + return nil + }) + + err := g.Wait() + close(results) + + commands := []cron.TemplateCommand{} + if err != nil { + return commands, err + } + + for result := range results { + c := result + if len(c) > 0 { + commands = append(commands, c...) + } + } + + return commands, nil +} + +func writeCronEntries() error { + commands, err := generateCronEntries() + if err != nil { + return err + } + + if len(commands) == 0 { + return deleteCrontab() + } + + data := map[string]interface{}{ + "Commands": commands, + } + + t, err := getCronTemplate() + if err != nil { + return err + } + + tmpFile, err := ioutil.TempFile(os.TempDir(), fmt.Sprintf("dokku-%s-%s", common.MustGetEnv("DOKKU_PID"), "WriteCronEntries")) + if err != nil { + return fmt.Errorf("Cannot create temporary schedule file: %v", err) + } + + defer tmpFile.Close() + defer os.Remove(tmpFile.Name()) + + if err := t.Execute(tmpFile, data); err != nil { + return fmt.Errorf("Unable to template out schedule file: %v", err) + } + + command := common.NewShellCmd(fmt.Sprintf("sudo /usr/bin/crontab -u dokku %s", tmpFile.Name())) + command.ShowOutput = false + out, err := command.CombinedOutput() + if err != nil { + return fmt.Errorf("Unable to update schedule file: %s", string(out)) + } + + common.LogInfo1("Updated schedule file") + + return nil +} + +func getCronTemplate() (*template.Template, error) { + t := template.New("cron") + + templatePath := filepath.Join(common.MustGetEnv("PLUGIN_ENABLED_PATH"), "cron", "templates", "cron.tmpl") + b, err := ioutil.ReadFile(templatePath) + if err != nil { + return t, fmt.Errorf("Cannot read template file: %v", err) + } + + s := strings.TrimSpace(string(b)) + return t.Parse(s) +} diff --git a/plugins/scheduler-docker-local/go.mod b/plugins/scheduler-docker-local/go.mod new file mode 100644 index 000000000..8e565bfe1 --- /dev/null +++ b/plugins/scheduler-docker-local/go.mod @@ -0,0 +1,27 @@ +module github.com/dokku/dokku/plugins/scheduler-docker-local + +go 1.19 + +require ( + github.com/dokku/dokku/plugins/common v0.0.0-00010101000000-000000000000 + github.com/dokku/dokku/plugins/cron v0.0.0-00010101000000-000000000000 + github.com/multiformats/go-base36 v0.2.0 + golang.org/x/sync v0.3.0 +) + +require ( + github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 // indirect + github.com/codeskyblue/go-sh v0.0.0-20190412065543-76bd3d59ff27 // indirect + github.com/dokku/dokku/plugins/app-json v0.0.0-00010101000000-000000000000 // indirect + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect + github.com/otiai10/copy v1.12.0 // indirect + github.com/robfig/cron/v3 v3.0.1 // indirect + github.com/ryanuber/columnize v2.1.2+incompatible // indirect + golang.org/x/sys v0.8.0 // indirect +) + +replace github.com/dokku/dokku/plugins/app-json => ../app-json + +replace github.com/dokku/dokku/plugins/common => ../common + +replace github.com/dokku/dokku/plugins/cron => ../cron diff --git a/plugins/scheduler-docker-local/go.sum b/plugins/scheduler-docker-local/go.sum new file mode 100644 index 000000000..085ca0d9e --- /dev/null +++ b/plugins/scheduler-docker-local/go.sum @@ -0,0 +1,24 @@ +github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 h1:sDMmm+q/3+BukdIpxwO365v/Rbspp2Nt5XntgQRXq8Q= +github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= +github.com/codeskyblue/go-sh v0.0.0-20190412065543-76bd3d59ff27 h1:HHUr4P/aKh4quafGxDT9LDasjGdlGkzLbfmmrlng3kA= +github.com/codeskyblue/go-sh v0.0.0-20190412065543-76bd3d59ff27/go.mod h1:VQx0hjo2oUeQkQUET7wRwradO6f+fN5jzXgB/zROxxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= +github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= +github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= +github.com/otiai10/copy v1.12.0 h1:cLMgSQnXBs1eehF0Wy/FAGsgDTDmAqFR7rQylBb1nDY= +github.com/otiai10/copy v1.12.0/go.mod h1:rSaLseMUsZFFbsFGc7wCJnnkTAvdc5L6VWxPE4308Ww= +github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks= +github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= +github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= +github.com/ryanuber/columnize v2.1.2+incompatible h1:C89EOx/XBWwIXl8wm8OPJBd7kPF25UfsK2X7Ph/zCAk= +github.com/ryanuber/columnize v2.1.2+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/plugins/scheduler-docker-local/install b/plugins/scheduler-docker-local/install index 319838e2a..0fd3de0a5 100755 --- a/plugins/scheduler-docker-local/install +++ b/plugins/scheduler-docker-local/install @@ -14,6 +14,9 @@ trigger-scheduler-docker-local-install() { fn-plugin-property-setup "scheduler-docker-local" + echo "%dokku ALL=(ALL) NOPASSWD:/usr/bin/crontab" >"/etc/sudoers.d/dokku-cron" + chmod "0440" "/etc/sudoers.d/dokku-cron" + DOKKU_PATH="$(command -v dokku)" if [[ $(systemctl 2>/dev/null) =~ -\.mount ]]; then diff --git a/plugins/scheduler-docker-local/post-delete b/plugins/scheduler-docker-local/post-delete index 109b07748..472643544 100755 --- a/plugins/scheduler-docker-local/post-delete +++ b/plugins/scheduler-docker-local/post-delete @@ -14,6 +14,8 @@ trigger-scheduler-docker-local-post-delete() { # remove all application containers "$DOCKER_BIN" container ls --filter "label=com.dokku.app-name=${APP}" -q | xargs -n1 -I {} "$DOCKER_BIN" container rm --force {} &>/dev/null || true + + plugn trigger cron-write } trigger-scheduler-docker-local-post-delete "$@" diff --git a/plugins/scheduler-docker-local/post-deploy b/plugins/scheduler-docker-local/post-deploy new file mode 100755 index 000000000..3c19037f1 --- /dev/null +++ b/plugins/scheduler-docker-local/post-deploy @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +trigger-scheduler-docker-local-post-deploy() { + declare desc="scheduler-docker-local post-deploy plugin trigger" + declare trigger="post-deploy" + declare APP="$1" + + plugn trigger cron-write +} + +trigger-scheduler-docker-local-post-deploy "$@" diff --git a/plugins/cron/src/triggers/triggers.go b/plugins/scheduler-docker-local/src/triggers/triggers.go similarity index 56% rename from plugins/cron/src/triggers/triggers.go rename to plugins/scheduler-docker-local/src/triggers/triggers.go index 43ae583f7..86b030d9d 100644 --- a/plugins/cron/src/triggers/triggers.go +++ b/plugins/scheduler-docker-local/src/triggers/triggers.go @@ -7,7 +7,7 @@ import ( "strings" "github.com/dokku/dokku/plugins/common" - cron "github.com/dokku/dokku/plugins/cron" + schedulerdockerlocal "github.com/dokku/dokku/plugins/scheduler-docker-local" ) // main entrypoint to all triggers @@ -19,16 +19,7 @@ func main() { var err error switch trigger { case "cron-write": - err = cron.TriggerCronWrite() - case "install": - err = cron.TriggerInstall() - case "post-delete": - err = cron.TriggerPostDelete() - case "post-deploy": - err = cron.TriggerPostDeploy() - case "report": - appName := flag.Arg(0) - err = cron.ReportSingleApp(appName, "", "") + err = schedulerdockerlocal.TriggerCronWrite() default: err = fmt.Errorf("Invalid plugin trigger call: %s", trigger) } diff --git a/plugins/scheduler-docker-local/triggers.go b/plugins/scheduler-docker-local/triggers.go new file mode 100644 index 000000000..018ff411e --- /dev/null +++ b/plugins/scheduler-docker-local/triggers.go @@ -0,0 +1,6 @@ +package schedulerdockerlocal + +// TriggerCronWrite force updates the cron file for all apps +func TriggerCronWrite() error { + return writeCronEntries() +} From 820883e60c9936a0e4b63fdf8b2329388652c1e9 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 2 Jul 2023 04:36:08 -0400 Subject: [PATCH 14/61] feat: add json format output to cron:list command This makes it easier to parse output in a machine-readable format - such as when interacting with cron tasks via jq for writing tests. --- docs/processes/scheduled-cron-tasks.md | 14 ++++++++-- plugins/cron/cron.go | 12 ++++---- plugins/cron/src/commands/commands.go | 2 +- plugins/cron/src/subcommands/subcommands.go | 3 +- plugins/cron/subcommands.go | 31 +++++++++++++++++---- tests/unit/cron.bats | 20 +++++++++++++ 6 files changed, 66 insertions(+), 16 deletions(-) diff --git a/docs/processes/scheduled-cron-tasks.md b/docs/processes/scheduled-cron-tasks.md index fc7c59859..adeab9dcf 100644 --- a/docs/processes/scheduled-cron-tasks.md +++ b/docs/processes/scheduled-cron-tasks.md @@ -3,8 +3,8 @@ > New as of 0.23.0 ``` -cron:list # List scheduled cron tasks for an app -cron:report [] [] # Display report about an app +cron:list [--format json|stdout] # List scheduled cron tasks for an app +cron:report [] [] # Display report about an app ``` ## Usage @@ -62,6 +62,16 @@ cGhwPT09cGhwIHRlc3QucGhwPT09QGRhaWx5 @daily node index.js cGhwPT09dHJ1ZT09PSogKiAqICogKg== * * * * * true ``` +The output can also be displayed in json format: + +```shell +dokku cron:list node-js-app --format json +``` + +``` +[{"id":"cGhwPT09cGhwIHRlc3QucGhwPT09QGRhaWx5","app":"node-js-app","command":"node index.js","schedule":"@daily"}] +``` + #### Displaying reports You can get a report about the cron configuration for apps using the `cron:report` command: diff --git a/plugins/cron/cron.go b/plugins/cron/cron.go index d315583df..7c71f0735 100644 --- a/plugins/cron/cron.go +++ b/plugins/cron/cron.go @@ -14,12 +14,12 @@ import ( ) type TemplateCommand struct { - ID string - App string - Command string - Schedule string - AltCommand string - LogFile string + ID string `json:"id"` + App string `json:"app"` + Command string `json:"command"` + Schedule string `json:"schedule"` + AltCommand string `json:"-"` + LogFile string `json:"-"` } func (t TemplateCommand) CronCommand() string { diff --git a/plugins/cron/src/commands/commands.go b/plugins/cron/src/commands/commands.go index 402ac2799..90f825fc2 100644 --- a/plugins/cron/src/commands/commands.go +++ b/plugins/cron/src/commands/commands.go @@ -18,7 +18,7 @@ Manage scheduled cron tasks Additional commands:` helpContent = ` - cron:list , List scheduled cron tasks for an app + cron:list [--format json|stdout], List scheduled cron tasks for an app cron:report [] [], Display report about an app ` ) diff --git a/plugins/cron/src/subcommands/subcommands.go b/plugins/cron/src/subcommands/subcommands.go index 12daa862f..302fdef6e 100644 --- a/plugins/cron/src/subcommands/subcommands.go +++ b/plugins/cron/src/subcommands/subcommands.go @@ -20,9 +20,10 @@ func main() { switch subcommand { case "list": args := flag.NewFlagSet("cron:list", flag.ExitOnError) + format := args.String("format", "stdout", "format: [ stdout | json ]") args.Parse(os.Args[2:]) appName := args.Arg(0) - err = cron.CommandList(appName) + err = cron.CommandList(appName, *format) case "report": args := flag.NewFlagSet("cron:report", flag.ExitOnError) format := args.String("format", "stdout", "format: [ stdout | json ]") diff --git a/plugins/cron/subcommands.go b/plugins/cron/subcommands.go index 72a13f89b..b34bf758b 100644 --- a/plugins/cron/subcommands.go +++ b/plugins/cron/subcommands.go @@ -1,6 +1,7 @@ package cron import ( + "encoding/json" "fmt" "github.com/dokku/dokku/plugins/common" @@ -8,22 +9,40 @@ import ( ) // CommandList lists all scheduled cron tasks for a given app -func CommandList(appName string) error { +func CommandList(appName string, format string) error { if err := common.VerifyAppName(appName); err != nil { return err } + if format == "" { + format = "stdout" + } + + if format != "stdout" && format != "json" { + return fmt.Errorf("Invalid format specified, supported formats: json, stdout") + } + entries, err := FetchCronEntries(appName) if err != nil { return err } - output := []string{"ID | Schedule | Command"} - for _, entry := range entries { - output = append(output, fmt.Sprintf("%s | %s | %s", entry.ID, entry.Schedule, entry.Command)) + if format == "stdout" { + output := []string{"ID | Schedule | Command"} + for _, entry := range entries { + output = append(output, fmt.Sprintf("%s | %s | %s", entry.ID, entry.Schedule, entry.Command)) + } + + result := columnize.SimpleFormat(output) + fmt.Println(result) + return nil } - result := columnize.SimpleFormat(output) - fmt.Println(result) + + out, err := json.Marshal(entries) + if err != nil { + return err + } + common.Log(string(out)) return nil } diff --git a/tests/unit/cron.bats b/tests/unit/cron.bats index 6f7f20afd..4349027bc 100644 --- a/tests/unit/cron.bats +++ b/tests/unit/cron.bats @@ -177,6 +177,26 @@ teardown() { assert_failure } +@test "(cron) cron:list --format json" { + run deploy_app python dokku@dokku.me:$TEST_APP template_cron_file_valid + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku cron:list $TEST_APP --format json" + echo "output: $output" + echo "status: $status" + assert_success + assert_output_exists + + cron_id="$(dokku cron:list $TEST_APP --format json | jq -r '.[0].id')" + run /bin/bash -c "echo $cron_id" + echo "output: $output" + echo "status: $status" + assert_success + assert_output_exists +} + template_cron_file_invalid() { local APP="$1" local APP_REPO_DIR="$2" From 1d0d050596fd5469997e4c2d45867e82e9cd1f88 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 2 Jul 2023 05:01:52 -0400 Subject: [PATCH 15/61] feat: mount the vector data directory instead of the vector file This allows users to provide extra configuration files for Vector, such as when using the GCP Operations log sink. Closes #4971 --- docs/appendices/0.31.0-migration-guide.md | 1 + plugins/logs/functions.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/appendices/0.31.0-migration-guide.md b/docs/appendices/0.31.0-migration-guide.md index e81819399..98835f867 100644 --- a/docs/appendices/0.31.0-migration-guide.md +++ b/docs/appendices/0.31.0-migration-guide.md @@ -4,3 +4,4 @@ - Herokuish build cache is now mounted from a docker volume - eg. `cache-node-js-app` - instead of the local filesystem. All existing app cache will be cleared upon upgrading past 0.29.0. - The `proxy:ports*` commands have been replaced with the new `ports` plugin. Users will be able to use the old `proxy:ports*` commands for a single minor release, and they will be removed in the next minor release. +- The `vector` container integration now mounts config to `/etc/vector` instead of the path `/etc/vector/vector.json`, allowing users the ability to provide extra configuration for Vector Sinks. To take advantage of the new functionality, the vector container should be stopped (via `dokku logs:vector-stop`) and then started (via `dokku logs:vector-start`). diff --git a/plugins/logs/functions.go b/plugins/logs/functions.go index 14cb3d5c9..edc41faa5 100644 --- a/plugins/logs/functions.go +++ b/plugins/logs/functions.go @@ -72,7 +72,7 @@ func startVectorContainer(vectorImage string) error { "container", "run", "--detach", "--name", vectorContainerName, common.MustGetEnv("DOKKU_GLOBAL_RUN_ARGS"), "--restart", "unless-stopped", - "--volume", "/var/lib/dokku/data/logs/vector.json:/etc/vector/vector.json", + "--volume", "/var/lib/dokku/data/logs:/etc/vector", "--volume", "/var/run/docker.sock:/var/run/docker.sock", "--volume", common.MustGetEnv("DOKKU_LOGS_HOST_DIR") + ":/var/logs/dokku/apps", "--volume", common.MustGetEnv("DOKKU_LOGS_HOST_DIR") + "/apps:/var/log/dokku/apps", From 798a413096b1c14a8b4e2bcf75d373d7cbf61626 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 2 Jul 2023 05:48:09 -0400 Subject: [PATCH 16/61] feat: re-add apps and config cli aliases This better follows Heroku's output and is more useful to users. The subcommands will be treated as the primary, documented versions, while the aliases are there for convenience. Closes #5362 --- plugins/apps/src/commands/commands.go | 11 ++++++++++- plugins/config/src/commands/commands.go | 8 ++++---- tests/unit/apps_1.bats | 2 +- tests/unit/config.bats | 7 +++++++ 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/plugins/apps/src/commands/commands.go b/plugins/apps/src/commands/commands.go index 0e38ebbd9..b37257f76 100644 --- a/plugins/apps/src/commands/commands.go +++ b/plugins/apps/src/commands/commands.go @@ -7,6 +7,7 @@ import ( "strconv" "strings" + "github.com/dokku/dokku/plugins/apps" "github.com/dokku/dokku/plugins/common" ) @@ -37,7 +38,15 @@ func main() { cmd := flag.Arg(0) switch cmd { - case "apps", "apps:help": + case "apps": + args := flag.NewFlagSet("apps", flag.ExitOnError) + args.Usage = usage + args.Parse(os.Args[2:]) + + if err := apps.CommandList(); err != nil { + common.LogFailWithError(err) + } + case "apps:help": usage() case "help": command := common.NewShellCmd(fmt.Sprintf("ps -o command= %d", os.Getppid())) diff --git a/plugins/config/src/commands/commands.go b/plugins/config/src/commands/commands.go index df63fabaf..5c9d63c76 100644 --- a/plugins/config/src/commands/commands.go +++ b/plugins/config/src/commands/commands.go @@ -38,16 +38,16 @@ func main() { cmd := flag.Arg(0) switch cmd { case "config": - common.LogWarn("Deprecated: Use the 'config:show' command instead") - args := flag.NewFlagSet("config:show", flag.ExitOnError) + args := flag.NewFlagSet("config", flag.ExitOnError) + args.Usage = usage global := args.Bool("global", false, "--global: use the global environment") shell := args.Bool("shell", false, "--shell: in a single-line for usage in command-line utilities [deprecated]") export := args.Bool("export", false, "--export: print the env as eval-compatible exports [deprecated]") merged := args.Bool("merged", false, "--merged: display the app's environment merged with the global environment") args.Parse(os.Args[2:]) appName := args.Arg(0) - err := config.CommandShow(appName, *global, *merged, *shell, *export) - if err != nil { + + if err := config.CommandShow(appName, *global, *merged, *shell, *export); err != nil { common.LogFailWithError(err) } case "config:help": diff --git a/tests/unit/apps_1.bats b/tests/unit/apps_1.bats index cb353a439..c5086bcd4 100644 --- a/tests/unit/apps_1.bats +++ b/tests/unit/apps_1.bats @@ -11,7 +11,7 @@ teardown() { } @test "(apps) apps:help" { - run /bin/bash -c "dokku apps" + run /bin/bash -c "dokku apps --help" echo "output: $output" echo "status: $status" assert_output_contains "Manage apps" diff --git a/tests/unit/config.bats b/tests/unit/config.bats index 610fd0a63..6216fe441 100644 --- a/tests/unit/config.bats +++ b/tests/unit/config.bats @@ -19,10 +19,17 @@ teardown() { } @test "(config) config:help" { + run /bin/bash -c "dokku config --help" + echo "output: $output" + echo "status: $status" + assert_output_contains "Manage global and app-specific config vars" + help_output="$output" + run /bin/bash -c "dokku config:help" echo "output: $output" echo "status: $status" assert_output_contains "Manage global and app-specific config vars" + assert_output "$help_output" } @test "(config) config:set --global" { From 515994c8d3a0537e948c5fbb90dd7934b3051226 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 2 Jul 2023 04:53:16 -0400 Subject: [PATCH 17/61] feat: add the ability to execute a cron task on the fly Closes #4904 --- docs/processes/scheduled-cron-tasks.md | 17 +++++++ plugins/cron/Makefile | 2 +- plugins/cron/go.mod | 1 + plugins/cron/go.sum | 6 +++ plugins/cron/src/commands/commands.go | 1 + plugins/cron/src/subcommands/subcommands.go | 7 +++ plugins/cron/subcommands.go | 48 ++++++++++++++++++ plugins/scheduler-docker-local/go.mod | 1 + plugins/scheduler-docker-local/go.sum | 6 +++ tests/apps/python/task.py | 2 + tests/unit/cron.bats | 55 ++++++++++++++++++++- 11 files changed, 144 insertions(+), 2 deletions(-) diff --git a/docs/processes/scheduled-cron-tasks.md b/docs/processes/scheduled-cron-tasks.md index adeab9dcf..c53a97d8b 100644 --- a/docs/processes/scheduled-cron-tasks.md +++ b/docs/processes/scheduled-cron-tasks.md @@ -5,6 +5,7 @@ ``` cron:list [--format json|stdout] # List scheduled cron tasks for an app cron:report [] [] # Display report about an app +cron:run [--detach] # Run a cron task on the fly ``` ## Usage @@ -72,6 +73,22 @@ dokku cron:list node-js-app --format json [{"id":"cGhwPT09cGhwIHRlc3QucGhwPT09QGRhaWx5","app":"node-js-app","command":"node index.js","schedule":"@daily"}] ``` +#### Executing a cron task on the fly + +Cron tasks can be invoked via the `cron:run` command. This command takes an `app` argument and a `cron id` (retrievable from `cron:list` output). + +```shell +dokku cron:run node-js-app cGhwPT09cGhwIHRlc3QucGhwPT09QGRhaWx5 +``` + +By default, the task is run in an attached container - as supported by the scheduler. To run in a background detached container, specify the `--detach` flag: + +```shell +dokku cron:run node-js-app cGhwPT09cGhwIHRlc3QucGhwPT09QGRhaWx5 --detach +``` + +All one-off cron executions have their containers terminated after invocation. + #### Displaying reports You can get a report about the cron configuration for apps using the `cron:report` command: diff --git a/plugins/cron/Makefile b/plugins/cron/Makefile index e2196f4dd..85c72cfea 100644 --- a/plugins/cron/Makefile +++ b/plugins/cron/Makefile @@ -1,4 +1,4 @@ -SUBCOMMANDS = subcommands/list subcommands/report +SUBCOMMANDS = subcommands/list subcommands/report subcommands/run BUILD = commands subcommands PLUGIN_NAME = cron diff --git a/plugins/cron/go.mod b/plugins/cron/go.mod index a11bc619f..66dcf00ba 100644 --- a/plugins/cron/go.mod +++ b/plugins/cron/go.mod @@ -9,6 +9,7 @@ require ( github.com/robfig/cron/v3 v3.0.1 github.com/ryanuber/columnize v2.1.2+incompatible github.com/spf13/pflag v1.0.5 + mvdan.cc/sh/v3 v3.7.0 ) require ( diff --git a/plugins/cron/go.sum b/plugins/cron/go.sum index f9e6e68bc..02dbf1968 100644 --- a/plugins/cron/go.sum +++ b/plugins/cron/go.sum @@ -2,9 +2,12 @@ github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 h1:sDMmm+q/3+Bu github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= github.com/codeskyblue/go-sh v0.0.0-20190412065543-76bd3d59ff27 h1:HHUr4P/aKh4quafGxDT9LDasjGdlGkzLbfmmrlng3kA= github.com/codeskyblue/go-sh v0.0.0-20190412065543-76bd3d59ff27/go.mod h1:VQx0hjo2oUeQkQUET7wRwradO6f+fN5jzXgB/zROxxE= +github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= @@ -13,6 +16,7 @@ github.com/otiai10/copy v1.12.0/go.mod h1:rSaLseMUsZFFbsFGc7wCJnnkTAvdc5L6VWxPE4 github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= +github.com/rogpeppe/go-internal v1.10.1-0.20230524175051-ec119421bb97 h1:3RPlVWzZ/PDqmVuf/FKHARG5EMid/tl7cv54Sw/QRVY= github.com/ryanuber/columnize v2.1.2+incompatible h1:C89EOx/XBWwIXl8wm8OPJBd7kPF25UfsK2X7Ph/zCAk= github.com/ryanuber/columnize v2.1.2+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -24,3 +28,5 @@ golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +mvdan.cc/sh/v3 v3.7.0 h1:lSTjdP/1xsddtaKfGg7Myu7DnlHItd3/M2tomOcNNBg= +mvdan.cc/sh/v3 v3.7.0/go.mod h1:K2gwkaesF/D7av7Kxl0HbF5kGOd2ArupNTX3X44+8l8= diff --git a/plugins/cron/src/commands/commands.go b/plugins/cron/src/commands/commands.go index 90f825fc2..466d428bb 100644 --- a/plugins/cron/src/commands/commands.go +++ b/plugins/cron/src/commands/commands.go @@ -20,6 +20,7 @@ Additional commands:` helpContent = ` cron:list [--format json|stdout], List scheduled cron tasks for an app cron:report [] [], Display report about an app + cron:run [--detach], Run a cron task on the fly ` ) diff --git a/plugins/cron/src/subcommands/subcommands.go b/plugins/cron/src/subcommands/subcommands.go index 302fdef6e..94ff0c191 100644 --- a/plugins/cron/src/subcommands/subcommands.go +++ b/plugins/cron/src/subcommands/subcommands.go @@ -33,6 +33,13 @@ func main() { appName := args.Arg(0) err = cron.CommandReport(appName, *format, infoFlag) } + case "run": + args := flag.NewFlagSet("cron:run", flag.ExitOnError) + detached := args.Bool("detach", false, "--detach: run the container in a detached mode") + args.Parse(os.Args[2:]) + appName := args.Arg(0) + cronID := args.Arg(1) + err = cron.CommandRun(appName, cronID, *detached) default: err = fmt.Errorf("Invalid plugin subcommand call: %s", subcommand) } diff --git a/plugins/cron/subcommands.go b/plugins/cron/subcommands.go index b34bf758b..aa6af1c0e 100644 --- a/plugins/cron/subcommands.go +++ b/plugins/cron/subcommands.go @@ -3,9 +3,12 @@ package cron import ( "encoding/json" "fmt" + "os" "github.com/dokku/dokku/plugins/common" + "github.com/ryanuber/columnize" + "mvdan.cc/sh/v3/shell" ) // CommandList lists all scheduled cron tasks for a given app @@ -64,3 +67,48 @@ func CommandReport(appName string, format string, infoFlag string) error { return ReportSingleApp(appName, format, infoFlag) } + +// CommandRun executes a cron command on the fly +func CommandRun(appName string, cronID string, detached bool) error { + if err := common.VerifyAppName(appName); err != nil { + return err + } + + entries, err := FetchCronEntries(appName) + if err != nil { + return err + } + + if cronID == "" { + return fmt.Errorf("Please specify a Cron ID from the output of 'dokku cron:list %s'", appName) + } + + command := "" + for _, entry := range entries { + if entry.ID == cronID { + command = entry.Command + } + } + + if command == "" { + return fmt.Errorf("No matching Cron ID found. Please specify a Cron ID from the output of 'dokku cron:list %s'", appName) + } + + fields, err := shell.Fields(command, func(name string) string { + return "" + }) + if err != nil { + return fmt.Errorf("Could not parse command: %s", err) + } + + if detached { + os.Setenv("DOKKU_DETACH_CONTAINER", "1") + os.Setenv("DOKKU_DISABLE_TTY", "true") + } + + os.Setenv("DOKKU_CRON_ID", cronID) + os.Setenv("DOKKU_RM_CONTAINER", "1") + scheduler := common.GetAppScheduler(appName) + args := append([]string{scheduler, appName, "0", ""}, fields...) + return common.PlugnTrigger("scheduler-run", args...) +} diff --git a/plugins/scheduler-docker-local/go.mod b/plugins/scheduler-docker-local/go.mod index 8e565bfe1..b1f47636a 100644 --- a/plugins/scheduler-docker-local/go.mod +++ b/plugins/scheduler-docker-local/go.mod @@ -18,6 +18,7 @@ require ( github.com/robfig/cron/v3 v3.0.1 // indirect github.com/ryanuber/columnize v2.1.2+incompatible // indirect golang.org/x/sys v0.8.0 // indirect + mvdan.cc/sh/v3 v3.7.0 // indirect ) replace github.com/dokku/dokku/plugins/app-json => ../app-json diff --git a/plugins/scheduler-docker-local/go.sum b/plugins/scheduler-docker-local/go.sum index 085ca0d9e..3a0b55f7b 100644 --- a/plugins/scheduler-docker-local/go.sum +++ b/plugins/scheduler-docker-local/go.sum @@ -2,9 +2,12 @@ github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 h1:sDMmm+q/3+Bu github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= github.com/codeskyblue/go-sh v0.0.0-20190412065543-76bd3d59ff27 h1:HHUr4P/aKh4quafGxDT9LDasjGdlGkzLbfmmrlng3kA= github.com/codeskyblue/go-sh v0.0.0-20190412065543-76bd3d59ff27/go.mod h1:VQx0hjo2oUeQkQUET7wRwradO6f+fN5jzXgB/zROxxE= +github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= @@ -13,6 +16,7 @@ github.com/otiai10/copy v1.12.0/go.mod h1:rSaLseMUsZFFbsFGc7wCJnnkTAvdc5L6VWxPE4 github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= +github.com/rogpeppe/go-internal v1.10.1-0.20230524175051-ec119421bb97 h1:3RPlVWzZ/PDqmVuf/FKHARG5EMid/tl7cv54Sw/QRVY= github.com/ryanuber/columnize v2.1.2+incompatible h1:C89EOx/XBWwIXl8wm8OPJBd7kPF25UfsK2X7Ph/zCAk= github.com/ryanuber/columnize v2.1.2+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= @@ -22,3 +26,5 @@ golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +mvdan.cc/sh/v3 v3.7.0 h1:lSTjdP/1xsddtaKfGg7Myu7DnlHItd3/M2tomOcNNBg= +mvdan.cc/sh/v3 v3.7.0/go.mod h1:K2gwkaesF/D7av7Kxl0HbF5kGOd2ArupNTX3X44+8l8= diff --git a/tests/apps/python/task.py b/tests/apps/python/task.py index 6d5436600..01b998d12 100644 --- a/tests/apps/python/task.py +++ b/tests/apps/python/task.py @@ -1,3 +1,5 @@ +import sys + def main(args): print(args) diff --git a/tests/unit/cron.bats b/tests/unit/cron.bats index 4349027bc..a7a2e409c 100644 --- a/tests/unit/cron.bats +++ b/tests/unit/cron.bats @@ -78,7 +78,7 @@ teardown() { } @test "(cron) create [single-verbose]" { - run deploy_app python dokku@dokku.me:$TEST_APP template_cron_file_valid + run deploy_app python dokku@dokku.me:$TEST_APP template_cron_file_valid_single echo "output: $output" echo "status: $status" assert_success @@ -197,6 +197,39 @@ teardown() { assert_output_exists } +@test "(cron) cron:run" { + run deploy_app python dokku@dokku.me:$TEST_APP template_cron_file_valid + echo "output: $output" + echo "status: $status" + assert_success + + cron_id="$(dokku cron:list $TEST_APP --format json | jq -r '.[0].id')" + run /bin/bash -c "echo $cron_id" + echo "output: $output" + echo "status: $status" + assert_success + assert_output_exists + + run /bin/bash -c "dokku cron:run $TEST_APP $cron_id" + echo "output: $output" + echo "status: $status" + assert_success + assert_output "['task.py', 'schedule']" + + cron_id="$(dokku cron:list $TEST_APP --format json | jq -r '.[1].id')" + run /bin/bash -c "echo $cron_id" + echo "output: $output" + echo "status: $status" + assert_success + assert_output_exists + + run /bin/bash -c "dokku cron:run $TEST_APP $cron_id" + echo "output: $output" + echo "status: $status" + assert_success + assert_output "['task.py', 'schedule', 'now']" +} + template_cron_file_invalid() { local APP="$1" local APP_REPO_DIR="$2" @@ -258,12 +291,32 @@ template_cron_file_valid() { { "command": "python task.py schedule", "schedule": "5 5 5 5 5" + }, + { + "command": "python task.py schedule now", + "schedule": "6 5 5 5 5" } ] } EOF } +template_cron_file_valid_single() { + local APP="$1" + local APP_REPO_DIR="$2" + [[ -z "$APP" ]] && local APP="$TEST_APP" + echo "injecting valid cron app.json -> $APP_REPO_DIR/app.json" + cat <"$APP_REPO_DIR/app.json" +{ + "cron": [ + { + "command": "python task.py schedule", + "schedule": "5 5 5 5 5" + } + ] +} +EOF +} template_cron_file_valid_short() { local APP="$1" local APP_REPO_DIR="$2" From 9bd6e17903a1c287cc8a58427b979c7b2d083e1f Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 2 Jul 2023 15:38:19 -0400 Subject: [PATCH 18/61] docs: update migration guide to point out other important changes --- docs/appendices/0.31.0-migration-guide.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/appendices/0.31.0-migration-guide.md b/docs/appendices/0.31.0-migration-guide.md index 98835f867..cd0dabd1e 100644 --- a/docs/appendices/0.31.0-migration-guide.md +++ b/docs/appendices/0.31.0-migration-guide.md @@ -3,5 +3,13 @@ ## Changes - Herokuish build cache is now mounted from a docker volume - eg. `cache-node-js-app` - instead of the local filesystem. All existing app cache will be cleared upon upgrading past 0.29.0. -- The `proxy:ports*` commands have been replaced with the new `ports` plugin. Users will be able to use the old `proxy:ports*` commands for a single minor release, and they will be removed in the next minor release. - The `vector` container integration now mounts config to `/etc/vector` instead of the path `/etc/vector/vector.json`, allowing users the ability to provide extra configuration for Vector Sinks. To take advantage of the new functionality, the vector container should be stopped (via `dokku logs:vector-stop`) and then started (via `dokku logs:vector-start`). +- The `traefik` integration now mounts config to `/data` instead of the path `/acme.json`, fixing permissions issues under certain architectures. To take advantage of the new functionality, the traefik container should be stopped (via `dokku traefik:stop`) and then started (via `dokku traefik:start`). + +## Deprecations + +- The `proxy:ports*` commands have been replaced with the new `ports` plugin. Users will be able to use the old `proxy:ports*` commands for a single minor release, and they will be removed in the next minor release. + +## Un-Deprecations + +- The bare `apps` and `config` cli aliases are no longer deprecated. This better follows Heroku's output and is more useful to users. The subcommands will be treated as the primary, documented versions, while the aliases are there for convenience. From 914475f3e481fedaa19cea0d196a77e31fc22e5f Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sat, 8 Jul 2023 15:23:29 -0400 Subject: [PATCH 19/61] refactor: use ports-get plugn trigger for fetching port mappings This removes use of ports internals from other plugins, which will make it easier to refactor the underlying property storage. --- docs/development/plugin-triggers.md | 10 ++++++++++ plugins/20_events/ports-get | 1 + plugins/caddy-vhosts/docker-args-process-deploy | 7 +++---- plugins/domains/internal-functions | 8 ++++---- plugins/haproxy-vhosts/docker-args-process-deploy | 7 +++---- plugins/nginx-vhosts/functions | 12 +++++------- plugins/ports/Makefile | 2 +- plugins/ports/src/triggers/triggers.go | 3 +++ plugins/ports/triggers.go | 15 +++++++++++++++ plugins/traefik-vhosts/docker-args-process-deploy | 7 +++---- 10 files changed, 48 insertions(+), 24 deletions(-) create mode 120000 plugins/20_events/ports-get diff --git a/docs/development/plugin-triggers.md b/docs/development/plugin-triggers.md index 91e8887c6..48f5d2436 100644 --- a/docs/development/plugin-triggers.md +++ b/docs/development/plugin-triggers.md @@ -1226,6 +1226,16 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x nginx -t ``` +### `ports-get` +- Description: Returns a list of port mappings, newline delimited +- Invoked by: Various networking plugins +- Arguments `$APP` +```shell +#!/usr/bin/env bash +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +# TODO +``` + ### `post-app-clone` - Description: Allows you to run commands after an app was cloned. diff --git a/plugins/20_events/ports-get b/plugins/20_events/ports-get new file mode 120000 index 000000000..5178a749f --- /dev/null +++ b/plugins/20_events/ports-get @@ -0,0 +1 @@ +hook \ No newline at end of file diff --git a/plugins/caddy-vhosts/docker-args-process-deploy b/plugins/caddy-vhosts/docker-args-process-deploy index 4fe047894..1d1fdd28e 100755 --- a/plugins/caddy-vhosts/docker-args-process-deploy +++ b/plugins/caddy-vhosts/docker-args-process-deploy @@ -8,7 +8,7 @@ trigger-caddy-vhosts-docker-args-process-deploy() { declare desc="nginx-vhosts core-post-deploy plugin trigger" declare trigger="docker-args-process-deploy" declare APP="$1" IMAGE_SOURCE_TYPE="$2" IMAGE_TAG="$3" PROC_TYPE="$4" CONTAINER_INDEX="$5" - local app_domains caddy_domains is_app_listening letsencrypt_email output proxy_container_port proxy_host_port port_map proxy_port_map proxy_scheme proxy_schemes scheme tls_internal + local app_domains caddy_domains is_app_listening letsencrypt_email output proxy_container_port proxy_host_port port_map proxy_scheme proxy_schemes scheme tls_internal local proxy_container_http_port proxy_container_http_port_candidate proxy_host_http_port_candidate local proxy_container_https_port proxy_container_https_port_candidate proxy_host_https_port_candidate local STDIN=$(cat) @@ -37,8 +37,7 @@ trigger-caddy-vhosts-docker-args-process-deploy() { # so this block parses the port mappings and tries to find the correct # mapping to expose is_app_listening="false" - proxy_port_map="$(plugn trigger config-get "$APP" DOKKU_PROXY_PORT_MAP)" - for port_map in $proxy_port_map; do + while read -r port_map; do proxy_scheme="$(awk -F ':' '{ print $1 }' <<<"$port_map")" proxy_host_port="$(awk -F ':' '{ print $2 }' <<<"$port_map")" proxy_container_port="$(awk -F ':' '{ print $3 }' <<<"$port_map")" @@ -66,7 +65,7 @@ trigger-caddy-vhosts-docker-args-process-deploy() { proxy_container_https_port="$proxy_container_port" fi fi - done + done < <(plugn trigger ports-get "$APP") letsencrypt_email="$(fn-caddy-letsencrypt-email)" if [[ -n "$letsencrypt_email" ]] && [[ -z "$proxy_container_https_port" ]]; then diff --git a/plugins/domains/internal-functions b/plugins/domains/internal-functions index 921ac9885..f897c4442 100755 --- a/plugins/domains/internal-functions +++ b/plugins/domains/internal-functions @@ -136,7 +136,7 @@ fn-domains-generate-urls() { fn-domains-generate-urls-from-config() { declare APP="$1" SCHEME="$2" VHOST="$3" DEFAULT_LISTEN_PORT="$4" - local DOKKU_PROXY_PORT_MAP=$(plugn trigger config-get "$APP" DOKKU_PROXY_PORT_MAP || true) + local APP_PORT_MAP="$(plugn trigger ports-get "$APP")" local RAW_TCP_PORTS="$(get_app_raw_tcp_ports "$APP")" if [[ "$(plugn trigger proxy-is-enabled "$APP")" == "false" ]]; then @@ -155,13 +155,13 @@ fn-domains-generate-urls-from-config() { done shopt -u nullglob fi - elif [[ -n "$DOKKU_PROXY_PORT_MAP" ]]; then + elif [[ -n "$APP_PORT_MAP" ]]; then local port_map - for port_map in $DOKKU_PROXY_PORT_MAP; do + while IFS= read -r port_map; do local scheme="$(awk -F ':' '{ print $1 }' <<<"$port_map")" local listen_port="$(awk -F ':' '{ print $2 }' <<<"$port_map")" fn-domains-generate-url "$SCHEME" "$VHOST" "$listen_port" - done + done <<<"$APP_PORT_MAP" elif [[ -n "$RAW_TCP_PORTS" ]]; then for listen_port in $RAW_TCP_PORTS; do fn-domains-generate-url "$SCHEME" "$VHOST" "$listen_port" diff --git a/plugins/haproxy-vhosts/docker-args-process-deploy b/plugins/haproxy-vhosts/docker-args-process-deploy index f08713a5e..9546d7280 100755 --- a/plugins/haproxy-vhosts/docker-args-process-deploy +++ b/plugins/haproxy-vhosts/docker-args-process-deploy @@ -8,7 +8,7 @@ trigger-haproxy-vhosts-docker-args-process-deploy() { declare desc="nginx-vhosts core-post-deploy plugin trigger" declare trigger="docker-args-process-deploy" declare APP="$1" IMAGE_SOURCE_TYPE="$2" IMAGE_TAG="$3" PROC_TYPE="$4" CONTAINER_INDEX="$5" - local app_domains haproxy_domains is_app_listening letsencrypt_email output proxy_container_port proxy_host_port port_map proxy_port_map proxy_scheme proxy_schemes scheme tls_internal + local app_domains haproxy_domains is_app_listening letsencrypt_email output proxy_container_port proxy_host_port port_map proxy_scheme proxy_schemes scheme tls_internal local proxy_container_http_port proxy_container_http_port_candidate proxy_host_http_port_candidate local proxy_container_https_port proxy_container_https_port_candidate proxy_host_https_port_candidate proxy_redirect_ssl local STDIN=$(cat) @@ -40,8 +40,7 @@ trigger-haproxy-vhosts-docker-args-process-deploy() { # so this block parses the port mappings and tries to find the correct # mapping to expose is_app_listening="false" - proxy_port_map="$(plugn trigger config-get "$APP" DOKKU_PROXY_PORT_MAP)" - for port_map in $proxy_port_map; do + while read -r port_map; do proxy_scheme="$(awk -F ':' '{ print $1 }' <<<"$port_map")" proxy_host_port="$(awk -F ':' '{ print $2 }' <<<"$port_map")" proxy_container_port="$(awk -F ':' '{ print $3 }' <<<"$port_map")" @@ -69,7 +68,7 @@ trigger-haproxy-vhosts-docker-args-process-deploy() { proxy_container_https_port="$proxy_container_port" fi fi - done + done < <(plugn trigger ports-get "$APP") letsencrypt_email="$(fn-haproxy-letsencrypt-email)" if [[ -n "$letsencrypt_email" ]] && [[ -z "$proxy_container_https_port" ]]; then diff --git a/plugins/nginx-vhosts/functions b/plugins/nginx-vhosts/functions index 1e3d2f1e2..7b2238347 100755 --- a/plugins/nginx-vhosts/functions +++ b/plugins/nginx-vhosts/functions @@ -321,10 +321,10 @@ nginx_build_config() { plugn trigger proxy-configure-ports "$APP" local PROXY_PORT=$(config_get "$APP" DOKKU_PROXY_PORT) local PROXY_SSL_PORT=$(config_get "$APP" DOKKU_PROXY_SSL_PORT) - local PROXY_PORT_MAP=$(config_get "$APP" DOKKU_PROXY_PORT_MAP) + local PROXY_PORT_MAP=$(plugn trigger ports-get "$APP") - local PORT_MAP proxy_port_map - for PORT_MAP in $PROXY_PORT_MAP; do + local PORT_MAP PROXY_PORT_MAP proxy_port_map + while read -r PORT_MAP; do local PROXY_UPSTREAM_SCHEME="$(awk -F ':' '{ print $1 }' <<<"$PORT_MAP")" if [[ "$PROXY_UPSTREAM_SCHEME" == "https" ]] && [[ "$IS_SSL_ENABLED" == "false" ]]; then dokku_log_warn "Ignoring detected https port mapping without an accompanying ssl certificate (${PORT_MAP})" @@ -337,8 +337,8 @@ nginx_build_config() { if [[ "$(is_val_in_list "$PROXY_UPSTREAM_PORT" "$PROXY_UPSTREAM_PORTS" " ")" == "false" ]]; then local PROXY_UPSTREAM_PORTS+="$PROXY_UPSTREAM_PORT " fi - done - PROXY_PORT_MAP="$proxy_port_map" + done < <(plugn trigger ports-get "$APP") + PROXY_PORT_MAP="$(echo "$proxy_port_map" | xargs)" # trailing spaces mess up default template local PROXY_UPSTREAM_PORTS="$(echo "$PROXY_UPSTREAM_PORTS" | xargs)" local SSL_INUSE= @@ -379,8 +379,6 @@ nginx_build_config() { HTTP2_PUSH_SUPPORTED="$(is_http2_push_enabled "$NGINX_VERSION")" GRPC_SUPPORTED="$(is_grpc_enabled "$NGINX_VERSION")" - PROXY_PORT_MAP=$(echo "$PROXY_PORT_MAP" | xargs) # trailing spaces mess up default template - local NGINX_LOG_ROOT="$(fn-nginx-log-root)" local NGINX_ACCESS_LOG_FORMAT="$(fn-nginx-access-log-format "$APP")" local NGINX_ACCESS_LOG_PATH="$(fn-nginx-access-log-path "$APP")" diff --git a/plugins/ports/Makefile b/plugins/ports/Makefile index 2149ff854..8c519e82e 100644 --- a/plugins/ports/Makefile +++ b/plugins/ports/Makefile @@ -1,5 +1,5 @@ SUBCOMMANDS = subcommands/list subcommands/add subcommands/clear subcommands/remove subcommands/set subcommands/report -TRIGGERS = triggers/post-certs-remove triggers/post-certs-update triggers/report +TRIGGERS = triggers/ports-get triggers/post-certs-remove triggers/post-certs-update triggers/report BUILD = commands subcommands triggers PLUGIN_NAME = ports diff --git a/plugins/ports/src/triggers/triggers.go b/plugins/ports/src/triggers/triggers.go index f159dbf57..ae0f68c4d 100644 --- a/plugins/ports/src/triggers/triggers.go +++ b/plugins/ports/src/triggers/triggers.go @@ -18,6 +18,9 @@ func main() { var err error switch trigger { + case "ports-get": + appName := flag.Arg(0) + err = ports.TriggerPortsGet(appName) case "post-certs-remove": appName := flag.Arg(0) err = ports.TriggerPostCertsRemove(appName) diff --git a/plugins/ports/triggers.go b/plugins/ports/triggers.go index aa209200a..0139ee3bd 100644 --- a/plugins/ports/triggers.go +++ b/plugins/ports/triggers.go @@ -1,9 +1,24 @@ package ports import ( + "fmt" + "github.com/dokku/dokku/plugins/config" ) +// TriggerPortsGet prints out the port mapping for a given app +func TriggerPortsGet(appName string) error { + for _, portMap := range getPortMaps(appName) { + if portMap.AllowsPersistence() { + continue + } + + fmt.Println(portMap) + } + + return nil +} + // TriggerPostCertsRemove unsets port config vars after SSL cert is added func TriggerPostCertsRemove(appName string) error { keys := []string{"DOKKU_PROXY_SSL_PORT"} diff --git a/plugins/traefik-vhosts/docker-args-process-deploy b/plugins/traefik-vhosts/docker-args-process-deploy index c81248cea..4f09b45cd 100755 --- a/plugins/traefik-vhosts/docker-args-process-deploy +++ b/plugins/traefik-vhosts/docker-args-process-deploy @@ -8,7 +8,7 @@ trigger-traefik-vhosts-docker-args-process-deploy() { declare desc="nginx-vhosts core-post-deploy plugin trigger" declare trigger="docker-args-process-deploy" declare APP="$1" IMAGE_SOURCE_TYPE="$2" IMAGE_TAG="$3" PROC_TYPE="$4" CONTAINER_INDEX="$5" - local app_domains is_app_listening letsencrypt_email output proxy_container_port proxy_host_port port_map proxy_port_map priority proxy_scheme proxy_schemes traefik_domains + local app_domains is_app_listening letsencrypt_email output proxy_container_port proxy_host_port port_map priority proxy_scheme proxy_schemes traefik_domains local proxy_container_http_port proxy_container_http_port_candidate proxy_host_http_port_candidate local proxy_container_https_port proxy_container_https_port_candidate proxy_host_https_port_candidate local STDIN=$(cat) @@ -37,8 +37,7 @@ trigger-traefik-vhosts-docker-args-process-deploy() { # so this block parses the port mappings and tries to find the correct # mapping to expose is_app_listening="false" - proxy_port_map="$(plugn trigger config-get "$APP" DOKKU_PROXY_PORT_MAP)" - for port_map in $proxy_port_map; do + while read -r port_map; do proxy_scheme="$(awk -F ':' '{ print $1 }' <<<"$port_map")" proxy_host_port="$(awk -F ':' '{ print $2 }' <<<"$port_map")" proxy_container_port="$(awk -F ':' '{ print $3 }' <<<"$port_map")" @@ -66,7 +65,7 @@ trigger-traefik-vhosts-docker-args-process-deploy() { proxy_container_https_port="$proxy_container_port" fi fi - done + done < <(plugn trigger ports-get "$APP") letsencrypt_email="$(fn-traefik-letsencrypt-email)" if [[ -n "$letsencrypt_email" ]] && [[ -z "$proxy_container_https_port" ]]; then From 11d9ed6724aad06af951fd886ae6a521da7b062d Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sat, 8 Jul 2023 16:56:14 -0400 Subject: [PATCH 20/61] tests: use ports:report to get the list of port mappings --- tests/unit/test_helper.bash | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_helper.bash b/tests/unit/test_helper.bash index 657567b01..fdcd3cb3a 100644 --- a/tests/unit/test_helper.bash +++ b/tests/unit/test_helper.bash @@ -276,7 +276,7 @@ assert_url() { echo "VHOST: $(cat $DOKKU_ROOT/$TEST_APP/VHOST | xargs)" echo "tls: $(ls $DOKKU_ROOT/$TEST_APP/tls || true)" echo "proxy-is-enabled: $(dokku plugin:trigger proxy-is-enabled "$TEST_APP")" - echo "port-map: $(dokku config:get "$TEST_APP" DOKKU_PROXY_PORT_MAP)" + echo "port-map: $(dokku ports:report "$TEST_APP" --ports-map)" echo "url: $(dokku urls $TEST_APP)" echo "output: $output" echo "status: $status" @@ -290,7 +290,7 @@ assert_urls() { echo "VHOST: $(cat $DOKKU_ROOT/$TEST_APP/VHOST | xargs)" echo "tls: $(ls $DOKKU_ROOT/$TEST_APP/tls || true)" echo "proxy-is-enabled: $(dokku plugin:trigger proxy-is-enabled "$TEST_APP")" - echo "port-map: $(dokku config:get "$TEST_APP" DOKKU_PROXY_PORT_MAP)" + echo "port-map: $(dokku ports:report "$TEST_APP" --ports-map)" echo "urls: $(dokku urls $TEST_APP)" echo "output: $output" echo "status: $status" From b8680f7de77de82d30bd9aba49b93561a0234ebf Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sat, 8 Jul 2023 17:12:23 -0400 Subject: [PATCH 21/61] fix: automatically clear the git source-image property when changing deployment methodologies This ensures users don't have issues with cached state in regards to files pulled from the repo/image. Also add missing docs for deploy-source-set trigger. Closes #5963 --- docs/appendices/0.31.0-migration-guide.md | 1 + docs/deployment/methods/git.md | 26 ----------------------- docs/development/plugin-triggers.md | 15 +++++++++++++ plugins/git/deploy-source-set | 18 ++++++++++++++++ 4 files changed, 34 insertions(+), 26 deletions(-) create mode 100755 plugins/git/deploy-source-set diff --git a/docs/appendices/0.31.0-migration-guide.md b/docs/appendices/0.31.0-migration-guide.md index cd0dabd1e..b4ff4a824 100644 --- a/docs/appendices/0.31.0-migration-guide.md +++ b/docs/appendices/0.31.0-migration-guide.md @@ -5,6 +5,7 @@ - Herokuish build cache is now mounted from a docker volume - eg. `cache-node-js-app` - instead of the local filesystem. All existing app cache will be cleared upon upgrading past 0.29.0. - The `vector` container integration now mounts config to `/etc/vector` instead of the path `/etc/vector/vector.json`, allowing users the ability to provide extra configuration for Vector Sinks. To take advantage of the new functionality, the vector container should be stopped (via `dokku logs:vector-stop`) and then started (via `dokku logs:vector-start`). - The `traefik` integration now mounts config to `/data` instead of the path `/acme.json`, fixing permissions issues under certain architectures. To take advantage of the new functionality, the traefik container should be stopped (via `dokku traefik:stop`) and then started (via `dokku traefik:start`). +- Users no longer need to clear the `source-image` git property when transitioning from image-based deploys (`git:from-image` and `git:load-image`) to other deployment methods (git push, `git:from-archive`, `git:sync`). ## Deprecations diff --git a/docs/deployment/methods/git.md b/docs/deployment/methods/git.md index 16b98a72f..e69d17aaa 100644 --- a/docs/deployment/methods/git.md +++ b/docs/deployment/methods/git.md @@ -160,19 +160,6 @@ dokku git:from-image node-js-app dokku/node-js-getting-started:latest "Camila" " If the image is a private image that requires a docker login to access, the `registry:login` command should be used to log into the registry. See the [registry documentation](/docs/advanced-usage/registry-management.md#logging-into-a-registry) for more details on this process. -Building an app from an image will result in the following files being extracted from the source image (with all custom paths specified for each file being respected): - -- `app.json` -- `nginx.conf.sigil` -- `Procfile` - -In the case where the repository is later modified to manually add any of the above files and deployed via `git push`, the files will still be extracted from the initial source image. To avoid this, please clear the `source-image` git property. It will be set back to the original source image on any subsequent `git:from-image` calls. - -```shell -# sets an empty value -dokku git:set node-js-app source-image -``` - Finally, certain images may require a custom build context in order for `ONBUILD ADD` and `ONBUILD COPY` statements to succeed. A custom build context can be specified via the `--build-dir` flag. All files in the specified `build-dir` will be copied into the repository for use within the `docker build` process. The build context _must_ be specified on each deploy, and is not otherwise persisted between builds. ```shell @@ -248,19 +235,6 @@ The `git:load-image` command can optionally take a git `user.name` and `user.ema docker image save dokku/node-js-getting-started:latest | ssh dokku@dokku.me git:load-image node-js-app dokku/node-js-getting-started:latest "Camila" "camila@example.com" ``` -Building an app from an image will result in the following files being extracted from the source image (with all custom paths specified for each file being respected): - -- `app.json` -- `nginx.conf.sigil` -- `Procfile` - -In the case where the repository is later modified to manually add any of the above files and deployed via `git push`, the files will still be extracted from the initial source image. To avoid this, please clear the `source-image` git property. It will be set back to the original source image on any subsequent `git:load-image` calls. - -```shell -# sets an empty value -dokku git:set node-js-app source-image -``` - Finally, certain images may require a custom build context in order for `ONBUILD ADD` and `ONBUILD COPY` statements to succeed. A custom build context can be specified via the `--build-dir` flag. All files in the specified `build-dir` will be copied into the repository for use within the `docker build` process. The build context _must_ be specified on each deploy, and is not otherwise persisted between builds. ```shell diff --git a/docs/development/plugin-triggers.md b/docs/development/plugin-triggers.md index 48f5d2436..72364ae61 100644 --- a/docs/development/plugin-triggers.md +++ b/docs/development/plugin-triggers.md @@ -599,6 +599,21 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x echo 'derp.dkr.ecr.us-east-1.amazonaws.com' ``` +### `deploy-source-set` + +- Description: Used to set metadata about how the app is being deployed +- Invoked by: `git:from-archive`, `git:from-image`, `git:load-image`, `git:sync`, and all git push commands +- Arguments: `$APP $SOURCE_TYPE $METADATA` +- Example: + +```shell +#!/usr/bin/env bash + +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x + +# TODO +``` + ### `docker-args-build` > Warning: Deprecated, please use `docker-args-process-build` instead diff --git a/plugins/git/deploy-source-set b/plugins/git/deploy-source-set new file mode 100755 index 000000000..2828f6e2f --- /dev/null +++ b/plugins/git/deploy-source-set @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +trigger-git-deploy-source-set() { + declare desc="clears the source-image if the source type is not docker" + declare trigger="deploy-source-set" + declare APP="$1" SOURCE_TYPE="$2" + + if [[ "$SOURCE_TYPE" == "docker-image" ]]; then + return + fi + + fn-plugin-property-write "git" "$APP" "source-image" +} + +trigger-git-deploy-source-set "$@" From 94f4d1325c17c4d5b8407810541dc6e02a305444 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sat, 1 Jul 2023 06:45:21 -0400 Subject: [PATCH 22/61] feat: export environment variables during dockerfile builds for use with value-less --build-arg flags Exposing all config values will allow users to skip setting environment variables twice - once as a docker option and once as an env var. Docker will automatically pull the value from the environment if none is set for the --build-arg flag. Users will still be required to specify each desired env var via --build-arg as otherwise docker builds will complain about unused build arguments. Re-implements #5978 (lost in a rebase) Closes #5903 --- docs/deployment/builders/dockerfiles.md | 4 ++-- plugins/builder-dockerfile/builder-build | 1 + tests/unit/builder-dockerfile.bats | 18 ++++++++++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/docs/deployment/builders/dockerfiles.md b/docs/deployment/builders/dockerfiles.md index 12874f1b5..7e060e40c 100644 --- a/docs/deployment/builders/dockerfiles.md +++ b/docs/deployment/builders/dockerfiles.md @@ -114,10 +114,10 @@ Dockerfile2 For security reasons - and as per [Docker recommendations](https://github.com/docker/docker/issues/13490) - Dockerfile-based deploys have variables available only during runtime. -For users that require customization in the `build` phase, you may use build arguments via the [docker-options plugin](/docs/advanced-usage/docker-options.md): +For users that require customization in the `build` phase, you may use build arguments via the [docker-options plugin](/docs/advanced-usage/docker-options.md). All environment variables set by the `config` plugin are automatically exported during a docker build, and thus `--build-arg` only requires setting a key without a value. ```shell -dokku docker-options:add node-js-app build '--build-arg NODE_ENV=production' +dokku docker-options:add node-js-app build '--build-arg NODE_ENV' ``` Once set, the Dockerfile usage would be as follows: diff --git a/plugins/builder-dockerfile/builder-build b/plugins/builder-dockerfile/builder-build index 4b57522ab..c733c4480 100755 --- a/plugins/builder-dockerfile/builder-build +++ b/plugins/builder-dockerfile/builder-build @@ -38,6 +38,7 @@ trigger-builder-dockerfile-builder-build() { declare -a ARG_ARRAY eval "ARG_ARRAY=($DOCKER_ARGS)" + eval "$(config_export app "$APP")" "$DOCKER_BIN" image build "${DOCKER_BUILD_LABEL_ARGS[@]}" $DOKKU_GLOBAL_BUILD_ARGS "${ARG_ARRAY[@]}" ${DOKKU_DOCKER_BUILD_OPTS} -t $IMAGE . plugn trigger post-build-dockerfile "$APP" diff --git a/tests/unit/builder-dockerfile.bats b/tests/unit/builder-dockerfile.bats index b9138e16f..c770138fc 100644 --- a/tests/unit/builder-dockerfile.bats +++ b/tests/unit/builder-dockerfile.bats @@ -48,3 +48,21 @@ teardown() { assert_success assert_output_contains 'echo hi' 0 } + +@test "(builder-dockerfile) config export" { + run /bin/bash -c "dokku config:set $TEST_APP GITHUB_TOKEN=custom-value" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku docker-options:add $TEST_APP build '--build-arg GITHUB_TOKEN'" + echo "output: $output" + echo "status: $status" + assert_success + + run deploy_app dockerfile + echo "output: $output" + echo "status: $status" + assert_success + assert_output_contains "TOKEN is: custom-value" +} From fb9d77878dd65f096fe6bfff2a95c7a77b01e492 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 2 Jul 2023 15:34:24 -0400 Subject: [PATCH 23/61] refactor: move CHECKS file extraction to the beginning of the deploy This makes deploys faster for any that are built from source as the image artifact isn't involved in those cases while also unifying how the file is handled. Refs #2760 --- docs/appendices/0.31.0-migration-guide.md | 1 + docs/deployment/zero-downtime-deploys.md | 4 +- plugins/scheduler-docker-local/check-deploy | 20 ++---- .../scheduler-docker-local/core-post-deploy | 12 ++++ .../scheduler-docker-local/core-post-extract | 70 +++++++++++++++++++ .../scheduler-docker-local/internal-functions | 38 ++++++++++ plugins/scheduler-docker-local/pre-deploy | 11 ++- 7 files changed, 130 insertions(+), 26 deletions(-) create mode 100755 plugins/scheduler-docker-local/core-post-extract diff --git a/docs/appendices/0.31.0-migration-guide.md b/docs/appendices/0.31.0-migration-guide.md index b4ff4a824..f873a9da1 100644 --- a/docs/appendices/0.31.0-migration-guide.md +++ b/docs/appendices/0.31.0-migration-guide.md @@ -6,6 +6,7 @@ - The `vector` container integration now mounts config to `/etc/vector` instead of the path `/etc/vector/vector.json`, allowing users the ability to provide extra configuration for Vector Sinks. To take advantage of the new functionality, the vector container should be stopped (via `dokku logs:vector-stop`) and then started (via `dokku logs:vector-start`). - The `traefik` integration now mounts config to `/data` instead of the path `/acme.json`, fixing permissions issues under certain architectures. To take advantage of the new functionality, the traefik container should be stopped (via `dokku traefik:stop`) and then started (via `dokku traefik:start`). - Users no longer need to clear the `source-image` git property when transitioning from image-based deploys (`git:from-image` and `git:load-image`) to other deployment methods (git push, `git:from-archive`, `git:sync`). +- For deploys via the `git:from-image` and `git:load-image` commands, the `CHECKS` file is now extracted from the configured `WORKDIR` property of the image. For all other deploys - git push, `git:from-archive`, `git:sync` - will have the `CHECKS` extracted directly from the source code. The filename in both cases is `CHECKS` and cannot be modified. ## Deprecations diff --git a/docs/deployment/zero-downtime-deploys.md b/docs/deployment/zero-downtime-deploys.md index c4e437797..1d6bc3f88 100644 --- a/docs/deployment/zero-downtime-deploys.md +++ b/docs/deployment/zero-downtime-deploys.md @@ -139,15 +139,13 @@ If your application needs a longer period to boot up - perhaps to load data into Checks are run against the detected `web` process from your application's `Procfile`. For non-web processes, Dokku will fallback to the aforementioned process uptime check. -To specify checks, add a `CHECKS` file to the root of your project directory. The `CHECKS` file should be plain text and may contain: +For deploys via the `git:from-image` and `git:load-image` commands, the `CHECKS` file is extracted from the configured `WORKDIR` property of the image. For all other deploys - git push, `git:from-archive`, `git:sync` - will have the `CHECKS` extracted directly from the source code. The filename in both cases is `CHECKS` and cannot be modified. The `CHECKS` file should be plain text and may contain: - check instructions - settings (NAME=VALUE) - comments (lines starting with #) - empty lines -> For Dockerfile and Docker Image based deploys, the file *must* be in the `WORKDIR` directory of the built image. `/app` is used by default as the root container directory for buildpack-based deploys. - ### Check instructions The format of a check instruction is a path or relative URL, optionally followed by the expected content: diff --git a/plugins/scheduler-docker-local/check-deploy b/plugins/scheduler-docker-local/check-deploy index 363b0c168..678ae2677 100755 --- a/plugins/scheduler-docker-local/check-deploy +++ b/plugins/scheduler-docker-local/check-deploy @@ -38,6 +38,7 @@ set -eo pipefail source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" source "$PLUGIN_AVAILABLE_PATH/checks/functions" source "$PLUGIN_AVAILABLE_PATH/config/functions" +source "$PLUGIN_AVAILABLE_PATH/scheduler-docker-local/internal-functions" trigger-scheduler-docker-local-check-deploy() { declare desc="scheduler-docker-local check-deploy plugin trigger" @@ -76,16 +77,14 @@ trigger-scheduler-docker-local-check-deploy() { # use this number of retries for checks local ATTEMPTS="${DOKKU_CHECKS_ATTEMPTS:-5}" - local CHECK_DEPLOY_TMP_WORK_DIR=$(mktemp -d "/tmp/dokku-${DOKKU_PID}-${FUNCNAME[0]}.XXXXXX") - local CHECKS_FILENAME=${CHECK_DEPLOY_TMP_WORK_DIR}/CHECKS + local CHECKS_FILENAME="$(fn-scheduler-docker-local-get-process-specific-checks-file-path "$APP")" local IMAGE_TAG="$(get_running_image_tag "$APP")" local IMAGE=$(get_deploying_app_image_name "$APP" "$IMAGE_TAG") - copy_from_image "$IMAGE" "CHECKS" "$CHECKS_FILENAME" 2>/dev/null || true checks_check_deploy_cleanup() { - declare desc="cleans up CHECK_DEPLOY_TMP_WORK_DIR and print container output" + declare desc="print container output" local id="$1" - rm -rf "$CHECK_DEPLOY_TMP_WORK_DIR" &>/dev/null || true + if [[ $id ]]; then dokku_log_info2_quiet "Start of $APP container output ($DOKKU_APP_CONTAINER_TYPE.$CONTAINER_INDEX)" dokku_container_log_verbose_quiet "$id" @@ -96,8 +95,6 @@ trigger-scheduler-docker-local-check-deploy() { # We allow custom check for web instances only if [[ ! -s "${CHECKS_FILENAME}" ]] || [[ "$DOKKU_APP_CONTAINER_TYPE" != "web" ]]; then - rm -rf "$CHECK_DEPLOY_TMP_WORK_DIR" &>/dev/null || true - # simple default check to see if the container stuck around local DOKKU_DEFAULT_CHECKS_WAIT="${DOKKU_DEFAULT_CHECKS_WAIT:-10}" dokku_log_verbose "Waiting for $DOKKU_DEFAULT_CHECKS_WAIT seconds ($DOKKU_APP_CONTAINER_TYPE.$CONTAINER_INDEX)" @@ -115,15 +112,6 @@ trigger-scheduler-docker-local-check-deploy() { dokku_log_verbose "Default container check successful ($DOKKU_APP_CONTAINER_TYPE.$CONTAINER_INDEX)" && exit 0 fi - local NEW_CHECKS_FILENAME="${CHECK_DEPLOY_TMP_WORK_DIR}/NEW_CHECKS" - template_checks() { - eval "$(config_export app "$APP" --merged)" - sigil -f "$CHECKS_FILENAME" | cat -s >"$NEW_CHECKS_FILENAME" - } - - template_checks - CHECKS_FILENAME="$NEW_CHECKS_FILENAME" - # Reads name/value pairs, sets the WAIT and TIMEOUT variables exec <"$CHECKS_FILENAME" local line diff --git a/plugins/scheduler-docker-local/core-post-deploy b/plugins/scheduler-docker-local/core-post-deploy index bd02b2d44..592bd0af1 100755 --- a/plugins/scheduler-docker-local/core-post-deploy +++ b/plugins/scheduler-docker-local/core-post-deploy @@ -15,6 +15,18 @@ trigger-scheduler-docker-local-core-post-deploy() { return fi + # move over the checks file appropriately + checks_file="${DOKKU_LIB_ROOT}/data/scheduler-docker-local/$APP/CHECKS" + process_specific_checks_file="${DOKKU_LIB_ROOT}/data/scheduler-docker-local/$APP/CHECKS.$DOKKU_PID" + if [[ -f "$process_specific_checks_file" ]]; then + mv "$process_specific_checks_file" "$checks_file" + elif [[ -f "$process_specific_checks_file.missing" ]]; then + rm -f "$process_specific_checks_file.missing" + if [[ -f "$checks_file" ]]; then + rm -f "$checks_file" + fi + fi + dokku_log_info1 "Renaming containers" local PROCTYPES="$(plugn trigger ps-current-scale "$APP" | awk -F '=' '{ print $1 }' | xargs)" local CONTAINER_FILES="$(find "$DOKKU_ROOT/$APP" -maxdepth 1 -name "CONTAINER.*" -printf "%f\n" 2>/dev/null | sort -t . -k 3 -n | xargs)" diff --git a/plugins/scheduler-docker-local/core-post-extract b/plugins/scheduler-docker-local/core-post-extract new file mode 100755 index 000000000..f8809136f --- /dev/null +++ b/plugins/scheduler-docker-local/core-post-extract @@ -0,0 +1,70 @@ +#!/usr/bin/env bash +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_AVAILABLE_PATH/config/functions" +source "$PLUGIN_AVAILABLE_PATH/scheduler-docker-local/internal-functions" + +fn-scheduler-docker-local-copy-from-image() { + declare APP="$1" IMAGE_NAME="$2" CHECKS_PATH="$3" + + mkdir -p "${DOKKU_LIB_ROOT}/data/scheduler-docker-local/$APP" + rm -f "${DOKKU_LIB_ROOT}/data/scheduler-docker-local/$APP/CHECKS."* + copy_from_image "$IMAGE_NAME" "$CHECKS_PATH" "${DOKKU_LIB_ROOT}/data/scheduler-docker-local/$APP/CHECKS.$DOKKU_PID" 2>/dev/null || true + if [[ ! -f "${DOKKU_LIB_ROOT}/data/scheduler-docker-local/$APP/CHECKS.$DOKKU_PID" ]]; then + touch "${DOKKU_LIB_ROOT}/data/scheduler-docker-local/$APP/CHECKS.$DOKKU_PID.missing" + fi +} + +fn-scheduler-docker-local-copy-from-directory() { + declare APP="$1" SOURCECODE_WORK_DIR="$2" CHECKS_PATH="$3" + + pushd "$SOURCECODE_WORK_DIR" >/dev/null + mkdir -p "${DOKKU_LIB_ROOT}/data/scheduler-docker-local/$APP" + + if [[ -z "$CHECKS_PATH" ]]; then + touch "${DOKKU_LIB_ROOT}/data/scheduler-docker-local/$APP/CHECKS.$DOKKU_PID.missing" + return + fi + + if [[ ! -f "$CHECKS_PATH" ]]; then + touch "${DOKKU_LIB_ROOT}/data/scheduler-docker-local/$APP/CHECKS.$DOKKU_PID.missing" + return + fi + + rm -f "${DOKKU_LIB_ROOT}/data/scheduler-docker-local/$APP/CHECKS."* + cp -f "$CHECKS_PATH" "${DOKKU_LIB_ROOT}/data/scheduler-docker-local/$APP/CHECKS.$DOKKU_PID" + popd &>/dev/null || pushd "/tmp" >/dev/null +} + +trigger-scheduler-docker-local-core-post-extract() { + declare desc="scheduler-docker-local post-extract plugin trigger" + declare trigger="post-extract" + declare APP="$1" SOURCECODE_WORK_DIR="$2" + local CHECKS_PATH="CHECKS" CHECKS_FILENAME="${DOKKU_LIB_ROOT}/data/scheduler-docker-local/$APP/CHECKS.$DOKKU_PID" + local app_source_image + + app_source_image="$(plugn trigger git-get-property "$APP" "source-image")" + if [[ -n "$app_source_image" ]]; then + fn-scheduler-docker-local-copy-from-image "$APP" "$app_source_image" "$CHECKS_PATH" + else + fn-scheduler-docker-local-copy-from-directory "$APP" "$SOURCECODE_WORK_DIR" "$CHECKS_PATH" + fi + + if [[ -f "$CHECKS_FILENAME" ]]; then + local TMP_CHECKS_FILE=$(mktemp "/tmp/dokku-${DOKKU_PID}-${FUNCNAME[0]}.XXXXXX") + trap "rm -rf '$TMP_CHECKS_FILE' >/dev/null" RETURN INT TERM EXIT + + template_checks() { + declare desc="templates out the checks file" + declare APP="$1" CHECKS_FILENAME="$2" + eval "$(config_export app "$APP" --format exports --merged)" + sigil -f "$CHECKS_FILENAME" | cat -s >"$TMP_CHECKS_FILE" + mv "$TMP_CHECKS_FILE" "$CHECKS_FILENAME" + } + + template_checks "$APP" "$CHECKS_FILENAME" + fi +} + +trigger-scheduler-docker-local-core-post-extract "$@" diff --git a/plugins/scheduler-docker-local/internal-functions b/plugins/scheduler-docker-local/internal-functions index a8985eef2..33f16100e 100755 --- a/plugins/scheduler-docker-local/internal-functions +++ b/plugins/scheduler-docker-local/internal-functions @@ -67,6 +67,44 @@ cmd-scheduler-docker-local-report-single() { fi } +fn-scheduler-docker-local-get-checks-file-path() { + declare APP="$1" + + echo "${DOKKU_LIB_ROOT}/data/scheduler-docker-local/$APP/CHECKS" +} + +fn-scheduler-docker-local-get-process-specific-checks-file-path() { + declare APP="$1" + + checks_path="$(fn-scheduler-docker-local-get-checks-file-path "$APP")" + process_specific_checks_path="$checks_path.$DOKKU_PID" + if [[ -f "$process_specific_checks_path" ]]; then + echo "$process_specific_checks_path" + return + fi + + echo "$checks_path" +} + +fn-scheduler-docker-local-has-checks-file() { + declare APP="$1" + + checks_path="$(fn-scheduler-docker-local-get-checks-file-path "$APP")" + if [[ -f "$checks_path.$DOKKU_PID.missing" ]]; then + return 1 + fi + + if [[ -f "$checks_path.$DOKKU_PID" ]]; then + return 0 + fi + + if [[ -f "$checks_path" ]]; then + return 0 + fi + + return 1 +} + fn-scheduler-docker-local-retire-container() { declare APP="$1" CID="$2" local STATE diff --git a/plugins/scheduler-docker-local/pre-deploy b/plugins/scheduler-docker-local/pre-deploy index b6d1bb197..217ce6630 100755 --- a/plugins/scheduler-docker-local/pre-deploy +++ b/plugins/scheduler-docker-local/pre-deploy @@ -4,6 +4,7 @@ set -eo pipefail source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions" source "$PLUGIN_AVAILABLE_PATH/config/functions" +source "$PLUGIN_AVAILABLE_PATH/scheduler-docker-local/internal-functions" trigger-scheduler-docker-local-pre-deploy() { declare desc="scheduler-docker-local pre-deploy plugin trigger" @@ -16,7 +17,7 @@ trigger-scheduler-docker-local-pre-deploy() { fi scheduler-docker-local-pre-deploy-chown-app "$APP" "$IMAGE_TAG" - scheduler-docker-local-pre-deploy-precheck "$APP" "$IMAGE_TAG" + scheduler-docker-local-pre-deploy-precheck "$APP" } scheduler-docker-local-pre-deploy-chown-app() { @@ -63,14 +64,10 @@ scheduler-docker-local-pre-deploy-chown-app() { scheduler-docker-local-pre-deploy-precheck() { declare desc="Outputs the checks messages if necessary" - declare APP="$1" IMAGE_TAG="$2" - local IMAGE=$(get_app_image_name "$APP" "$IMAGE_TAG") - local CHECKS_FILE=$(mktemp "/tmp/dokku-${DOKKU_PID}-${FUNCNAME[0]}.XXXXXX") - trap "rm -rf '$CHECKS_FILE' >/dev/null" RETURN INT TERM EXIT - copy_from_image "$IMAGE" "CHECKS" "$CHECKS_FILE" 2>/dev/null || true + declare APP="$1" dokku_log_info2 "Processing deployment checks" - if [[ ! -s "${CHECKS_FILE}" ]]; then + if ! fn-scheduler-docker-local-has-checks-file "$APP"; then local CHECKS_URL="${DOKKU_CHECKS_URL:-https://dokku.com/docs/deployment/zero-downtime-deploys/}" dokku_log_verbose "No CHECKS file found. Simple container checks will be performed." dokku_log_verbose "For more efficient zero downtime deployments, create a CHECKS file. See ${CHECKS_URL} for examples" From 1431724b858280f1a6cb9b9780d8c7c6044d9b9d Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sat, 8 Jul 2023 15:13:17 -0400 Subject: [PATCH 24/61] feat: use ports-clear plugn trigger to manage clearing the port map This removes any access to the underlying implementation for port map clearing, which will make it easier to refactor the actual clearing. --- docs/deployment/builders/dockerfiles.md | 2 +- .../builders/herokuish-buildpacks.md | 2 +- docs/development/plugin-triggers.md | 20 +++++++++++++++++++ plugins/20_events/ports-clear | 1 + plugins/nginx-vhosts/pre-disable-vhost | 3 ++- plugins/nginx-vhosts/pre-enable-vhost | 3 ++- plugins/ports/.gitignore | 1 + plugins/ports/Makefile | 2 +- plugins/ports/functions.go | 7 +++++++ plugins/ports/src/triggers/triggers.go | 3 +++ plugins/ports/subcommands.go | 4 +--- plugins/ports/triggers.go | 5 +++++ 12 files changed, 45 insertions(+), 8 deletions(-) create mode 120000 plugins/20_events/ports-clear diff --git a/docs/deployment/builders/dockerfiles.md b/docs/deployment/builders/dockerfiles.md index 7e060e40c..62a1cc590 100644 --- a/docs/deployment/builders/dockerfiles.md +++ b/docs/deployment/builders/dockerfiles.md @@ -29,7 +29,7 @@ Dokku will only select the `dockerfile` builder if both the `herokuish` and `pac If an application was previously deployed via buildpacks, the following commands should be run before a Dockerfile deploy will succeed: ```shell -dokku config:unset --no-restart node-js-app DOKKU_PROXY_PORT_MAP +dokku proxy:clear node-js-app ``` ### Changing the `Dockerfile` location diff --git a/docs/deployment/builders/herokuish-buildpacks.md b/docs/deployment/builders/herokuish-buildpacks.md index 947c36b97..66ca72ec3 100644 --- a/docs/deployment/builders/herokuish-buildpacks.md +++ b/docs/deployment/builders/herokuish-buildpacks.md @@ -301,7 +301,7 @@ false If an application was previously deployed via Dockerfile, the following commands should be run before a buildpack deploy will succeed: ```shell -dokku config:unset --no-restart node-js-app DOKKU_PROXY_PORT_MAP +dokku ports:clear node-js-app ``` ### Using a specific buildpack version diff --git a/docs/development/plugin-triggers.md b/docs/development/plugin-triggers.md index 72364ae61..c31e389d9 100644 --- a/docs/development/plugin-triggers.md +++ b/docs/development/plugin-triggers.md @@ -1242,12 +1242,32 @@ nginx -t ``` ### `ports-get` + - Description: Returns a list of port mappings, newline delimited - Invoked by: Various networking plugins - Arguments `$APP` +- Example: + ```shell #!/usr/bin/env bash + set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x + +# TODO +``` + +### `ports-clear` + +- Description: Clears the ports for a given app without triggering further restarts or rebuilds +- Invoked by: internally +- Arguments: `$APP` +- Example: + +```shell +#!/usr/bin/env bash + +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x + # TODO ``` diff --git a/plugins/20_events/ports-clear b/plugins/20_events/ports-clear new file mode 120000 index 000000000..5178a749f --- /dev/null +++ b/plugins/20_events/ports-clear @@ -0,0 +1 @@ +hook \ No newline at end of file diff --git a/plugins/nginx-vhosts/pre-disable-vhost b/plugins/nginx-vhosts/pre-disable-vhost index e786e5322..ecfba935c 100755 --- a/plugins/nginx-vhosts/pre-disable-vhost +++ b/plugins/nginx-vhosts/pre-disable-vhost @@ -10,7 +10,8 @@ trigger-nginx-vhosts-pre-disable-vhost() { declare APP="$1" if [[ "$(plugn trigger proxy-type "$APP")" == "nginx" ]]; then - DOKKU_QUIET_OUTPUT=1 config_unset --no-restart "$APP" DOKKU_PROXY_PORT DOKKU_PROXY_SSL_PORT DOKKU_PROXY_PORT_MAP + DOKKU_QUIET_OUTPUT=1 config_unset --no-restart "$APP" DOKKU_PROXY_PORT DOKKU_PROXY_SSL_PORT + plugn trigger ports-clear "$APP" fi } diff --git a/plugins/nginx-vhosts/pre-enable-vhost b/plugins/nginx-vhosts/pre-enable-vhost index bde11f963..64808eb3d 100755 --- a/plugins/nginx-vhosts/pre-enable-vhost +++ b/plugins/nginx-vhosts/pre-enable-vhost @@ -10,7 +10,8 @@ trigger-nginx-vhosts-pre-enable-vhost() { declare APP="$1" if [[ "$(plugn trigger proxy-type "$APP")" == "nginx" ]]; then - DOKKU_QUIET_OUTPUT=1 config_unset --no-restart "$APP" DOKKU_PROXY_PORT DOKKU_PROXY_SSL_PORT DOKKU_PROXY_PORT_MAP + DOKKU_QUIET_OUTPUT=1 config_unset --no-restart "$APP" DOKKU_PROXY_PORT DOKKU_PROXY_SSL_PORT + plugn trigger ports-clear "$APP" fi } diff --git a/plugins/ports/.gitignore b/plugins/ports/.gitignore index 291036bd4..3fc2c0d2f 100644 --- a/plugins/ports/.gitignore +++ b/plugins/ports/.gitignore @@ -3,5 +3,6 @@ /triggers/* /triggers /install +/ports-* /post-* /report diff --git a/plugins/ports/Makefile b/plugins/ports/Makefile index 8c519e82e..09ef51751 100644 --- a/plugins/ports/Makefile +++ b/plugins/ports/Makefile @@ -1,5 +1,5 @@ SUBCOMMANDS = subcommands/list subcommands/add subcommands/clear subcommands/remove subcommands/set subcommands/report -TRIGGERS = triggers/ports-get triggers/post-certs-remove triggers/post-certs-update triggers/report +TRIGGERS = triggers/ports-clear triggers/ports-get triggers/post-certs-remove triggers/post-certs-update triggers/report BUILD = commands subcommands triggers PLUGIN_NAME = ports diff --git a/plugins/ports/functions.go b/plugins/ports/functions.go index 06fdc1641..4739c839e 100644 --- a/plugins/ports/functions.go +++ b/plugins/ports/functions.go @@ -20,6 +20,13 @@ func addPortMaps(appName string, portMaps []PortMap) error { return setPortMaps(appName, allPortMaps) } +func clearPorts(appName string) error { + return common.EnvWrap(func() error { + keys := []string{"DOKKU_PROXY_PORT_MAP"} + return config.UnsetMany(appName, keys, false) + }, map[string]string{"DOKKU_QUIET_OUTPUT": "1"}) +} + func filterAppPortMaps(appName string, scheme string, hostPort int) []PortMap { var filteredPortMaps []PortMap for _, portMap := range getPortMaps(appName) { diff --git a/plugins/ports/src/triggers/triggers.go b/plugins/ports/src/triggers/triggers.go index ae0f68c4d..d7a1f8c88 100644 --- a/plugins/ports/src/triggers/triggers.go +++ b/plugins/ports/src/triggers/triggers.go @@ -21,6 +21,9 @@ func main() { case "ports-get": appName := flag.Arg(0) err = ports.TriggerPortsGet(appName) + case "ports-clear": + appName := flag.Arg(0) + err = ports.TriggerPortsClear(appName) case "post-certs-remove": appName := flag.Arg(0) err = ports.TriggerPostCertsRemove(appName) diff --git a/plugins/ports/subcommands.go b/plugins/ports/subcommands.go index 70ebf9057..ce34e37a8 100644 --- a/plugins/ports/subcommands.go +++ b/plugins/ports/subcommands.go @@ -5,7 +5,6 @@ import ( "strings" "github.com/dokku/dokku/plugins/common" - "github.com/dokku/dokku/plugins/config" ) // CommandList is a cmd wrapper to list port mappings for an app @@ -45,8 +44,7 @@ func CommandClear(appName string) error { return err } - keys := []string{"DOKKU_PROXY_PORT_MAP"} - if err := config.UnsetMany(appName, keys, false); err != nil { + if err := clearPorts(appName); err != nil { return err } diff --git a/plugins/ports/triggers.go b/plugins/ports/triggers.go index 0139ee3bd..a444a4a30 100644 --- a/plugins/ports/triggers.go +++ b/plugins/ports/triggers.go @@ -19,6 +19,11 @@ func TriggerPortsGet(appName string) error { return nil } +// TriggerPortsClear removes all ports for the specified app +func TriggerPortsClear(appName string) error { + return clearPorts(appName) +} + // TriggerPostCertsRemove unsets port config vars after SSL cert is added func TriggerPostCertsRemove(appName string) error { keys := []string{"DOKKU_PROXY_SSL_PORT"} From 87c4cb7230cb4c5ab6426984b0695baec1dab96e Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 9 Jul 2023 00:21:00 -0400 Subject: [PATCH 25/61] docs: sort the plugin triggers --- docs/development/plugin-triggers.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/development/plugin-triggers.md b/docs/development/plugin-triggers.md index c31e389d9..7e03191a8 100644 --- a/docs/development/plugin-triggers.md +++ b/docs/development/plugin-triggers.md @@ -1241,11 +1241,11 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x nginx -t ``` -### `ports-get` +### `ports-clear` -- Description: Returns a list of port mappings, newline delimited -- Invoked by: Various networking plugins -- Arguments `$APP` +- Description: Clears the ports for a given app without triggering further restarts or rebuilds +- Invoked by: internally +- Arguments: `$APP` - Example: ```shell @@ -1256,11 +1256,11 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x # TODO ``` -### `ports-clear` +### `ports-get` -- Description: Clears the ports for a given app without triggering further restarts or rebuilds -- Invoked by: internally -- Arguments: `$APP` +- Description: Returns a list of port mappings, newline delimited +- Invoked by: Various networking plugins +- Arguments `$APP` - Example: ```shell From bdf0b6c5098f4cf31c277847ec767820fc9f74dc Mon Sep 17 00:00:00 2001 From: Igor Loskutov Date: Fri, 7 Jul 2023 17:35:02 +0700 Subject: [PATCH 26/61] Add application/wasm to nginx.conf.sigil gzip --- plugins/nginx-vhosts/templates/nginx.conf.sigil | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/nginx-vhosts/templates/nginx.conf.sigil b/plugins/nginx-vhosts/templates/nginx.conf.sigil index 94c7d99c6..59a40a6b7 100644 --- a/plugins/nginx-vhosts/templates/nginx.conf.sigil +++ b/plugins/nginx-vhosts/templates/nginx.conf.sigil @@ -22,7 +22,7 @@ server { gzip on; gzip_min_length 1100; gzip_buffers 4 32k; - gzip_types text/css text/javascript text/xml text/plain text/x-component application/javascript application/x-javascript application/json application/xml application/rss+xml font/truetype application/x-font-ttf font/opentype application/vnd.ms-fontobject image/svg+xml; + gzip_types text/css text/javascript text/xml text/plain text/x-component application/javascript application/x-javascript application/wasm application/json application/xml application/rss+xml font/truetype application/x-font-ttf font/opentype application/vnd.ms-fontobject image/svg+xml; gzip_vary on; gzip_comp_level 6; From 121e4737a86604d756a32cf7712aa47abe537656 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 9 Jul 2023 02:26:16 -0400 Subject: [PATCH 27/61] refactor: move code that fetches raw tcp ports for dockerfile deploys to ports plugin --- docs/appendices/0.31.0-migration-guide.md | 1 + docs/development/plugin-triggers.md | 17 ++++++++++ .../20_events/ports-dockerfile-raw-tcp-ports | 1 + plugins/common/functions | 14 ++------- plugins/domains/internal-functions | 2 +- plugins/nginx-vhosts/functions | 2 +- plugins/ports/Makefile | 2 +- plugins/ports/functions.go | 31 +++++++++++++++++++ plugins/ports/src/triggers/triggers.go | 3 ++ plugins/ports/triggers.go | 21 ++++++++++--- 10 files changed, 75 insertions(+), 19 deletions(-) create mode 120000 plugins/20_events/ports-dockerfile-raw-tcp-ports diff --git a/docs/appendices/0.31.0-migration-guide.md b/docs/appendices/0.31.0-migration-guide.md index f873a9da1..365cd3193 100644 --- a/docs/appendices/0.31.0-migration-guide.md +++ b/docs/appendices/0.31.0-migration-guide.md @@ -7,6 +7,7 @@ - The `traefik` integration now mounts config to `/data` instead of the path `/acme.json`, fixing permissions issues under certain architectures. To take advantage of the new functionality, the traefik container should be stopped (via `dokku traefik:stop`) and then started (via `dokku traefik:start`). - Users no longer need to clear the `source-image` git property when transitioning from image-based deploys (`git:from-image` and `git:load-image`) to other deployment methods (git push, `git:from-archive`, `git:sync`). - For deploys via the `git:from-image` and `git:load-image` commands, the `CHECKS` file is now extracted from the configured `WORKDIR` property of the image. For all other deploys - git push, `git:from-archive`, `git:sync` - will have the `CHECKS` extracted directly from the source code. The filename in both cases is `CHECKS` and cannot be modified. +- The `common#get_app_raw_tcp_ports()` function has been deprecated and will be removed in the next release. Users should avoid interacting with this function for dockerfile ports and instead use the `ports-get` plugin trigger for fetching ports for an app. ## Deprecations diff --git a/docs/development/plugin-triggers.md b/docs/development/plugin-triggers.md index 7e03191a8..8fd94e8f5 100644 --- a/docs/development/plugin-triggers.md +++ b/docs/development/plugin-triggers.md @@ -1256,6 +1256,23 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x # TODO ``` +### `ports-dockerfile-raw-tcp-ports` + +> Warning: This trigger is for internal use and will be removed in a future release. Do not use it in your codebase. + +- Description: Extracts raw tcp port numbers from DOCKERFILE_PORTS config variable +- Invoked by: `internally triggered by proxy plugins` +- Arguments: `$APP` +- Example: + +```shell +#!/usr/bin/env bash + +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x + +# TODO +``` + ### `ports-get` - Description: Returns a list of port mappings, newline delimited diff --git a/plugins/20_events/ports-dockerfile-raw-tcp-ports b/plugins/20_events/ports-dockerfile-raw-tcp-ports new file mode 120000 index 000000000..5178a749f --- /dev/null +++ b/plugins/20_events/ports-dockerfile-raw-tcp-ports @@ -0,0 +1 @@ +hook \ No newline at end of file diff --git a/plugins/common/functions b/plugins/common/functions index f1f49bc83..23043bbd5 100755 --- a/plugins/common/functions +++ b/plugins/common/functions @@ -815,18 +815,10 @@ extract_directive_from_dockerfile() { get_app_raw_tcp_ports() { declare desc="extracts raw tcp port numbers from DOCKERFILE_PORTS config variable" - source "$PLUGIN_AVAILABLE_PATH/config/functions" + declare APP="$1" - local APP="$1" - local DOCKERFILE_PORTS="$(config_get "$APP" DOKKU_DOCKERFILE_PORTS)" - for p in $DOCKERFILE_PORTS; do - if [[ ! "$p" =~ .*udp.* ]]; then - p=${p//\/tcp/} - raw_tcp_ports+="$p " - fi - done - local raw_tcp_ports="$(echo "$raw_tcp_ports" | xargs)" - echo "$raw_tcp_ports" + dokku_log_warn "Deprecated: this function should not be used in app code" + plugn trigger ports-dockerfile-raw-tcp-ports "$APP" | xargs } get_container_ports() { diff --git a/plugins/domains/internal-functions b/plugins/domains/internal-functions index f897c4442..cd5bcf2cd 100755 --- a/plugins/domains/internal-functions +++ b/plugins/domains/internal-functions @@ -137,7 +137,7 @@ fn-domains-generate-urls() { fn-domains-generate-urls-from-config() { declare APP="$1" SCHEME="$2" VHOST="$3" DEFAULT_LISTEN_PORT="$4" local APP_PORT_MAP="$(plugn trigger ports-get "$APP")" - local RAW_TCP_PORTS="$(get_app_raw_tcp_ports "$APP")" + local RAW_TCP_PORTS="$(plugn trigger ports-dockerfile-raw-tcp-ports "$APP" | xargs)" if [[ "$(plugn trigger proxy-is-enabled "$APP")" == "false" ]]; then if [[ -n "$RAW_TCP_PORTS" ]]; then diff --git a/plugins/nginx-vhosts/functions b/plugins/nginx-vhosts/functions index 7b2238347..687a09f6b 100755 --- a/plugins/nginx-vhosts/functions +++ b/plugins/nginx-vhosts/functions @@ -294,7 +294,7 @@ nginx_build_config() { local SCHEME=http local NGINX_TEMPLATE_SOURCE="built-in" local APP_SSL_PATH="$DOKKU_ROOT/$APP/tls" - local RAW_TCP_PORTS="$(get_app_raw_tcp_ports "$APP")" + local RAW_TCP_PORTS="$(plugn trigger ports-dockerfile-raw-tcp-ports "$APP" | xargs)" local DOKKU_APP_LISTENERS CUSTOM_NGINX_TEMPLATE="$(plugn trigger nginx-app-template-source "$APP" "app-config")" diff --git a/plugins/ports/Makefile b/plugins/ports/Makefile index 09ef51751..8a4da72d2 100644 --- a/plugins/ports/Makefile +++ b/plugins/ports/Makefile @@ -1,5 +1,5 @@ SUBCOMMANDS = subcommands/list subcommands/add subcommands/clear subcommands/remove subcommands/set subcommands/report -TRIGGERS = triggers/ports-clear triggers/ports-get triggers/post-certs-remove triggers/post-certs-update triggers/report +TRIGGERS = triggers/ports-clear triggers/ports-dockerfile-raw-tcp-ports triggers/ports-get triggers/post-certs-remove triggers/post-certs-update triggers/report BUILD = commands subcommands triggers PLUGIN_NAME = ports diff --git a/plugins/ports/functions.go b/plugins/ports/functions.go index 4739c839e..0f701a664 100644 --- a/plugins/ports/functions.go +++ b/plugins/ports/functions.go @@ -38,6 +38,37 @@ func filterAppPortMaps(appName string, scheme string, hostPort int) []PortMap { return filteredPortMaps } +func getDockerfileRawTCPPorts(appName string) []int { + b, _ := common.PlugnTriggerOutput("config-get", []string{appName, "DOKKU_DOCKERFILE_PORTS"}...) + dockerfilePorts := strings.TrimSpace(string(b[:])) + + ports := []int{} + for _, dockerfilePort := range strings.Split(dockerfilePorts, " ") { + dockerfilePort = strings.TrimSpace(dockerfilePort) + if strings.HasSuffix(dockerfilePort, "/udp") { + continue + } + + dockerfilePort = strings.TrimSuffix(dockerfilePort, "/tcp") + if dockerfilePort == "" { + continue + } + + port, err := strconv.Atoi(dockerfilePort) + if err != nil { + continue + } + + if port == 0 { + continue + } + + ports = append(ports, port) + } + + return ports +} + func getPortMaps(appName string) []PortMap { value := config.GetWithDefault(appName, "DOKKU_PROXY_PORT_MAP", "") portMaps, _ := parsePortMapString(value) diff --git a/plugins/ports/src/triggers/triggers.go b/plugins/ports/src/triggers/triggers.go index d7a1f8c88..75d40616d 100644 --- a/plugins/ports/src/triggers/triggers.go +++ b/plugins/ports/src/triggers/triggers.go @@ -24,6 +24,9 @@ func main() { case "ports-clear": appName := flag.Arg(0) err = ports.TriggerPortsClear(appName) + case "ports-dockerfile-raw-tcp-ports": + appName := flag.Arg(0) + err = ports.TriggerPortsDockerfileRawTCPPorts(appName) case "post-certs-remove": appName := flag.Arg(0) err = ports.TriggerPostCertsRemove(appName) diff --git a/plugins/ports/triggers.go b/plugins/ports/triggers.go index a444a4a30..39aa0b49a 100644 --- a/plugins/ports/triggers.go +++ b/plugins/ports/triggers.go @@ -3,9 +3,25 @@ package ports import ( "fmt" + "github.com/dokku/dokku/plugins/common" "github.com/dokku/dokku/plugins/config" ) +// TriggerPortsClear removes all ports for the specified app +func TriggerPortsClear(appName string) error { + return clearPorts(appName) +} + +// TriggerRawTCPPorts extracts raw tcp port numbers from DOCKERFILE_PORTS config variable +func TriggerPortsDockerfileRawTCPPorts(appName string) error { + ports := getDockerfileRawTCPPorts(appName) + for _, port := range ports { + common.Log(fmt.Sprint(port)) + } + + return nil +} + // TriggerPortsGet prints out the port mapping for a given app func TriggerPortsGet(appName string) error { for _, portMap := range getPortMaps(appName) { @@ -19,11 +35,6 @@ func TriggerPortsGet(appName string) error { return nil } -// TriggerPortsClear removes all ports for the specified app -func TriggerPortsClear(appName string) error { - return clearPorts(appName) -} - // TriggerPostCertsRemove unsets port config vars after SSL cert is added func TriggerPostCertsRemove(appName string) error { keys := []string{"DOKKU_PROXY_SSL_PORT"} From 192f7cf7680a6de2d6b24f3198ffa1b37f42b742 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 9 Jul 2023 02:45:36 -0400 Subject: [PATCH 28/61] refactor: move code for fetching an available port to the ports plugin Also translate the code to golang. --- docs/appendices/0.31.0-migration-guide.md | 1 + docs/development/plugin-triggers.md | 15 +++++++++++++++ plugins/20_events/ports-get-available | 1 + plugins/common/functions | 11 ++--------- plugins/ports/Makefile | 2 +- plugins/ports/functions.go | 21 +++++++++++++++++++++ plugins/ports/proxy-configure-ports | 6 +++--- plugins/ports/src/triggers/triggers.go | 8 +++++--- plugins/ports/triggers.go | 10 ++++++++++ 9 files changed, 59 insertions(+), 16 deletions(-) create mode 120000 plugins/20_events/ports-get-available diff --git a/docs/appendices/0.31.0-migration-guide.md b/docs/appendices/0.31.0-migration-guide.md index 365cd3193..dd66f2196 100644 --- a/docs/appendices/0.31.0-migration-guide.md +++ b/docs/appendices/0.31.0-migration-guide.md @@ -8,6 +8,7 @@ - Users no longer need to clear the `source-image` git property when transitioning from image-based deploys (`git:from-image` and `git:load-image`) to other deployment methods (git push, `git:from-archive`, `git:sync`). - For deploys via the `git:from-image` and `git:load-image` commands, the `CHECKS` file is now extracted from the configured `WORKDIR` property of the image. For all other deploys - git push, `git:from-archive`, `git:sync` - will have the `CHECKS` extracted directly from the source code. The filename in both cases is `CHECKS` and cannot be modified. - The `common#get_app_raw_tcp_ports()` function has been deprecated and will be removed in the next release. Users should avoid interacting with this function for dockerfile ports and instead use the `ports-get` plugin trigger for fetching ports for an app. +- The `common#get_available_port()` function has been deprecated and will be removed in the next release. Users should avoid interacting with this function and instead use the `ports-get-available` plugin trigger for fetching an available port. ## Deprecations diff --git a/docs/development/plugin-triggers.md b/docs/development/plugin-triggers.md index 8fd94e8f5..f1fc0c5f6 100644 --- a/docs/development/plugin-triggers.md +++ b/docs/development/plugin-triggers.md @@ -1288,6 +1288,21 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x # TODO ``` +### `ports-get-available` + +- Description: Prints out an available port greater than 1024 +- Invoked by: Various networking plugins +- Arguments `$APP` +- Example: + +```shell +#!/usr/bin/env bash + +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x + +# TODO +``` + ### `post-app-clone` - Description: Allows you to run commands after an app was cloned. diff --git a/plugins/20_events/ports-get-available b/plugins/20_events/ports-get-available new file mode 120000 index 000000000..5178a749f --- /dev/null +++ b/plugins/20_events/ports-get-available @@ -0,0 +1 @@ +hook \ No newline at end of file diff --git a/plugins/common/functions b/plugins/common/functions index 23043bbd5..5357269ad 100755 --- a/plugins/common/functions +++ b/plugins/common/functions @@ -699,15 +699,8 @@ docker_cleanup() { get_available_port() { declare desc="returns first currently unused port > 1024" - while true; do - local port=$(shuf -i 1025-65535 -n 1) - if ! nc -z 0.0.0.0 "$port"; then - echo "$port" - return 0 - else - continue - fi - done + dokku_log_warn "Deprecated: please use the 'ports-get-available' plugin trigger instead" + plugn trigger ports-get-available } dokku_auth() { diff --git a/plugins/ports/Makefile b/plugins/ports/Makefile index 8a4da72d2..962d51470 100644 --- a/plugins/ports/Makefile +++ b/plugins/ports/Makefile @@ -1,5 +1,5 @@ SUBCOMMANDS = subcommands/list subcommands/add subcommands/clear subcommands/remove subcommands/set subcommands/report -TRIGGERS = triggers/ports-clear triggers/ports-dockerfile-raw-tcp-ports triggers/ports-get triggers/post-certs-remove triggers/post-certs-update triggers/report +TRIGGERS = triggers/ports-clear triggers/ports-dockerfile-raw-tcp-ports triggers/ports-get triggers/ports-get-available triggers/post-certs-remove triggers/post-certs-update triggers/report BUILD = commands subcommands triggers PLUGIN_NAME = ports diff --git a/plugins/ports/functions.go b/plugins/ports/functions.go index 0f701a664..05c087d73 100644 --- a/plugins/ports/functions.go +++ b/plugins/ports/functions.go @@ -3,6 +3,7 @@ package ports import ( "errors" "fmt" + "net" "os" "sort" "strconv" @@ -38,6 +39,26 @@ func filterAppPortMaps(appName string, scheme string, hostPort int) []PortMap { return filteredPortMaps } +func getAvailablePort() int { + addr, err := net.ResolveTCPAddr("tcp", "localhost:0") + if err != nil { + return 0 + } + + for { + l, err := net.ListenTCP("tcp", addr) + if err != nil { + return 0 + } + defer l.Close() + + port := l.Addr().(*net.TCPAddr).Port + if port >= 1025 && port <= 65535 { + return port + } + } +} + func getDockerfileRawTCPPorts(appName string) []int { b, _ := common.PlugnTriggerOutput("config-get", []string{appName, "DOKKU_DOCKERFILE_PORTS"}...) dockerfilePorts := strings.TrimSpace(string(b[:])) diff --git a/plugins/ports/proxy-configure-ports b/plugins/ports/proxy-configure-ports index deee9b39d..4f56e11db 100755 --- a/plugins/ports/proxy-configure-ports +++ b/plugins/ports/proxy-configure-ports @@ -8,7 +8,7 @@ trigger-ports-proxy-configure-ports() { declare desc="ports proxy-configure-ports plugin trigger" declare trigger="proxy-configure-ports" declare APP="$1" - local RAW_TCP_PORTS="$(get_app_raw_tcp_ports "$APP")" + local RAW_TCP_PORTS="$(plugn trigger ports-dockerfile-raw-tcp-ports "$APP" | xargs)" local DOKKU_PROXY_PORT=$(config_get "$APP" DOKKU_PROXY_PORT) local DOKKU_PROXY_SSL_PORT=$(config_get "$APP" DOKKU_PROXY_SSL_PORT) local DOKKU_PROXY_PORT_MAP=$(config_get "$APP" DOKKU_PROXY_PORT_MAP) @@ -20,7 +20,7 @@ trigger-ports-proxy-configure-ports() { if [[ -z "$DOKKU_PROXY_PORT" ]] && [[ -z "$RAW_TCP_PORTS" ]]; then if [[ "$IS_APP_VHOST_ENABLED" == "false" ]]; then dokku_log_info1 "No port set, setting to random open high port" - local PROXY_PORT=$(get_available_port) + local PROXY_PORT=$(plugn trigger ports-get-available) else local PROXY_PORT=$(config_get --global DOKKU_PROXY_PORT) PROXY_PORT=${PROXY_PORT:=80} @@ -33,7 +33,7 @@ trigger-ports-proxy-configure-ports() { PROXY_SSL_PORT=${PROXY_SSL_PORT:=443} if [[ -z "$RAW_TCP_PORTS" ]] && [[ "$IS_APP_VHOST_ENABLED" == "false" ]]; then dokku_log_info1 "No ssl port set, setting to random open high port" - PROXY_SSL_PORT=$(get_available_port) + PROXY_SSL_PORT=$(plugn trigger ports-get-available) fi DOKKU_QUIET_OUTPUT=1 config_set --no-restart "$APP" DOKKU_PROXY_SSL_PORT="$PROXY_SSL_PORT" fi diff --git a/plugins/ports/src/triggers/triggers.go b/plugins/ports/src/triggers/triggers.go index 75d40616d..bb125943c 100644 --- a/plugins/ports/src/triggers/triggers.go +++ b/plugins/ports/src/triggers/triggers.go @@ -18,15 +18,17 @@ func main() { var err error switch trigger { - case "ports-get": - appName := flag.Arg(0) - err = ports.TriggerPortsGet(appName) case "ports-clear": appName := flag.Arg(0) err = ports.TriggerPortsClear(appName) case "ports-dockerfile-raw-tcp-ports": appName := flag.Arg(0) err = ports.TriggerPortsDockerfileRawTCPPorts(appName) + case "ports-get": + appName := flag.Arg(0) + err = ports.TriggerPortsGet(appName) + case "ports-get-available": + err = ports.TriggerPortsGetAvailable() case "post-certs-remove": appName := flag.Arg(0) err = ports.TriggerPostCertsRemove(appName) diff --git a/plugins/ports/triggers.go b/plugins/ports/triggers.go index 39aa0b49a..ea842f83a 100644 --- a/plugins/ports/triggers.go +++ b/plugins/ports/triggers.go @@ -35,6 +35,16 @@ func TriggerPortsGet(appName string) error { return nil } +// TriggerPortsGetAvailable prints out an available port greater than 1024 +func TriggerPortsGetAvailable() error { + port := getAvailablePort() + if port > 0 { + common.Log(fmt.Sprint(port)) + } + + return nil +} + // TriggerPostCertsRemove unsets port config vars after SSL cert is added func TriggerPostCertsRemove(appName string) error { keys := []string{"DOKKU_PROXY_SSL_PORT"} From 48d5820b8cc1836fcc4637ba1961e53e0362eca1 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 2 Jul 2023 19:29:05 -0400 Subject: [PATCH 29/61] refactor: deprecate proxy-configure-ports plugin trigger in favor of ports-configure This also rewrites the logic to be in golang, which will make it easier to refactor portions of it to use the property system going forward. --- docs/appendices/0.31.0-migration-guide.md | 5 +- docs/development/plugin-triggers.md | 17 +++ plugins/20_events/ports-configure | 1 + .../caddy-vhosts/docker-args-process-deploy | 2 +- plugins/common/ports-get-available | 6 + .../haproxy-vhosts/docker-args-process-deploy | 2 +- plugins/nginx-vhosts/functions | 2 +- plugins/ports/Makefile | 2 +- plugins/ports/functions.go | 65 ++++++++++- plugins/ports/proxy-configure-ports | 54 +-------- plugins/ports/src/triggers/triggers.go | 3 + plugins/ports/triggers.go | 106 ++++++++++++++++++ .../traefik-vhosts/docker-args-process-deploy | 2 +- 13 files changed, 203 insertions(+), 64 deletions(-) create mode 120000 plugins/20_events/ports-configure create mode 100755 plugins/common/ports-get-available diff --git a/docs/appendices/0.31.0-migration-guide.md b/docs/appendices/0.31.0-migration-guide.md index dd66f2196..b07d22ca8 100644 --- a/docs/appendices/0.31.0-migration-guide.md +++ b/docs/appendices/0.31.0-migration-guide.md @@ -7,12 +7,13 @@ - The `traefik` integration now mounts config to `/data` instead of the path `/acme.json`, fixing permissions issues under certain architectures. To take advantage of the new functionality, the traefik container should be stopped (via `dokku traefik:stop`) and then started (via `dokku traefik:start`). - Users no longer need to clear the `source-image` git property when transitioning from image-based deploys (`git:from-image` and `git:load-image`) to other deployment methods (git push, `git:from-archive`, `git:sync`). - For deploys via the `git:from-image` and `git:load-image` commands, the `CHECKS` file is now extracted from the configured `WORKDIR` property of the image. For all other deploys - git push, `git:from-archive`, `git:sync` - will have the `CHECKS` extracted directly from the source code. The filename in both cases is `CHECKS` and cannot be modified. -- The `common#get_app_raw_tcp_ports()` function has been deprecated and will be removed in the next release. Users should avoid interacting with this function for dockerfile ports and instead use the `ports-get` plugin trigger for fetching ports for an app. -- The `common#get_available_port()` function has been deprecated and will be removed in the next release. Users should avoid interacting with this function and instead use the `ports-get-available` plugin trigger for fetching an available port. ## Deprecations - The `proxy:ports*` commands have been replaced with the new `ports` plugin. Users will be able to use the old `proxy:ports*` commands for a single minor release, and they will be removed in the next minor release. +- The `common#get_app_raw_tcp_ports()` function has been deprecated and will be removed in the next release. Users should avoid interacting with this function for dockerfile ports and instead use the `ports-get` plugin trigger for fetching ports for an app. +- The `common#get_available_port()` function has been deprecated and will be removed in the next release. Users should avoid interacting with this function and instead use the `ports-get-available` plugin trigger for fetching an available port. +- The `proxy-configure-ports` plugin trigger has been deprecated and will be removed in the next release. Users should instead trigger the `ports-configure` plugin trigger. ## Un-Deprecations diff --git a/docs/development/plugin-triggers.md b/docs/development/plugin-triggers.md index f1fc0c5f6..a3ddf285f 100644 --- a/docs/development/plugin-triggers.md +++ b/docs/development/plugin-triggers.md @@ -1256,6 +1256,21 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x # TODO ``` +### `ports-configure` + +- Description: Configures the port mapping +- Invoked by: `internally triggered by proxy plugins` +- Arguments: `$APP` +- Example: + +```shell +#!/usr/bin/env bash + +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x + +# TODO +``` + ### `ports-dockerfile-raw-tcp-ports` > Warning: This trigger is for internal use and will be removed in a future release. Do not use it in your codebase. @@ -1934,6 +1949,8 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x ### `proxy-configure-ports` +> Warning: Deprecated, please use `ports-configure` instead + - Description: Configures the port mapping - Invoked by: `internally triggered by proxy plugins` - Arguments: `$APP` diff --git a/plugins/20_events/ports-configure b/plugins/20_events/ports-configure new file mode 120000 index 000000000..5178a749f --- /dev/null +++ b/plugins/20_events/ports-configure @@ -0,0 +1 @@ +hook \ No newline at end of file diff --git a/plugins/caddy-vhosts/docker-args-process-deploy b/plugins/caddy-vhosts/docker-args-process-deploy index 1d1fdd28e..65a9c9796 100755 --- a/plugins/caddy-vhosts/docker-args-process-deploy +++ b/plugins/caddy-vhosts/docker-args-process-deploy @@ -30,7 +30,7 @@ trigger-caddy-vhosts-docker-args-process-deploy() { fi # ensure we have a port mapping - plugn trigger proxy-configure-ports "$APP" + plugn trigger ports-configure "$APP" # gather port mapping information # we only support proxying a single port for http and https listeners diff --git a/plugins/common/ports-get-available b/plugins/common/ports-get-available new file mode 100755 index 000000000..f7d989333 --- /dev/null +++ b/plugins/common/ports-get-available @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +get_available_port "$@" diff --git a/plugins/haproxy-vhosts/docker-args-process-deploy b/plugins/haproxy-vhosts/docker-args-process-deploy index 9546d7280..321b403fe 100755 --- a/plugins/haproxy-vhosts/docker-args-process-deploy +++ b/plugins/haproxy-vhosts/docker-args-process-deploy @@ -33,7 +33,7 @@ trigger-haproxy-vhosts-docker-args-process-deploy() { plugn trigger domains-setup "$APP" >/dev/null # ensure we have a port mapping - plugn trigger proxy-configure-ports "$APP" + plugn trigger ports-configure "$APP" # gather port mapping information # we only support proxying a single port for http and https listeners diff --git a/plugins/nginx-vhosts/functions b/plugins/nginx-vhosts/functions index 687a09f6b..9f65f1be2 100755 --- a/plugins/nginx-vhosts/functions +++ b/plugins/nginx-vhosts/functions @@ -318,7 +318,7 @@ nginx_build_config() { fi # setup nginx listen ports - plugn trigger proxy-configure-ports "$APP" + plugn trigger ports-configure "$APP" local PROXY_PORT=$(config_get "$APP" DOKKU_PROXY_PORT) local PROXY_SSL_PORT=$(config_get "$APP" DOKKU_PROXY_SSL_PORT) local PROXY_PORT_MAP=$(plugn trigger ports-get "$APP") diff --git a/plugins/ports/Makefile b/plugins/ports/Makefile index 962d51470..189f7b899 100644 --- a/plugins/ports/Makefile +++ b/plugins/ports/Makefile @@ -1,5 +1,5 @@ SUBCOMMANDS = subcommands/list subcommands/add subcommands/clear subcommands/remove subcommands/set subcommands/report -TRIGGERS = triggers/ports-clear triggers/ports-dockerfile-raw-tcp-ports triggers/ports-get triggers/ports-get-available triggers/post-certs-remove triggers/post-certs-update triggers/report +TRIGGERS = triggers/ports-clear triggers/ports-configure triggers/ports-dockerfile-raw-tcp-ports triggers/ports-get triggers/ports-get-available triggers/post-certs-remove triggers/post-certs-update triggers/report BUILD = commands subcommands triggers PLUGIN_NAME = ports diff --git a/plugins/ports/functions.go b/plugins/ports/functions.go index 05c087d73..d01d5dcbb 100644 --- a/plugins/ports/functions.go +++ b/plugins/ports/functions.go @@ -28,6 +28,12 @@ func clearPorts(appName string) error { }, map[string]string{"DOKKU_QUIET_OUTPUT": "1"}) } +func doesCertExist(appName string) bool { + b, _ := common.PlugnTriggerOutput("certs-exists", []string{appName}...) + certsExists := strings.TrimSpace(string(b[:])) + return certsExists == "true" +} + func filterAppPortMaps(appName string, scheme string, hostPort int) []PortMap { var filteredPortMaps []PortMap for _, portMap := range getPortMaps(appName) { @@ -90,12 +96,59 @@ func getDockerfileRawTCPPorts(appName string) []int { return ports } +func getGlobalProxyPort() int { + port := 0 + b, _ := common.PlugnTriggerOutput("config-get-global", []string{"DOKKU_PROXY_PORT"}...) + if intVar, err := strconv.Atoi(strings.TrimSpace(string(b[:]))); err == nil { + port = intVar + } + + return port +} + +func getGlobalProxySSLPort() int { + port := 0 + b, _ := common.PlugnTriggerOutput("config-get-global", []string{"DOKKU_PROXY_SSL_PORT"}...) + if intVar, err := strconv.Atoi(strings.TrimSpace(string(b[:]))); err == nil { + port = intVar + } + + return port +} + func getPortMaps(appName string) []PortMap { value := config.GetWithDefault(appName, "DOKKU_PROXY_PORT_MAP", "") portMaps, _ := parsePortMapString(value) return portMaps } +func getProxyPort(appName string) int { + port := 0 + b, _ := common.PlugnTriggerOutput("config-get", []string{appName, "DOKKU_PROXY_PORT"}...) + if intVar, err := strconv.Atoi(strings.TrimSpace(string(b[:]))); err == nil { + port = intVar + } + + return port +} + +func getProxySSLPort(appName string) int { + port := 0 + b, _ := common.PlugnTriggerOutput("config-get", []string{appName, "DOKKU_PROXY_SSL_PORT"}...) + if intVar, err := strconv.Atoi(strings.TrimSpace(string(b[:]))); err == nil { + port = intVar + } + + return port +} + +func isAppVhostEnabled(appName string) bool { + if err := common.PlugnTrigger("domains-vhost-enabled", []string{appName}...); err != nil { + return false + } + return true +} + func inRange(value int, min int, max int) bool { return min < value && value < max } @@ -223,11 +276,13 @@ func setPortMaps(appName string, portMaps []PortMap) error { value = append(value, portMap.String()) } - sort.Strings(value) - entries := map[string]string{ - "DOKKU_PROXY_PORT_MAP": strings.Join(value, " "), - } - return config.SetMany(appName, entries, false) + return common.EnvWrap(func() error { + sort.Strings(value) + entries := map[string]string{ + "DOKKU_PROXY_PORT_MAP": strings.Join(value, " "), + } + return config.SetMany(appName, entries, false) + }, map[string]string{"DOKKU_QUIET_OUTPUT": "1"}) } func uniquePortMaps(portMaps []PortMap) []PortMap { diff --git a/plugins/ports/proxy-configure-ports b/plugins/ports/proxy-configure-ports index 4f56e11db..649106588 100755 --- a/plugins/ports/proxy-configure-ports +++ b/plugins/ports/proxy-configure-ports @@ -1,6 +1,5 @@ #!/usr/bin/env bash source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" -source "$PLUGIN_AVAILABLE_PATH/config/functions" set -eo pipefail [[ $DOKKU_TRACE ]] && set -x @@ -8,58 +7,9 @@ trigger-ports-proxy-configure-ports() { declare desc="ports proxy-configure-ports plugin trigger" declare trigger="proxy-configure-ports" declare APP="$1" - local RAW_TCP_PORTS="$(plugn trigger ports-dockerfile-raw-tcp-ports "$APP" | xargs)" - local DOKKU_PROXY_PORT=$(config_get "$APP" DOKKU_PROXY_PORT) - local DOKKU_PROXY_SSL_PORT=$(config_get "$APP" DOKKU_PROXY_SSL_PORT) - local DOKKU_PROXY_PORT_MAP=$(config_get "$APP" DOKKU_PROXY_PORT_MAP) - local IS_APP_VHOST_ENABLED=true - local UPSTREAM_PORT="5000" - plugn trigger domains-vhost-enabled "$APP" 2>/dev/null || IS_APP_VHOST_ENABLED=false - - if [[ -z "$DOKKU_PROXY_PORT" ]] && [[ -z "$RAW_TCP_PORTS" ]]; then - if [[ "$IS_APP_VHOST_ENABLED" == "false" ]]; then - dokku_log_info1 "No port set, setting to random open high port" - local PROXY_PORT=$(plugn trigger ports-get-available) - else - local PROXY_PORT=$(config_get --global DOKKU_PROXY_PORT) - PROXY_PORT=${PROXY_PORT:=80} - fi - DOKKU_QUIET_OUTPUT=1 config_set --no-restart "$APP" DOKKU_PROXY_PORT="$PROXY_PORT" - fi - if [[ -z "$DOKKU_PROXY_SSL_PORT" ]]; then - if [[ "$(plugn trigger certs-exists "$APP")" == "true" ]]; then - local PROXY_SSL_PORT=$(config_get --global DOKKU_PROXY_SSL_PORT) - PROXY_SSL_PORT=${PROXY_SSL_PORT:=443} - if [[ -z "$RAW_TCP_PORTS" ]] && [[ "$IS_APP_VHOST_ENABLED" == "false" ]]; then - dokku_log_info1 "No ssl port set, setting to random open high port" - PROXY_SSL_PORT=$(plugn trigger ports-get-available) - fi - DOKKU_QUIET_OUTPUT=1 config_set --no-restart "$APP" DOKKU_PROXY_SSL_PORT="$PROXY_SSL_PORT" - fi - fi - if [[ -z "$DOKKU_PROXY_PORT_MAP" ]]; then - if [[ -n "$RAW_TCP_PORTS" ]]; then - local RAW_TCP_PORT - for RAW_TCP_PORT in $RAW_TCP_PORTS; do - local PROXY_PORT_MAP+=" http:${RAW_TCP_PORT}:${RAW_TCP_PORT} " - done - else - local PROXY_PORT=${PROXY_PORT:-$DOKKU_PROXY_PORT} - local PROXY_SSL_PORT=${PROXY_SSL_PORT:-$DOKKU_PROXY_SSL_PORT} - [[ -f "$DOKKU_ROOT/$APP/PORT.web.1" ]] && local UPSTREAM_PORT="$(<"$DOKKU_ROOT/$APP/PORT.web.1")" - if [[ -n "$PROXY_PORT" ]] && [[ -n "$PROXY_SSL_PORT" ]]; then - local PROXY_PORT_MAP+=" http:${PROXY_PORT}:$UPSTREAM_PORT https:${PROXY_SSL_PORT}:$UPSTREAM_PORT " - elif [[ -n "$PROXY_PORT" ]]; then - local PROXY_PORT_MAP+=" http:${PROXY_PORT}:$UPSTREAM_PORT " - fi - fi - if [[ -n "$PROXY_PORT_MAP" ]]; then - local PROXY_PORT_MAP="$(echo "$PROXY_PORT_MAP" | xargs)" - local PROXY_PORT_MAP+=" $(merge_dedupe_list "$(remove_val_from_list "$PORT_MAP" "$DOKKU_PROXY_PORT_MAP" " ")" " ") " - DOKKU_QUIET_OUTPUT=1 config_set --no-restart "$APP" DOKKU_PROXY_PORT_MAP="$PROXY_PORT_MAP" - fi - fi + dokku_log_warn "Deprecated: please use the 'ports-configure' plugin trigger instead" + plugn trigger ports-configure "$APP" } trigger-ports-proxy-configure-ports "$@" diff --git a/plugins/ports/src/triggers/triggers.go b/plugins/ports/src/triggers/triggers.go index bb125943c..6551298da 100644 --- a/plugins/ports/src/triggers/triggers.go +++ b/plugins/ports/src/triggers/triggers.go @@ -21,6 +21,9 @@ func main() { case "ports-clear": appName := flag.Arg(0) err = ports.TriggerPortsClear(appName) + case "ports-configure": + appName := flag.Arg(0) + err = ports.TriggerPortsConfigure(appName) case "ports-dockerfile-raw-tcp-ports": appName := flag.Arg(0) err = ports.TriggerPortsDockerfileRawTCPPorts(appName) diff --git a/plugins/ports/triggers.go b/plugins/ports/triggers.go index ea842f83a..9da826bf2 100644 --- a/plugins/ports/triggers.go +++ b/plugins/ports/triggers.go @@ -2,6 +2,8 @@ package ports import ( "fmt" + "path/filepath" + "strconv" "github.com/dokku/dokku/plugins/common" "github.com/dokku/dokku/plugins/config" @@ -12,6 +14,110 @@ func TriggerPortsClear(appName string) error { return clearPorts(appName) } +// TriggerPortsConfigure ensures we have a port mapping +func TriggerPortsConfigure(appName string) error { + rawTCPPorts := getDockerfileRawTCPPorts(appName) + + dokkuProxyPort := getProxyPort(appName) + dokkuProxySSLPort := getProxySSLPort(appName) + portMaps := getPortMaps(appName) + + vhostEnabled := isAppVhostEnabled(appName) + + if dokkuProxyPort == 0 && len(rawTCPPorts) == 0 { + proxyPort := 80 + if !vhostEnabled { + common.LogInfo1("No port set, setting to random open high port") + proxyPort = getAvailablePort() + } else { + proxyPort = getGlobalProxyPort() + } + + if proxyPort == 0 { + proxyPort = 80 + } + + dokkuProxyPort = proxyPort + err := common.EnvWrap(func() error { + entries := map[string]string{ + "DOKKU_PROXY_PORT": fmt.Sprint(proxyPort), + } + return config.SetMany(appName, entries, false) + }, map[string]string{"DOKKU_QUIET_OUTPUT": "1"}) + if err != nil { + return err + } + } + + if dokkuProxySSLPort == 0 { + if doesCertExist(appName) { + proxySSLPort := getGlobalProxySSLPort() + if proxySSLPort == 0 { + proxySSLPort = 443 + } + + if len(rawTCPPorts) == 0 && !vhostEnabled { + common.LogInfo1("No ssl port set, setting to random open high port") + proxySSLPort = getAvailablePort() + } + + dokkuProxySSLPort = proxySSLPort + err := common.EnvWrap(func() error { + entries := map[string]string{ + "DOKKU_PROXY_SSL_PORT": fmt.Sprint(proxySSLPort), + } + return config.SetMany(appName, entries, false) + }, map[string]string{"DOKKU_QUIET_OUTPUT": "1"}) + if err != nil { + return err + } + } + } + + if len(portMaps) == 0 { + if len(rawTCPPorts) > 0 { + for _, rawTcpPort := range rawTCPPorts { + portMaps = append(portMaps, PortMap{ + ContainerPort: rawTcpPort, + HostPort: rawTcpPort, + Scheme: "http", + }) + } + } else { + upstreamPort := 5000 + portFile := filepath.Join(common.AppRoot(appName), "PORT.web.1") + if common.FileExists(portFile) { + if port, err := strconv.Atoi(common.ReadFirstLine(portFile)); err == nil { + upstreamPort = port + } + } + + if dokkuProxyPort != 0 { + portMaps = append(portMaps, PortMap{ + ContainerPort: upstreamPort, + HostPort: dokkuProxyPort, + Scheme: "http", + }) + } + if dokkuProxySSLPort != 0 { + portMaps = append(portMaps, PortMap{ + ContainerPort: upstreamPort, + HostPort: dokkuProxySSLPort, + Scheme: "https", + }) + } + } + + if len(portMaps) > 0 { + return setPortMaps(appName, portMaps) + } + + return nil + } + + return nil +} + // TriggerRawTCPPorts extracts raw tcp port numbers from DOCKERFILE_PORTS config variable func TriggerPortsDockerfileRawTCPPorts(appName string) error { ports := getDockerfileRawTCPPorts(appName) diff --git a/plugins/traefik-vhosts/docker-args-process-deploy b/plugins/traefik-vhosts/docker-args-process-deploy index 4f09b45cd..f9d94ffa4 100755 --- a/plugins/traefik-vhosts/docker-args-process-deploy +++ b/plugins/traefik-vhosts/docker-args-process-deploy @@ -30,7 +30,7 @@ trigger-traefik-vhosts-docker-args-process-deploy() { fi # ensure we have a port mapping - plugn trigger proxy-configure-ports "$APP" + plugn trigger ports-configure "$APP" # gather port mapping information # we only support proxying a single port for http and https listeners From 977b4567f8a265b9a1d9199472f734d153acdd41 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 9 Jul 2023 18:31:18 -0400 Subject: [PATCH 30/61] refactor: simplify ports-configure codebase This makes it slightly easier to reason about, at the cost of being slower. Also ignore failure to remove image as this was flaky. --- plugins/ports/functions.go | 130 +++++++++++++++++++++++++++++++- plugins/ports/triggers.go | 99 ++---------------------- tests/unit/nginx-vhosts_12.bats | 5 +- 3 files changed, 133 insertions(+), 101 deletions(-) diff --git a/plugins/ports/functions.go b/plugins/ports/functions.go index d01d5dcbb..5c20be0f0 100644 --- a/plugins/ports/functions.go +++ b/plugins/ports/functions.go @@ -5,6 +5,7 @@ import ( "fmt" "net" "os" + "path/filepath" "sort" "strconv" "strings" @@ -142,6 +143,113 @@ func getProxySSLPort(appName string) int { return port } +func initializeProxyPort(appName string, rawTCPPorts []int) error { + port := getProxyPort(appName) + if port != 0 { + return nil + } + + if len(rawTCPPorts) > 0 { + return nil + } + + if isAppVhostEnabled(appName) { + port = getGlobalProxyPort() + } else { + common.LogInfo1("No port set, setting to random open high port") + port = getAvailablePort() + } + + if port == 0 { + port = 80 + } + + if err := setProxyPort(appName, port); err != nil { + return err + } + return nil +} + +func initializeProxySSLPort(appName string, rawTCPPorts []int) error { + port := getProxySSLPort(appName) + if port != 0 { + return nil + } + + if !doesCertExist(appName) { + return nil + } + + port = getGlobalProxySSLPort() + if port == 0 { + port = 443 + } + + if len(rawTCPPorts) == 0 && !isAppVhostEnabled(appName) { + common.LogInfo1("No ssl port set, setting to random open high port") + port = getAvailablePort() + } + + if err := setProxySSLPort(appName, port); err != nil { + return err + } + + return nil +} + +func initializePortMap(appName string, rawTCPPorts []int) error { + portMaps := getPortMaps(appName) + if len(portMaps) > 0 { + return nil + } + + if len(rawTCPPorts) > 0 { + for _, rawTcpPort := range rawTCPPorts { + portMaps = append(portMaps, PortMap{ + ContainerPort: rawTcpPort, + HostPort: rawTcpPort, + Scheme: "http", + }) + } + } else { + upstreamPort := 5000 + portFile := filepath.Join(common.AppRoot(appName), "PORT.web.1") + if common.FileExists(portFile) { + if port, err := strconv.Atoi(common.ReadFirstLine(portFile)); err == nil { + upstreamPort = port + } + } + + dokkuProxyPort := getProxyPort(appName) + dokkuProxySSLPort := getProxySSLPort(appName) + + if dokkuProxyPort != 0 { + portMaps = append(portMaps, PortMap{ + ContainerPort: upstreamPort, + HostPort: dokkuProxyPort, + Scheme: "http", + }) + } + if dokkuProxySSLPort != 0 { + portMaps = append(portMaps, PortMap{ + ContainerPort: upstreamPort, + HostPort: dokkuProxySSLPort, + Scheme: "https", + }) + } + } + + if len(portMaps) > 0 { + return setPortMaps(appName, portMaps) + } + + return nil +} + +func inRange(value int, min int, max int) bool { + return min < value && value < max +} + func isAppVhostEnabled(appName string) bool { if err := common.PlugnTrigger("domains-vhost-enabled", []string{appName}...); err != nil { return false @@ -149,10 +257,6 @@ func isAppVhostEnabled(appName string) bool { return true } -func inRange(value int, min int, max int) bool { - return min < value && value < max -} - func listAppPortMaps(appName string) error { portMaps := getPortMaps(appName) @@ -285,6 +389,24 @@ func setPortMaps(appName string, portMaps []PortMap) error { }, map[string]string{"DOKKU_QUIET_OUTPUT": "1"}) } +func setProxyPort(appName string, port int) error { + return common.EnvWrap(func() error { + entries := map[string]string{ + "DOKKU_PROXY_PORT": fmt.Sprint(port), + } + return config.SetMany(appName, entries, false) + }, map[string]string{"DOKKU_QUIET_OUTPUT": "1"}) +} + +func setProxySSLPort(appName string, port int) error { + return common.EnvWrap(func() error { + entries := map[string]string{ + "DOKKU_PROXY_SSL_PORT": fmt.Sprint(port), + } + return config.SetMany(appName, entries, false) + }, map[string]string{"DOKKU_QUIET_OUTPUT": "1"}) +} + func uniquePortMaps(portMaps []PortMap) []PortMap { var unique []PortMap existingPortMaps := map[string]bool{} diff --git a/plugins/ports/triggers.go b/plugins/ports/triggers.go index 9da826bf2..d9a8bad33 100644 --- a/plugins/ports/triggers.go +++ b/plugins/ports/triggers.go @@ -2,8 +2,6 @@ package ports import ( "fmt" - "path/filepath" - "strconv" "github.com/dokku/dokku/plugins/common" "github.com/dokku/dokku/plugins/config" @@ -18,101 +16,16 @@ func TriggerPortsClear(appName string) error { func TriggerPortsConfigure(appName string) error { rawTCPPorts := getDockerfileRawTCPPorts(appName) - dokkuProxyPort := getProxyPort(appName) - dokkuProxySSLPort := getProxySSLPort(appName) - portMaps := getPortMaps(appName) - - vhostEnabled := isAppVhostEnabled(appName) - - if dokkuProxyPort == 0 && len(rawTCPPorts) == 0 { - proxyPort := 80 - if !vhostEnabled { - common.LogInfo1("No port set, setting to random open high port") - proxyPort = getAvailablePort() - } else { - proxyPort = getGlobalProxyPort() - } - - if proxyPort == 0 { - proxyPort = 80 - } - - dokkuProxyPort = proxyPort - err := common.EnvWrap(func() error { - entries := map[string]string{ - "DOKKU_PROXY_PORT": fmt.Sprint(proxyPort), - } - return config.SetMany(appName, entries, false) - }, map[string]string{"DOKKU_QUIET_OUTPUT": "1"}) - if err != nil { - return err - } + if err := initializeProxyPort(appName, rawTCPPorts); err != nil { + return err } - if dokkuProxySSLPort == 0 { - if doesCertExist(appName) { - proxySSLPort := getGlobalProxySSLPort() - if proxySSLPort == 0 { - proxySSLPort = 443 - } - - if len(rawTCPPorts) == 0 && !vhostEnabled { - common.LogInfo1("No ssl port set, setting to random open high port") - proxySSLPort = getAvailablePort() - } - - dokkuProxySSLPort = proxySSLPort - err := common.EnvWrap(func() error { - entries := map[string]string{ - "DOKKU_PROXY_SSL_PORT": fmt.Sprint(proxySSLPort), - } - return config.SetMany(appName, entries, false) - }, map[string]string{"DOKKU_QUIET_OUTPUT": "1"}) - if err != nil { - return err - } - } + if err := initializeProxySSLPort(appName, rawTCPPorts); err != nil { + return err } - if len(portMaps) == 0 { - if len(rawTCPPorts) > 0 { - for _, rawTcpPort := range rawTCPPorts { - portMaps = append(portMaps, PortMap{ - ContainerPort: rawTcpPort, - HostPort: rawTcpPort, - Scheme: "http", - }) - } - } else { - upstreamPort := 5000 - portFile := filepath.Join(common.AppRoot(appName), "PORT.web.1") - if common.FileExists(portFile) { - if port, err := strconv.Atoi(common.ReadFirstLine(portFile)); err == nil { - upstreamPort = port - } - } - - if dokkuProxyPort != 0 { - portMaps = append(portMaps, PortMap{ - ContainerPort: upstreamPort, - HostPort: dokkuProxyPort, - Scheme: "http", - }) - } - if dokkuProxySSLPort != 0 { - portMaps = append(portMaps, PortMap{ - ContainerPort: upstreamPort, - HostPort: dokkuProxySSLPort, - Scheme: "https", - }) - } - } - - if len(portMaps) > 0 { - return setPortMaps(appName, portMaps) - } - - return nil + if err := initializePortMap(appName, rawTCPPorts); err != nil { + return err } return nil diff --git a/tests/unit/nginx-vhosts_12.bats b/tests/unit/nginx-vhosts_12.bats index 159526f6b..d6070b5fd 100644 --- a/tests/unit/nginx-vhosts_12.bats +++ b/tests/unit/nginx-vhosts_12.bats @@ -127,8 +127,5 @@ teardown() { assert_success assert_output_contains "Overriding default nginx.conf with detected nginx.conf.sigil" 2 - run /bin/bash -c "docker image rm dokku-test/$TEST_APP:latest dokku-test/$TEST_APP:v2" - echo "output: $output" - echo "status: $status" - assert_success + docker image rm dokku-test/$TEST_APP:latest dokku-test/$TEST_APP:v2 || true } From f842869c3d3aef981541d3ee8f5040ce7717ac8a Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 9 Jul 2023 18:35:42 -0400 Subject: [PATCH 31/61] refactor: move the proxy port map from config to property Also remove all direct references to DOKKU_PROXY_PORT_MAP from outside of ports. --- docs/appendices/0.31.0-migration-guide.md | 1 + docs/configuration/environment-variables.md | 1 - docs/networking/port-management.md | 4 -- docs/networking/proxies/nginx.md | 4 +- plugins/ports/Makefile | 2 +- plugins/ports/functions.go | 25 +++----- plugins/ports/src/triggers/triggers.go | 13 ++++ plugins/ports/triggers.go | 69 +++++++++++++++++++++ tests/unit/apps_2.bats | 19 +++++- tests/unit/nginx-vhosts_3.bats | 50 +++++++++++++-- 10 files changed, 157 insertions(+), 31 deletions(-) diff --git a/docs/appendices/0.31.0-migration-guide.md b/docs/appendices/0.31.0-migration-guide.md index b07d22ca8..06f644699 100644 --- a/docs/appendices/0.31.0-migration-guide.md +++ b/docs/appendices/0.31.0-migration-guide.md @@ -7,6 +7,7 @@ - The `traefik` integration now mounts config to `/data` instead of the path `/acme.json`, fixing permissions issues under certain architectures. To take advantage of the new functionality, the traefik container should be stopped (via `dokku traefik:stop`) and then started (via `dokku traefik:start`). - Users no longer need to clear the `source-image` git property when transitioning from image-based deploys (`git:from-image` and `git:load-image`) to other deployment methods (git push, `git:from-archive`, `git:sync`). - For deploys via the `git:from-image` and `git:load-image` commands, the `CHECKS` file is now extracted from the configured `WORKDIR` property of the image. For all other deploys - git push, `git:from-archive`, `git:sync` - will have the `CHECKS` extracted directly from the source code. The filename in both cases is `CHECKS` and cannot be modified. +- The environment variable `DOKKU_PROXY_PORT_MAP` has been migrated to the properties system. Direct changes to the value will be ignored, and users should interact with port maps via the `ports` plugin. ## Deprecations diff --git a/docs/configuration/environment-variables.md b/docs/configuration/environment-variables.md index e48084557..eb8c02b59 100644 --- a/docs/configuration/environment-variables.md +++ b/docs/configuration/environment-variables.md @@ -116,7 +116,6 @@ The following config variables have special meanings and can be set in a variety | `DOKKU_PARALLEL_ARGUMENTS`. | none | `dokku config:set` | Allows passing custom arguments to parallel for `ps:*all` commands | | `DOKKU_PROXY_PORT` | automatically assigned | `/etc/environment`
`~dokku/.dokkurc`
`~dokku/.dokkurc/*`
`dokku config:set` | | | `DOKKU_PROXY_SSL_PORT` | automatically assigned | `/etc/environment`
`~dokku/.dokkurc`
`~dokku/.dokkurc/*`
`dokku config:set` | | -| `DOKKU_PROXY_PORT_MAP` | automatically assigned | `dokku ports:add`
`dokku ports:remove`, `dokku ports:clear` | | | `DOKKU_SKIP_ALL_CHECKS` | none | `dokku config:set` | | | `DOKKU_SKIP_CLEANUP` | | `/etc/environment`
`~dokku/.dokkurc`
`~dokku/.dokkurc/*` | When a deploy is triggered, if this is set to a non-empty value, then old docker containers and images will not be removed. | | `DOKKU_SKIP_DEFAULT_CHECKS` | | `dokku config:set` | | diff --git a/docs/networking/port-management.md b/docs/networking/port-management.md index 9c0be5967..66b8e6dd9 100644 --- a/docs/networking/port-management.md +++ b/docs/networking/port-management.md @@ -84,8 +84,6 @@ dokku ports:add node-js-app http:8080:5000 ``` ``` ------> Setting config vars - DOKKU_PROXY_PORT_MAP: http:80:5000 http:8080:5000 -----> Configuring node-js-app.dokku.me...(using built-in template) -----> Creating http nginx.conf Reloading nginx @@ -120,8 +118,6 @@ dokku ports:set node-js-app http:8080:5000 ``` ``` ------> Setting config vars - DOKKU_PROXY_PORT_MAP: http:80:5000 http:8080:5000 -----> Configuring node-js-app.dokku.me...(using built-in template) -----> Creating http nginx.conf Reloading nginx diff --git a/docs/networking/proxies/nginx.md b/docs/networking/proxies/nginx.md index 748d87f3c..bd0c974a1 100644 --- a/docs/networking/proxies/nginx.md +++ b/docs/networking/proxies/nginx.md @@ -376,8 +376,8 @@ Unsetting this value is the same as enabling custom nginx config usage. {{ .PROXY_PORT }} Non-SSL nginx listener port (same as `DOKKU_PROXY_PORT` config var) {{ .PROXY_SSL_PORT }} SSL nginx listener port (same as `DOKKU_PROXY_SSL_PORT` config var) {{ .NOSSL_SERVER_NAME }} List of non-SSL VHOSTS -{{ .PROXY_PORT_MAP }} List of port mappings (same as `DOKKU_PROXY_PORT_MAP` config var) -{{ .PROXY_UPSTREAM_PORTS }} List of configured upstream ports (derived from `DOKKU_PROXY_PORT_MAP` config var) +{{ .PROXY_PORT_MAP }} List of port mappings (same as the `port-map` ports property) +{{ .PROXY_UPSTREAM_PORTS }} List of configured upstream ports (derived from the `port-map` ports property) {{ .RAW_TCP_PORTS }} List of exposed tcp ports as defined by Dockerfile `EXPOSE` directive (**Dockerfile apps only**) {{ .SSL_INUSE }} Boolean set when an app is SSL-enabled {{ .SSL_SERVER_NAME }} List of SSL VHOSTS diff --git a/plugins/ports/Makefile b/plugins/ports/Makefile index 189f7b899..ac8759b6e 100644 --- a/plugins/ports/Makefile +++ b/plugins/ports/Makefile @@ -1,5 +1,5 @@ SUBCOMMANDS = subcommands/list subcommands/add subcommands/clear subcommands/remove subcommands/set subcommands/report -TRIGGERS = triggers/ports-clear triggers/ports-configure triggers/ports-dockerfile-raw-tcp-ports triggers/ports-get triggers/ports-get-available triggers/post-certs-remove triggers/post-certs-update triggers/report +TRIGGERS = triggers/install triggers/ports-clear triggers/ports-configure triggers/ports-dockerfile-raw-tcp-ports triggers/ports-get triggers/ports-get-available triggers/post-app-clone-setup triggers/post-app-rename-setup triggers/post-certs-remove triggers/post-certs-update triggers/post-delete triggers/report BUILD = commands subcommands triggers PLUGIN_NAME = ports diff --git a/plugins/ports/functions.go b/plugins/ports/functions.go index 5c20be0f0..64b8e9628 100644 --- a/plugins/ports/functions.go +++ b/plugins/ports/functions.go @@ -23,10 +23,7 @@ func addPortMaps(appName string, portMaps []PortMap) error { } func clearPorts(appName string) error { - return common.EnvWrap(func() error { - keys := []string{"DOKKU_PROXY_PORT_MAP"} - return config.UnsetMany(appName, keys, false) - }, map[string]string{"DOKKU_QUIET_OUTPUT": "1"}) + return common.PropertyDelete("ports", appName, "port-map") } func doesCertExist(appName string) bool { @@ -118,8 +115,12 @@ func getGlobalProxySSLPort() int { } func getPortMaps(appName string) []PortMap { - value := config.GetWithDefault(appName, "DOKKU_PROXY_PORT_MAP", "") - portMaps, _ := parsePortMapString(value) + value, err := common.PropertyListGet("ports", appName, "port-map") + if err != nil { + return []PortMap{} + } + + portMaps, _ := parsePortMapString(strings.Join(value, " ")) return portMaps } @@ -363,8 +364,7 @@ func removePortMaps(appName string, portMaps []PortMap) error { } if len(toSet) == 0 { - keys := []string{"DOKKU_PROXY_PORT_MAP"} - return config.UnsetMany(appName, keys, false) + return common.PropertyDelete("ports", appName, "port-map") } return setPortMaps(appName, toSet) @@ -380,13 +380,8 @@ func setPortMaps(appName string, portMaps []PortMap) error { value = append(value, portMap.String()) } - return common.EnvWrap(func() error { - sort.Strings(value) - entries := map[string]string{ - "DOKKU_PROXY_PORT_MAP": strings.Join(value, " "), - } - return config.SetMany(appName, entries, false) - }, map[string]string{"DOKKU_QUIET_OUTPUT": "1"}) + sort.Strings(value) + return common.PropertyListWrite("ports", appName, "port-map", value) } func setProxyPort(appName string, port int) error { diff --git a/plugins/ports/src/triggers/triggers.go b/plugins/ports/src/triggers/triggers.go index 6551298da..f97571120 100644 --- a/plugins/ports/src/triggers/triggers.go +++ b/plugins/ports/src/triggers/triggers.go @@ -18,6 +18,8 @@ func main() { var err error switch trigger { + case "install": + err = ports.TriggerInstall() case "ports-clear": appName := flag.Arg(0) err = ports.TriggerPortsClear(appName) @@ -32,12 +34,23 @@ func main() { err = ports.TriggerPortsGet(appName) case "ports-get-available": err = ports.TriggerPortsGetAvailable() + case "post-app-clone-setup": + oldAppName := flag.Arg(0) + newAppName := flag.Arg(1) + err = ports.TriggerPostAppCloneSetup(oldAppName, newAppName) + case "post-app-rename-setup": + oldAppName := flag.Arg(0) + newAppName := flag.Arg(1) + err = ports.TriggerPostAppRenameSetup(oldAppName, newAppName) case "post-certs-remove": appName := flag.Arg(0) err = ports.TriggerPostCertsRemove(appName) case "post-certs-update": appName := flag.Arg(0) err = ports.TriggerPostCertsUpdate(appName) + case "post-delete": + appName := flag.Arg(0) + err = ports.TriggerPostDelete(appName) case "report": appName := flag.Arg(0) err = ports.ReportSingleApp(appName, "", "") diff --git a/plugins/ports/triggers.go b/plugins/ports/triggers.go index d9a8bad33..e5a9af257 100644 --- a/plugins/ports/triggers.go +++ b/plugins/ports/triggers.go @@ -7,6 +7,43 @@ import ( "github.com/dokku/dokku/plugins/config" ) +// TriggerInstall migrates the ports config to properties +func TriggerInstall() error { + if err := common.PropertySetup("ports"); err != nil { + return fmt.Errorf("Unable to install the ports plugin: %s", err.Error()) + } + + apps, err := common.UnfilteredDokkuApps() + if err != nil { + return nil + } + + for _, appName := range apps { + if common.PropertyExists("ports", appName, "port-map") { + continue + } + + common.LogInfo1(fmt.Sprintf("Migrating DOKKU_PROXY_PORT_MAP to port-map property for %s", appName)) + portMapString := config.GetWithDefault(appName, "DOKKU_PROXY_PORT_MAP", "") + portMaps, _ := parsePortMapString(portMapString) + + propertyValue := []string{} + for _, portMap := range portMaps { + propertyValue = append(propertyValue, portMap.String()) + } + + if err := common.PropertyListWrite("ports", appName, "port-map", propertyValue); err != nil { + return err + } + + if err := config.UnsetMany(appName, []string{"DOKKU_PROXY_PORT_MAP"}, false); err != nil { + return err + } + } + + return nil +} + // TriggerPortsClear removes all ports for the specified app func TriggerPortsClear(appName string) error { return clearPorts(appName) @@ -64,6 +101,29 @@ func TriggerPortsGetAvailable() error { return nil } +// TriggerPostAppCloneSetup creates new ports files +func TriggerPostAppCloneSetup(oldAppName string, newAppName string) error { + err := common.PropertyClone("ports", oldAppName, newAppName) + if err != nil { + return err + } + + return nil +} + +// TriggerPostAppRenameSetup renames ports files +func TriggerPostAppRenameSetup(oldAppName string, newAppName string) error { + if err := common.PropertyClone("ports", oldAppName, newAppName); err != nil { + return err + } + + if err := common.PropertyDestroy("ports", oldAppName); err != nil { + return err + } + + return nil +} + // TriggerPostCertsRemove unsets port config vars after SSL cert is added func TriggerPostCertsRemove(appName string) error { keys := []string{"DOKKU_PROXY_SSL_PORT"} @@ -129,3 +189,12 @@ func TriggerPostCertsUpdate(appName string) error { return nil } + +// TriggerPostDelete is the ports post-delete plugin trigger +func TriggerPostDelete(appName string) error { + if err := common.PropertyDestroy("ports", appName); err != nil { + common.LogWarn(err.Error()) + } + + return nil +} diff --git a/tests/unit/apps_2.bats b/tests/unit/apps_2.bats index ce91b5e78..d7a2a2cec 100644 --- a/tests/unit/apps_2.bats +++ b/tests/unit/apps_2.bats @@ -108,8 +108,23 @@ teardown() { } @test "(apps) apps:clone ssl-app" { - run /bin/bash -c "dokku config:set --no-restart $TEST_APP DOKKU_PROXY_PORT_MAP=https:443:5000 DOKKU_PROXY_SSL_PORT=443" - deploy_app + skip "this test always failed and requires changes in dokku to support detected vs specified functionality" + run /bin/bash -c "dokku apps:create $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + run /bin/bash -c "dokku ports:set $TEST_APP https:443:5000" + echo "output: $output" + echo "status: $status" + assert_success + run /bin/bash -c "dokku config:set --no-restart $TEST_APP DOKKU_PROXY_SSL_PORT=443" + echo "output: $output" + echo "status: $status" + assert_success + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success run /bin/bash -c "dokku apps:clone $TEST_APP app-without-ssl" echo "output: $output" echo "status: $status" diff --git a/tests/unit/nginx-vhosts_3.bats b/tests/unit/nginx-vhosts_3.bats index 0a7ad5fa7..6f7a5fbb9 100644 --- a/tests/unit/nginx-vhosts_3.bats +++ b/tests/unit/nginx-vhosts_3.bats @@ -17,23 +17,61 @@ teardown() { } @test "(nginx-vhosts) grpc endpoint" { - deploy_app gogrpc - dokku ports:add "$TEST_APP" "grpc:80:50051" + run deploy_app gogrpc + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku ports:add $TEST_APP grpc:80:50051" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku nginx:show-config $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + run /bin/bash -c "docker run --rm ${TEST_APP}-docker-image /go/bin/greeter_client -address ${TEST_APP}.dokku.me:80 -name grpc" + echo "output: $output" + echo "status: $status" + assert_success assert_output "Greeting: Hello grpc" } @test "(nginx-vhosts) grpc endpoint on a port other than 80" { - deploy_app gogrpc - dokku ports:add "$TEST_APP" "grpc:8080:50051" + run deploy_app gogrpc + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku ports:add $TEST_APP grpc:8080:50051" + echo "output: $output" + echo "status: $status" + assert_success + run /bin/bash -c "docker run --rm ${TEST_APP}-docker-image /go/bin/greeter_client -address ${TEST_APP}.dokku.me:8080 -name grpc8080" + echo "output: $output" + echo "status: $status" + assert_success assert_output "Greeting: Hello grpc8080" } @test "(nginx-vhosts) grpcs endpoint" { setup_test_tls - deploy_app gogrpc - dokku ports:add "$TEST_APP" "grpcs:443:50051" + run deploy_app gogrpc + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku ports:add $TEST_APP grpcs:443:50051" + echo "output: $output" + echo "status: $status" + assert_success + run /bin/bash -c "docker run --rm ${TEST_APP}-docker-image /go/bin/greeter_client -address ${TEST_APP}.dokku.me:443 -name grpcs -tls" + echo "output: $output" + echo "status: $status" + assert_success assert_output "Greeting: Hello grpcs" } From b44be8def90fb3cc499f414ae6b6d902292bcb49 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 16 Jul 2023 02:30:44 -0400 Subject: [PATCH 32/61] refactor: rename port-map property to port --- docs/networking/proxies/nginx.md | 4 ++-- plugins/ports/functions.go | 8 ++++---- plugins/ports/triggers.go | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/networking/proxies/nginx.md b/docs/networking/proxies/nginx.md index bd0c974a1..aa34d5b10 100644 --- a/docs/networking/proxies/nginx.md +++ b/docs/networking/proxies/nginx.md @@ -376,8 +376,8 @@ Unsetting this value is the same as enabling custom nginx config usage. {{ .PROXY_PORT }} Non-SSL nginx listener port (same as `DOKKU_PROXY_PORT` config var) {{ .PROXY_SSL_PORT }} SSL nginx listener port (same as `DOKKU_PROXY_SSL_PORT` config var) {{ .NOSSL_SERVER_NAME }} List of non-SSL VHOSTS -{{ .PROXY_PORT_MAP }} List of port mappings (same as the `port-map` ports property) -{{ .PROXY_UPSTREAM_PORTS }} List of configured upstream ports (derived from the `port-map` ports property) +{{ .PROXY_PORT_MAP }} List of port mappings (same as the `map` ports property) +{{ .PROXY_UPSTREAM_PORTS }} List of configured upstream ports (derived from the `map` ports property) {{ .RAW_TCP_PORTS }} List of exposed tcp ports as defined by Dockerfile `EXPOSE` directive (**Dockerfile apps only**) {{ .SSL_INUSE }} Boolean set when an app is SSL-enabled {{ .SSL_SERVER_NAME }} List of SSL VHOSTS diff --git a/plugins/ports/functions.go b/plugins/ports/functions.go index 64b8e9628..f13c049c4 100644 --- a/plugins/ports/functions.go +++ b/plugins/ports/functions.go @@ -23,7 +23,7 @@ func addPortMaps(appName string, portMaps []PortMap) error { } func clearPorts(appName string) error { - return common.PropertyDelete("ports", appName, "port-map") + return common.PropertyDelete("ports", appName, "map") } func doesCertExist(appName string) bool { @@ -115,7 +115,7 @@ func getGlobalProxySSLPort() int { } func getPortMaps(appName string) []PortMap { - value, err := common.PropertyListGet("ports", appName, "port-map") + value, err := common.PropertyListGet("ports", appName, "map") if err != nil { return []PortMap{} } @@ -364,7 +364,7 @@ func removePortMaps(appName string, portMaps []PortMap) error { } if len(toSet) == 0 { - return common.PropertyDelete("ports", appName, "port-map") + return common.PropertyDelete("ports", appName, "map") } return setPortMaps(appName, toSet) @@ -381,7 +381,7 @@ func setPortMaps(appName string, portMaps []PortMap) error { } sort.Strings(value) - return common.PropertyListWrite("ports", appName, "port-map", value) + return common.PropertyListWrite("ports", appName, "map", value) } func setProxyPort(appName string, port int) error { diff --git a/plugins/ports/triggers.go b/plugins/ports/triggers.go index e5a9af257..53f01fb2b 100644 --- a/plugins/ports/triggers.go +++ b/plugins/ports/triggers.go @@ -19,11 +19,11 @@ func TriggerInstall() error { } for _, appName := range apps { - if common.PropertyExists("ports", appName, "port-map") { + if common.PropertyExists("ports", appName, "map") { continue } - common.LogInfo1(fmt.Sprintf("Migrating DOKKU_PROXY_PORT_MAP to port-map property for %s", appName)) + common.LogInfo1(fmt.Sprintf("Migrating DOKKU_PROXY_PORT_MAP to ports map property for %s", appName)) portMapString := config.GetWithDefault(appName, "DOKKU_PROXY_PORT_MAP", "") portMaps, _ := parsePortMapString(portMapString) @@ -32,7 +32,7 @@ func TriggerInstall() error { propertyValue = append(propertyValue, portMap.String()) } - if err := common.PropertyListWrite("ports", appName, "port-map", propertyValue); err != nil { + if err := common.PropertyListWrite("ports", appName, "map", propertyValue); err != nil { return err } From 4bc3de554018445e389d53d70618ae445563ba1c Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 16 Jul 2023 03:35:40 -0400 Subject: [PATCH 33/61] feat: write auto-detected port mappings during a deploy During an app build, we now auto-detect ports based on the source code. This is usually http:80:5000, with Dockerfile-based deploys having their ports extracted from the docker image or Dockerfile. Additionally, we add an https:443 mapping for any detected http:80 mapping when there is an ssl certificate, and all http port mappings are transformed to https mappings for Dockerfile-based deploys. While the ports aren't currently consumed, a future refactor will provide the ability to fallback to the new detected ports when there is no user-specified port mapping. --- docs/development/plugin-triggers.md | 20 +++++- docs/networking/port-management.md | 12 ++-- plugins/20_events/ports-set-detected | 1 + plugins/builder-dockerfile/builder-build | 4 ++ plugins/builder-dockerfile/internal-functions | 44 ++++++++++++ plugins/builder-herokuish/builder-build | 1 + plugins/builder-lambda/builder-build | 1 + plugins/builder-pack/builder-build | 1 + plugins/ports/Makefile | 2 +- plugins/ports/functions.go | 48 +++++++++++++ plugins/ports/report.go | 12 +++- plugins/ports/src/triggers/triggers.go | 4 ++ plugins/ports/triggers.go | 18 +++++ tests/apps/python/expose.Dockerfile | 9 +++ tests/unit/ports.bats | 71 ++++++++++++++++++- tests/unit/test_helper.bash | 8 +++ 16 files changed, 246 insertions(+), 10 deletions(-) create mode 120000 plugins/20_events/ports-set-detected create mode 100644 tests/apps/python/expose.Dockerfile diff --git a/docs/development/plugin-triggers.md b/docs/development/plugin-triggers.md index a3ddf285f..4afe73354 100644 --- a/docs/development/plugin-triggers.md +++ b/docs/development/plugin-triggers.md @@ -1292,7 +1292,7 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x - Description: Returns a list of port mappings, newline delimited - Invoked by: Various networking plugins -- Arguments `$APP` +- Arguments: `$APP` - Example: ```shell @@ -1307,7 +1307,21 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x - Description: Prints out an available port greater than 1024 - Invoked by: Various networking plugins -- Arguments `$APP` +- Arguments: `$APP` +- Example: + +```shell +#!/usr/bin/env bash + +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x + +# TODO +``` + +### `ports-set-detected` +- Description: Allows builders to specify detected port mappings for a given app +- Invoked by: Builder plugins +- Arguments: `$APP [$PORT_MAPPING...]` - Example: ```shell @@ -1480,7 +1494,7 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x - Description: This trigger should be used to do stuff to containers after they are created but before they are started. They are explicitely for commands that may involve network traffic, and _not_ for commands that are self-contained, such as chown or tar. - Invoked by: `dokku run`, `dokku ps:rebuild`, `dokku deploy` -- Arguments "app|service" "$CONTAINER_ID" "$APP|$SERVICE" "$PHASE" +- Arguments: "app|service" "$CONTAINER_ID" "$APP|$SERVICE" "$PHASE" ```shell #!/usr/bin/env bash diff --git a/docs/networking/port-management.md b/docs/networking/port-management.md index 66b8e6dd9..f7a770de8 100644 --- a/docs/networking/port-management.md +++ b/docs/networking/port-management.md @@ -212,11 +212,14 @@ dokku ports:report ``` =====> node-js-app ports information - Port map: http:80:5000 https:443:5000 + Port map detected: http:80:5000 + Port map: http:80:5000 https:443:5000 =====> python-sample ports information - Port map: http:80:5000 + Port map detected: http:80:5000 + Port map: http:80:5000 =====> ruby-sample ports information - Port map: http:80:5000 + Port map detected: http:80:5000 + Port map: http:80:5000 ``` You can run the command for a specific app also. @@ -227,7 +230,8 @@ dokku ports:report node-js-app ``` =====> node-js-app ports information - Port map: http:80:5000 https:443:5000 + Port map detected: http:80:5000 + Port map: http:80:5000 https:443:5000 ``` You can pass flags which will output only the value of the specific information you want. For example: diff --git a/plugins/20_events/ports-set-detected b/plugins/20_events/ports-set-detected new file mode 120000 index 000000000..5178a749f --- /dev/null +++ b/plugins/20_events/ports-set-detected @@ -0,0 +1 @@ +hook \ No newline at end of file diff --git a/plugins/builder-dockerfile/builder-build b/plugins/builder-dockerfile/builder-build index c733c4480..0ff7d1f6b 100755 --- a/plugins/builder-dockerfile/builder-build +++ b/plugins/builder-dockerfile/builder-build @@ -41,6 +41,10 @@ trigger-builder-dockerfile-builder-build() { eval "$(config_export app "$APP")" "$DOCKER_BIN" image build "${DOCKER_BUILD_LABEL_ARGS[@]}" $DOKKU_GLOBAL_BUILD_ARGS "${ARG_ARRAY[@]}" ${DOKKU_DOCKER_BUILD_OPTS} -t $IMAGE . + set -x + fn-builder-dockerfile-get-detect-port-map "$APP" "$IMAGE" "$SOURCECODE_WORK_DIR/Dockerfile" + plugn trigger ports-set-detected "$APP" "$(fn-builder-dockerfile-get-detect-port-map "$APP" "$IMAGE" "$SOURCECODE_WORK_DIR/Dockerfile")" + set +x plugn trigger post-build-dockerfile "$APP" } diff --git a/plugins/builder-dockerfile/internal-functions b/plugins/builder-dockerfile/internal-functions index a230f67b3..02fe4dee1 100755 --- a/plugins/builder-dockerfile/internal-functions +++ b/plugins/builder-dockerfile/internal-functions @@ -88,3 +88,47 @@ fn-builder-dockerfile-dockerfile-path() { fn-plugin-property-get-default "builder-dockerfile" "$APP" "dockerfile-path" "" } + +fn-builder-dockerfile-get-ports-from-dockerfile() { + declare desc="return all exposed ports from passed file path" + declare DOCKERFILE_PATH="$1" + + suppress_output dos2unix "$DOCKERFILE_PATH" + local ports="$(grep -E "^EXPOSE " "$DOCKERFILE_PATH" | awk '{ print $2 }' | xargs)" || true + echo "$ports" +} + +fn-builder-dockerfile-get-ports-from-image() { + declare desc="return all exposed ports from passed image name" + declare IMAGE="$1" + + local ports="$("$DOCKER_BIN" image inspect --format "{{range $key, $value := .Config.ExposedPorts}}{{$key}} {{end}}" "$IMAGE" | xargs)" || true + echo "$ports" +} + +fn-builder-dockerfile-get-detect-port-map() { + declare desc="extracts and echos a port mapping from the app" + declare APP="$1" IMAGE="$2" DOCKERFILE_PATH="$3" + + local detected_ports=$(fn-builder-dockerfile-get-ports-from-dockerfile "$DOCKERFILE_PATH") + + if [[ -z "$detected_ports" ]]; then + local detected_ports=$(fn-builder-dockerfile-get-ports-from-image "$IMAGE") + fi + + if [[ -n "$detected_ports" ]]; then + local port_map="" + for p in $detected_ports; do + if [[ "$p" =~ .*udp.* ]]; then + p=${p//\/udp/} + port_map+="udp:$p:$p " + else + p=${p//\/tcp/} + port_map+="http:$p:$p " + fi + done + echo "$port_map" | xargs + else + echo "http:80:5000" + fi +} diff --git a/plugins/builder-herokuish/builder-build b/plugins/builder-herokuish/builder-build index 8ebc672bf..d09f6df28 100755 --- a/plugins/builder-herokuish/builder-build +++ b/plugins/builder-herokuish/builder-build @@ -91,6 +91,7 @@ trigger-builder-herokuish-builder-build() { "$DOCKER_BIN" container commit "${DOCKER_COMMIT_LABEL_ARGS[@]}" "$CID" "$IMAGE" >/dev/null plugn trigger scheduler-register-retired "$APP" "$CID" + plugn trigger ports-set-detected "$APP" "http:80:5000" plugn trigger post-build-buildpack "$APP" "$SOURCECODE_WORK_DIR" } diff --git a/plugins/builder-lambda/builder-build b/plugins/builder-lambda/builder-build index 2c1b7a107..64beb30a0 100755 --- a/plugins/builder-lambda/builder-build +++ b/plugins/builder-lambda/builder-build @@ -36,6 +36,7 @@ trigger-builder-lambda-builder-build() { cp "$SOURCECODE_WORK_DIR/Procfile" "${DOKKU_LIB_ROOT}/data/builder-lambda/$APP/$GIT_REV.Procfile" fi + plugn trigger ports-set-detected "$APP" "http:80:5000" plugn trigger post-build-lambda "$APP" } diff --git a/plugins/builder-pack/builder-build b/plugins/builder-pack/builder-build index 773ebfd80..8a2f865b1 100755 --- a/plugins/builder-pack/builder-build +++ b/plugins/builder-pack/builder-build @@ -35,6 +35,7 @@ trigger-builder-pack-builder-build() { pack build "$IMAGE" --builder "$DOKKU_CNB_BUILDER" --path "$SOURCECODE_WORK_DIR" --default-process web "${ENV_ARGS[@]}" docker-image-labeler --label=dokku --label=org.label-schema.schema-version=1.0 --label=org.label-schema.vendor=dokku --label=com.dokku.image-stage=build --label=com.dokku.builder-type=pack --label=com.dokku.app-name=$APP "$IMAGE" + plugn trigger ports-set-detected "$APP" "http:80:5000" plugn trigger post-build-pack "$APP" "$SOURCECODE_WORK_DIR" } diff --git a/plugins/ports/Makefile b/plugins/ports/Makefile index ac8759b6e..f1d13cfbd 100644 --- a/plugins/ports/Makefile +++ b/plugins/ports/Makefile @@ -1,5 +1,5 @@ SUBCOMMANDS = subcommands/list subcommands/add subcommands/clear subcommands/remove subcommands/set subcommands/report -TRIGGERS = triggers/install triggers/ports-clear triggers/ports-configure triggers/ports-dockerfile-raw-tcp-ports triggers/ports-get triggers/ports-get-available triggers/post-app-clone-setup triggers/post-app-rename-setup triggers/post-certs-remove triggers/post-certs-update triggers/post-delete triggers/report +TRIGGERS = triggers/install triggers/ports-clear triggers/ports-configure triggers/ports-dockerfile-raw-tcp-ports triggers/ports-get triggers/ports-get-available triggers/ports-set-detected triggers/post-app-clone-setup triggers/post-app-rename-setup triggers/post-certs-remove triggers/post-certs-update triggers/post-delete triggers/report BUILD = commands subcommands triggers PLUGIN_NAME = ports diff --git a/plugins/ports/functions.go b/plugins/ports/functions.go index f13c049c4..ba9358b20 100644 --- a/plugins/ports/functions.go +++ b/plugins/ports/functions.go @@ -63,6 +63,54 @@ func getAvailablePort() int { } } +func getDetectedPortMaps(appName string) []PortMap { + defaultMapping := []PortMap{ + { + ContainerPort: 5000, + HostPort: 80, + Scheme: "http", + }, + } + + portMaps := []PortMap{} + value, err := common.PropertyListGet("ports", appName, "map-detected") + if err == nil { + portMaps, _ = parsePortMapString(strings.Join(value, " ")) + } + + if len(portMaps) == 0 { + portMaps = defaultMapping + } + + if doesCertExist(appName) { + setSSLPort := false + for _, portMap := range portMaps { + if portMap.Scheme != "http" || portMap.HostPort != 80 { + continue + } + + setSSLPort = true + portMaps = append(portMaps, PortMap{ + ContainerPort: portMap.ContainerPort, + HostPort: 443, + Scheme: "https", + }) + } + + if !setSSLPort { + for i, portMap := range portMaps { + if portMap.Scheme != "http" { + continue + } + + portMaps[i].Scheme = "https" + } + } + } + + return portMaps +} + func getDockerfileRawTCPPorts(appName string) []int { b, _ := common.PlugnTriggerOutput("config-get", []string{appName, "DOKKU_DOCKERFILE_PORTS"}...) dockerfilePorts := strings.TrimSpace(string(b[:])) diff --git a/plugins/ports/report.go b/plugins/ports/report.go index bd820554d..8031478b7 100644 --- a/plugins/ports/report.go +++ b/plugins/ports/report.go @@ -13,7 +13,8 @@ func ReportSingleApp(appName string, format string, infoFlag string) error { } flags := map[string]common.ReportFunc{ - "--ports-map": reportPortMap, + "--ports-map": reportPortMap, + "--ports-map-detected": reportPortMapDetected, } flagKeys := []string{} @@ -35,3 +36,12 @@ func reportPortMap(appName string) string { return strings.Join(portMaps, " ") } + +func reportPortMapDetected(appName string) string { + var portMaps []string + for _, portMap := range getDetectedPortMaps(appName) { + portMaps = append(portMaps, portMap.String()) + } + + return strings.Join(portMaps, " ") +} diff --git a/plugins/ports/src/triggers/triggers.go b/plugins/ports/src/triggers/triggers.go index f97571120..3adc5c27a 100644 --- a/plugins/ports/src/triggers/triggers.go +++ b/plugins/ports/src/triggers/triggers.go @@ -34,6 +34,10 @@ func main() { err = ports.TriggerPortsGet(appName) case "ports-get-available": err = ports.TriggerPortsGetAvailable() + case "ports-set-detected": + appName := flag.Arg(0) + appName, portMapString := common.ShiftString(flag.Args()) + err = ports.TriggerPortsSetDetected(appName, strings.Join(portMapString, " ")) case "post-app-clone-setup": oldAppName := flag.Arg(0) newAppName := flag.Arg(1) diff --git a/plugins/ports/triggers.go b/plugins/ports/triggers.go index 53f01fb2b..0bb59ddf6 100644 --- a/plugins/ports/triggers.go +++ b/plugins/ports/triggers.go @@ -2,6 +2,7 @@ package ports import ( "fmt" + "sort" "github.com/dokku/dokku/plugins/common" "github.com/dokku/dokku/plugins/config" @@ -101,6 +102,23 @@ func TriggerPortsGetAvailable() error { return nil } +// TriggerPortsGetAvailable prints out an available port greater than 1024 +func TriggerPortsSetDetected(appName string, portMapString string) error { + portMaps, _ := parsePortMapString(portMapString) + + var value []string + for _, portMap := range uniquePortMaps(portMaps) { + if portMap.AllowsPersistence() { + continue + } + + value = append(value, portMap.String()) + } + + sort.Strings(value) + return common.PropertyListWrite("ports", appName, "map-detected", value) +} + // TriggerPostAppCloneSetup creates new ports files func TriggerPostAppCloneSetup(oldAppName string, newAppName string) error { err := common.PropertyClone("ports", oldAppName, newAppName) diff --git a/tests/apps/python/expose.Dockerfile b/tests/apps/python/expose.Dockerfile new file mode 100644 index 000000000..130cbb2fc --- /dev/null +++ b/tests/apps/python/expose.Dockerfile @@ -0,0 +1,9 @@ +FROM python:3.11.0-buster + +EXPOSE 3001/udp +EXPOSE 3000/tcp +EXPOSE 3003 + +COPY . /app + +WORKDIR /app diff --git a/tests/unit/ports.bats b/tests/unit/ports.bats index 2bef9aa78..772deca21 100644 --- a/tests/unit/ports.bats +++ b/tests/unit/ports.bats @@ -90,7 +90,7 @@ teardown() { assert_output "http 80 5000" } -@test "(ports) ports:add (post-deploy add)" { +@test "(ports:add) post-deploy add" { deploy_app run /bin/bash -c "dokku ports:add $TEST_APP http:8080:5000 http:8081:5000" echo "output: $output" @@ -104,3 +104,72 @@ teardown() { assert_http_success "http://$TEST_APP.dokku.me:8080" assert_http_success "http://$TEST_APP.dokku.me:8081" } + +@test "(ports:report) herokuish tls" { + run /bin/bash -c "dokku builder-herokuish:set $TEST_APP allowed true" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku ports:report $TEST_APP --ports-map" + echo "output: $output" + echo "status: $status" + assert_output "" + + run /bin/bash -c "dokku ports:report $TEST_APP --ports-map-detected" + echo "output: $output" + echo "status: $status" + assert_output "http:80:5000" + + run setup_test_tls + echo "output: $output" + echo "status: $status" + assert_success + + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku ports:report $TEST_APP --ports-map" + echo "output: $output" + echo "status: $status" + assert_output "http:80:5000 https:443:5000" + + run /bin/bash -c "dokku ports:report $TEST_APP --ports-map-detected" + echo "output: $output" + echo "status: $status" + assert_output "http:80:5000 https:443:5000" +} + +@test "(ports:report) dockerfile tls" { + run deploy_app python dokku@dokku.me:$TEST_APP move_expose_dockerfile_into_place + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku ports:report $TEST_APP --ports-map" + echo "output: $output" + echo "status: $status" + assert_output "http:3000:3000 http:3003:3003" + + run /bin/bash -c "dokku ports:report $TEST_APP --ports-map-detected" + echo "output: $output" + echo "status: $status" + assert_output "http:3000:3000 http:3003:3003 udp:3001:3001" + + run setup_test_tls + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku ports:report $TEST_APP --ports-map" + echo "output: $output" + echo "status: $status" + assert_output "http:3000:3000 http:3003:3003" + + run /bin/bash -c "dokku ports:report $TEST_APP --ports-map-detected" + echo "output: $output" + echo "status: $status" + assert_output "https:3000:3000 https:3003:3003 udp:3001:3001" +} diff --git a/tests/unit/test_helper.bash b/tests/unit/test_helper.bash index fdcd3cb3a..76b261000 100644 --- a/tests/unit/test_helper.bash +++ b/tests/unit/test_helper.bash @@ -529,6 +529,14 @@ move_dockerfile_into_place() { mv "$APP_REPO_DIR/alt.Dockerfile" "$APP_REPO_DIR/Dockerfile" } +move_expose_dockerfile_into_place() { + local APP="$1" + local APP_REPO_DIR="$2" + [[ -z "$APP" ]] && local APP="$TEST_APP" + cat "$APP_REPO_DIR/expose.Dockerfile" + mv "$APP_REPO_DIR/expose.Dockerfile" "$APP_REPO_DIR/Dockerfile" +} + add_requirements_txt() { local APP="$1" local APP_REPO_DIR="$2" From 3f127a7cc4410c6d2fe308bbb6b010dbb9eb6575 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Tue, 25 Jul 2023 00:36:38 -0400 Subject: [PATCH 34/61] feat: inject docker labels when nginx proxy implementation is in use This will allow users to switch to a docker label-based nginx proxy server transparently without rebuilds (after the first one), which would mimic how the caddy, haproxy, and traefik proxy implementations work. See https://github.com/dokku/nginx-docker-proxy for an example server implementation. --- plugins/nginx-vhosts/command-functions | 20 +++---- .../nginx-vhosts/docker-args-process-deploy | 58 +++++++++++++++++++ plugins/nginx-vhosts/functions | 54 +++++++++++++++-- plugins/nginx-vhosts/internal-functions | 21 ++++++- 4 files changed, 134 insertions(+), 19 deletions(-) create mode 100755 plugins/nginx-vhosts/docker-args-process-deploy diff --git a/plugins/nginx-vhosts/command-functions b/plugins/nginx-vhosts/command-functions index 78ab2c6e9..a397e1429 100755 --- a/plugins/nginx-vhosts/command-functions +++ b/plugins/nginx-vhosts/command-functions @@ -39,17 +39,17 @@ cmd-nginx-report-single() { local flag_map=( "--nginx-access-log-format: $(fn-nginx-access-log-format "$APP")" "--nginx-access-log-path: $(fn-nginx-access-log-path "$APP")" - "--nginx-bind-address-ipv4: $(fn-plugin-property-get-default "nginx" "$APP" "bind-address-ipv4" "")" - "--nginx-bind-address-ipv6: $(fn-plugin-property-get-default "nginx" "$APP" "bind-address-ipv6" "::")" - "--nginx-client-max-body-size: $(fn-plugin-property-get-default "nginx" "$APP" "client-max-body-size" "")" + "--nginx-bind-address-ipv4: $(fn-nginx-bind-address-ipv4 "$APP")" + "--nginx-bind-address-ipv6: $(fn-nginx-bind-address-ipv6 "$APP")" + "--nginx-client-max-body-size: $(fn-nginx-client-max-body-size "$APP")" "--nginx-disable-custom-config: $(fn-plugin-property-get-default "nginx" "$APP" "disable-custom-config" "false")" "--nginx-error-log-path: $(fn-nginx-error-log-path "$APP")" "--nginx-global-hsts: $(fn-plugin-property-get-default "nginx" "--global" "hsts" "true")" "--nginx-computed-hsts: $(fn-nginx-hsts-is-enabled "$APP")" "--nginx-hsts: $(fn-plugin-property-get-default "nginx" "$APP" "hsts" "")" - "--nginx-hsts-include-subdomains: $(fn-plugin-property-get-default "nginx" "$APP" "hsts-include-subdomains" "true")" - "--nginx-hsts-max-age: $(fn-plugin-property-get-default "nginx" "$APP" "hsts-max-age" "15724800")" - "--nginx-hsts-preload: $(fn-plugin-property-get-default "nginx" "$APP" "hsts-preload" "false")" + "--nginx-hsts-include-subdomains: $(fn-nginx-hsts-include-subdomains "$APP")" + "--nginx-hsts-max-age: $(fn-nginx-hsts-max-age "$APP")" + "--nginx-hsts-preload: $(fn-nginx-hsts-preload "$APP")" "--nginx-computed-nginx-conf-sigil-path: $(fn-nginx-computed-nginx-conf-sigil-path "$APP")" "--nginx-global-nginx-conf-sigil-path: $(fn-nginx-global-nginx-conf-sigil-path)" "--nginx-nginx-conf-sigil-path: $(fn-nginx-nginx-conf-sigil-path "$APP")" @@ -59,10 +59,10 @@ cmd-nginx-report-single() { "--nginx-proxy-busy-buffers-size: $(fn-nginx-proxy-busy-buffers-size "$APP")" "--nginx-proxy-read-timeout: $(fn-nginx-proxy-read-timeout "$APP")" "--nginx-last-visited-at: $(fn-nginx-vhosts-last-visited-at "$APP")" - "--nginx-x-forwarded-for-value: $(fn-plugin-property-get-default "nginx" "$APP" "x-forwarded-for-value" "\$remote_addr")" - "--nginx-x-forwarded-port-value: $(fn-plugin-property-get-default "nginx" "$APP" "x-forwarded-port-value" "\$server_port")" - "--nginx-x-forwarded-proto-value: $(fn-plugin-property-get-default "nginx" "$APP" "x-forwarded-proto-value" "\$scheme")" - "--nginx-x-forwarded-ssl: $(fn-plugin-property-get-default "nginx" "$APP" "x-forwarded-ssl" "")" + "--nginx-x-forwarded-for-value: $(fn-nginx-x-forwarded-for-value "$APP")" + "--nginx-x-forwarded-port-value: $(fn-nginx-x-forwarded-port-value "$APP")" + "--nginx-x-forwarded-proto-value: $(fn-nginx-x-forwarded-proto-value "$APP")" + "--nginx-x-forwarded-ssl: $(fn-nginx-x-forwarded-ssl "$APP")" ) if [[ -z "$INFO_FLAG" ]]; then diff --git a/plugins/nginx-vhosts/docker-args-process-deploy b/plugins/nginx-vhosts/docker-args-process-deploy new file mode 100755 index 000000000..a20e10819 --- /dev/null +++ b/plugins/nginx-vhosts/docker-args-process-deploy @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +source "$PLUGIN_AVAILABLE_PATH/nginx-vhosts/functions" + +trigger-nginx-vhosts-docker-args-process-deploy() { + declare desc="nginx-vhosts docker-args-process-deploy plugin trigger" + declare trigger="docker-args-process-deploy" + declare APP="$1" IMAGE_SOURCE_TYPE="$2" IMAGE_TAG="$3" PROC_TYPE="$4" CONTAINER_INDEX="$5" + local output + local STDIN=$(cat) + + if [[ "$PROC_TYPE" != "web" ]]; then + return + fi + + if [[ "$(plugn trigger proxy-type "$APP")" != "nginx" ]]; then + return + fi + + if [[ "$(plugn trigger proxy-is-enabled "$APP")" != "true" ]]; then + return + fi + + if ! plugn trigger domains-vhost-enabled "$APP" 2>/dev/null; then + return + fi + + # ensure we have a port mapping + plugn trigger ports-configure "$APP" + + output="$output '--label=nginx.access-log-format=$(fn-nginx-access-log-format "$APP")'" + output="$output '--label=nginx.bind-address-ipv4=$(fn-nginx-bind-address-ipv4 "$APP")'" + output="$output '--label=nginx.bind-address-ipv6=$(fn-nginx-bind-address-ipv6 "$APP")'" + output="$output '--label=nginx.client-max-body-size=$(fn-nginx-client-max-body-size "$APP")'" + output="$output '--label=nginx.hsts-include-subdomains=$(fn-nginx-hsts-include-subdomains "$APP")'" + output="$output '--label=nginx.hsts-max-age=$(fn-nginx-hsts-max-age "$APP")'" + output="$output '--label=nginx.hsts-preload=$(fn-nginx-hsts-preload "$APP")'" + output="$output '--label=nginx.hsts=$(fn-nginx-hsts-is-enabled "$APP")'" + output="$output '--label=nginx.https-port=443'" + output="$output '--label=nginx.domains=$(plugn trigger domains-list "$APP" | xargs)'" + output="$output '--label=nginx.initial-network=$(plugn trigger network-get-property "$APP" initial-network)'" + output="$output '--label=nginx.port-mapping=$(plugn trigger ports-get "$APP" | xargs)'" + output="$output '--label=nginx.proxy-buffer-size=$(fn-nginx-proxy-buffer-size "$APP")'" + output="$output '--label=nginx.proxy-buffering=$(fn-nginx-proxy-buffering "$APP")'" + output="$output '--label=nginx.proxy-buffers=$(fn-nginx-proxy-buffers "$APP")'" + output="$output '--label=nginx.proxy-busy-buffer-size=$(fn-nginx-proxy-busy-buffers-size "$APP")'" + output="$output '--label=nginx.proxy-read-timeout=$(fn-nginx-proxy-read-timeout "$APP")'" + output="$output '--label=nginx.x-forwarded-for-value=$(fn-nginx-x-forwarded-for-value "$APP")'" + output="$output '--label=nginx.x-forwarded-port-value=$(fn-nginx-x-forwarded-port-value "$APP")'" + output="$output '--label=nginx.x-forwarded-proto-value=$(fn-nginx-x-forwarded-proto-value "$APP")'" + output="$output '--label=nginx.x-forwarded-ssl=$(fn-nginx-x-forwarded-ssl "$APP")'" + + echo -n "$STDIN$output" +} + +trigger-nginx-vhosts-docker-args-process-deploy "$@" diff --git a/plugins/nginx-vhosts/functions b/plugins/nginx-vhosts/functions index 9f65f1be2..902f3eb7d 100755 --- a/plugins/nginx-vhosts/functions +++ b/plugins/nginx-vhosts/functions @@ -28,6 +28,20 @@ fn-nginx-access-log-path() { fn-plugin-property-get-default "nginx" "$APP" "access-log-path" "${NGINX_LOG_ROOT}/${APP}-access.log" } +fn-nginx-bind-address-ipv4() { + declare desc="get the configured ipv4 bind address" + declare APP="$1" + + fn-plugin-property-get-default "nginx" "$APP" "bind-address-ipv4" "" +} + +fn-nginx-bind-address-ipv6() { + declare desc="get the configured ipv6 bind address" + declare APP="$1" + + fn-plugin-property-get-default "nginx" "$APP" "bind-address-ipv6" "::" +} + fn-nginx-proxy-buffer-size() { declare desc="get the configured proxy buffer size" declare APP="$1" @@ -78,6 +92,34 @@ fn-nginx-error-log-path() { fn-plugin-property-get-default "nginx" "$APP" "error-log-path" "${NGINX_LOG_ROOT}/${APP}-error.log" } +fn-nginx-x-forwarded-for-value() { + declare desc="get the configured x-forwarded-for value" + declare APP="$1" + + fn-plugin-property-get-default "nginx" "$APP" "x-forwarded-for-value" "\$remote_addr" +} + +fn-nginx-x-forwarded-port-value() { + declare desc="get the configured x-forwarded-port value" + declare APP="$1" + + fn-plugin-property-get-default "nginx" "$APP" "x-forwarded-port-value" "\$server_port" +} + +fn-nginx-x-forwarded-proto-value() { + declare desc="get the configured x-forwarded-proto value" + declare APP="$1" + + fn-plugin-property-get-default "nginx" "$APP" "x-forwarded-proto-value" "\$scheme" +} + +fn-nginx-x-forwarded-ssl() { + declare desc="get the configured x-forwarded-ssl value" + declare APP="$1" + + fn-plugin-property-get-default "nginx" "$APP" "x-forwarded-ssl" "" +} + get_nginx_location() { declare desc="check that nginx is at the expected location and return it" fn-nginx-vhosts-nginx-location @@ -413,14 +455,14 @@ nginx_build_config() { local NGINX_TEMPLATE_SOURCE="app-supplied" fi - local NGINX_BIND_ADDRESS_IP4="$(fn-plugin-property-get-default "nginx" "$APP" "bind-address-ipv4" "")" - local NGINX_BIND_ADDRESS_IP6="$(fn-plugin-property-get-default "nginx" "$APP" "bind-address-ipv6" "::")" + local NGINX_BIND_ADDRESS_IP4="$(fn-nginx-bind-address-ipv4 "$APP")" + local NGINX_BIND_ADDRESS_IP6="$(fn-nginx-bind-address-ipv6 "$APP")" [[ -z "$NGINX_BIND_ADDRESS_IP6" ]] && NGINX_BIND_ADDRESS_IP6="::" - local PROXY_X_FORWARDED_FOR="$(fn-plugin-property-get-default "nginx" "$APP" "x-forwarded-for-value" "\$remote_addr")" - local PROXY_X_FORWARDED_PORT="$(fn-plugin-property-get-default "nginx" "$APP" "x-forwarded-port-value" "\$server_port")" - local PROXY_X_FORWARDED_PROTO="$(fn-plugin-property-get-default "nginx" "$APP" "x-forwarded-proto-value" "\$scheme")" - local PROXY_X_FORWARDED_SSL="$(fn-plugin-property-get-default "nginx" "$APP" "x-forwarded-ssl" "")" + local PROXY_X_FORWARDED_FOR="$(fn-nginx-x-forwarded-for-value "$APP")" + local PROXY_X_FORWARDED_PORT="$(fn-nginx-x-forwarded-port-value "$APP")" + local PROXY_X_FORWARDED_PROTO="$(fn-nginx-x-forwarded-proto-value "$APP")" + local PROXY_X_FORWARDED_SSL="$(fn-nginx-x-forwarded-ssl "$APP")" eval "$(config_export app "$APP")" local SIGIL_PARAMS=(-f "$NGINX_TEMPLATE" APP="$APP" DOKKU_ROOT="$DOKKU_ROOT" diff --git a/plugins/nginx-vhosts/internal-functions b/plugins/nginx-vhosts/internal-functions index a653499ef..c2221e009 100755 --- a/plugins/nginx-vhosts/internal-functions +++ b/plugins/nginx-vhosts/internal-functions @@ -13,6 +13,21 @@ fn-nginx-vhosts-last-visited-at() { fi } +fn-nginx-hsts-include-subdomains() { + declare APP="$1" + fn-plugin-property-get-default "nginx" "$APP" "hsts-include-subdomains" "true" +} + +fn-nginx-hsts-max-age() { + declare APP="$1" + fn-plugin-property-get-default "nginx" "$APP" "hsts-max-age" "15724800" +} + +fn-nginx-hsts-preload() { + declare APP="$1" + fn-plugin-property-get-default "nginx" "$APP" "hsts-preload" "false" +} + fn-nginx-hsts-is-enabled() { declare APP="$1" local hsts_is_enabled="$(fn-plugin-property-get-default "nginx" "$APP" "hsts" "")" @@ -49,9 +64,9 @@ fn-nginx-nginx-conf-sigil-path() { fn-nginx-vhosts-manage-hsts() { declare APP="$1" SSL_ENABLED="$2" local HSTS="$(fn-nginx-hsts-is-enabled "$APP")" - local HSTS_INCLUDE_SUBDOMAINS="$(fn-plugin-property-get-default "nginx" "$APP" "hsts-include-subdomains" "true")" - local HSTS_MAX_AGE="$(fn-plugin-property-get-default "nginx" "$APP" "hsts-max-age" "15724800")" - local HSTS_PRELOAD="$(fn-plugin-property-get-default "nginx" "$APP" "hsts-preload" "false")" + local HSTS_INCLUDE_SUBDOMAINS="$(fn-nginx-hsts-include-subdomains "$APP")" + local HSTS_MAX_AGE="$(fn-nginx-hsts-max-age "$APP")" + local HSTS_PRELOAD="$(fn-nginx-hsts-preload "$APP")" local NGINX_HSTS_CONF="$DOKKU_ROOT/$APP/nginx.conf.d/hsts.conf" local HSTS_TEMPLATE="$PLUGIN_AVAILABLE_PATH/nginx-vhosts/templates/hsts.conf.sigil" From 2b1e173c038c7037a4fb7c15872880b46758074e Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 30 Jul 2023 02:59:16 -0400 Subject: [PATCH 35/61] fix: do not write empty values for nginx container labels This ensures the nginx-docker-proxy project doesn't use the empty values as explicit values. --- .../nginx-vhosts/docker-args-process-deploy | 63 ++++++++++++------- 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/plugins/nginx-vhosts/docker-args-process-deploy b/plugins/nginx-vhosts/docker-args-process-deploy index a20e10819..78b06cb98 100755 --- a/plugins/nginx-vhosts/docker-args-process-deploy +++ b/plugins/nginx-vhosts/docker-args-process-deploy @@ -30,27 +30,48 @@ trigger-nginx-vhosts-docker-args-process-deploy() { # ensure we have a port mapping plugn trigger ports-configure "$APP" - output="$output '--label=nginx.access-log-format=$(fn-nginx-access-log-format "$APP")'" - output="$output '--label=nginx.bind-address-ipv4=$(fn-nginx-bind-address-ipv4 "$APP")'" - output="$output '--label=nginx.bind-address-ipv6=$(fn-nginx-bind-address-ipv6 "$APP")'" - output="$output '--label=nginx.client-max-body-size=$(fn-nginx-client-max-body-size "$APP")'" - output="$output '--label=nginx.hsts-include-subdomains=$(fn-nginx-hsts-include-subdomains "$APP")'" - output="$output '--label=nginx.hsts-max-age=$(fn-nginx-hsts-max-age "$APP")'" - output="$output '--label=nginx.hsts-preload=$(fn-nginx-hsts-preload "$APP")'" - output="$output '--label=nginx.hsts=$(fn-nginx-hsts-is-enabled "$APP")'" - output="$output '--label=nginx.https-port=443'" - output="$output '--label=nginx.domains=$(plugn trigger domains-list "$APP" | xargs)'" - output="$output '--label=nginx.initial-network=$(plugn trigger network-get-property "$APP" initial-network)'" - output="$output '--label=nginx.port-mapping=$(plugn trigger ports-get "$APP" | xargs)'" - output="$output '--label=nginx.proxy-buffer-size=$(fn-nginx-proxy-buffer-size "$APP")'" - output="$output '--label=nginx.proxy-buffering=$(fn-nginx-proxy-buffering "$APP")'" - output="$output '--label=nginx.proxy-buffers=$(fn-nginx-proxy-buffers "$APP")'" - output="$output '--label=nginx.proxy-busy-buffer-size=$(fn-nginx-proxy-busy-buffers-size "$APP")'" - output="$output '--label=nginx.proxy-read-timeout=$(fn-nginx-proxy-read-timeout "$APP")'" - output="$output '--label=nginx.x-forwarded-for-value=$(fn-nginx-x-forwarded-for-value "$APP")'" - output="$output '--label=nginx.x-forwarded-port-value=$(fn-nginx-x-forwarded-port-value "$APP")'" - output="$output '--label=nginx.x-forwarded-proto-value=$(fn-nginx-x-forwarded-proto-value "$APP")'" - output="$output '--label=nginx.x-forwarded-ssl=$(fn-nginx-x-forwarded-ssl "$APP")'" + value="$(fn-nginx-access-log-format "$APP")" + [[ -n "$value" ]] && output="$output '--label=nginx.access-log-format=$value'" + value="$(fn-nginx-bind-address-ipv4 "$APP")" + [[ -n "$value" ]] && output="$output '--label=nginx.bind-address-ipv4=$value'" + value="$(fn-nginx-bind-address-ipv6 "$APP")" + [[ -n "$value" ]] && output="$output '--label=nginx.bind-address-ipv6=$value'" + value="$(fn-nginx-client-max-body-size "$APP")" + [[ -n "$value" ]] && output="$output '--label=nginx.client-max-body-size=$value'" + value="$(fn-nginx-hsts-include-subdomains "$APP")" + [[ -n "$value" ]] && output="$output '--label=nginx.hsts-include-subdomains=$value'" + value="$(fn-nginx-hsts-max-age "$APP")" + [[ -n "$value" ]] && output="$output '--label=nginx.hsts-max-age=$value'" + value="$(fn-nginx-hsts-preload "$APP")" + [[ -n "$value" ]] && output="$output '--label=nginx.hsts-preload=$value'" + value="$(fn-nginx-hsts-is-enabled "$APP")" + [[ -n "$value" ]] && output="$output '--label=nginx.hsts=$value'" + value="443" + [[ -n "$value" ]] && output="$output '--label=nginx.https-port=$value'" + value="$(plugn trigger domains-list "$APP" | xargs)" + [[ -n "$value" ]] && output="$output '--label=nginx.domains=$value'" + value="$(plugn trigger network-get-property "$APP" initial-network)" + [[ -n "$value" ]] && output="$output '--label=nginx.initial-network=$value'" + value="$(plugn trigger ports-get "$APP" | xargs)" + [[ -n "$value" ]] && output="$output '--label=nginx.port-mapping=$value'" + value="$(fn-nginx-proxy-buffer-size "$APP")" + [[ -n "$value" ]] && output="$output '--label=nginx.proxy-buffer-size=$value'" + value="$(fn-nginx-proxy-buffering "$APP")" + [[ -n "$value" ]] && output="$output '--label=nginx.proxy-buffering=$value'" + value="$(fn-nginx-proxy-buffers "$APP")" + [[ -n "$value" ]] && output="$output '--label=nginx.proxy-buffers=$value'" + value="$(fn-nginx-proxy-busy-buffers-size "$APP")" + [[ -n "$value" ]] && output="$output '--label=nginx.proxy-busy-buffer-size=$value'" + value="$(fn-nginx-proxy-read-timeout "$APP")" + [[ -n "$value" ]] && output="$output '--label=nginx.proxy-read-timeout=$value'" + value="$(fn-nginx-x-forwarded-for-value "$APP")" + [[ -n "$value" ]] && output="$output '--label=nginx.x-forwarded-for-value=$value'" + value="$(fn-nginx-x-forwarded-port-value "$APP")" + [[ -n "$value" ]] && output="$output '--label=nginx.x-forwarded-port-value=$value'" + value="$(fn-nginx-x-forwarded-proto-value "$APP")" + [[ -n "$value" ]] && output="$output '--label=nginx.x-forwarded-proto-value=$value'" + value="$(fn-nginx-x-forwarded-ssl "$APP")" + [[ -n "$value" ]] && output="$output '--label=nginx.x-forwarded-ssl=$value'" echo -n "$STDIN$output" } From 04905bf5534165e9d2888cf043e5417a7896e6e1 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 31 Jul 2023 00:19:51 -0400 Subject: [PATCH 36/61] fix: run go mod update --- plugins/ports/go.mod | 2 +- plugins/ports/go.sum | 10 +++++----- plugins/repo/go.sum | 2 -- plugins/scheduler-docker-local/go.mod | 2 +- plugins/scheduler-docker-local/go.sum | 10 +++++----- 5 files changed, 12 insertions(+), 14 deletions(-) diff --git a/plugins/ports/go.mod b/plugins/ports/go.mod index 2ab83576b..c1f98fcbe 100644 --- a/plugins/ports/go.mod +++ b/plugins/ports/go.mod @@ -15,7 +15,7 @@ require ( github.com/joho/godotenv v1.2.0 // indirect github.com/otiai10/copy v1.12.0 // indirect golang.org/x/sync v0.3.0 // indirect - golang.org/x/sys v0.8.0 // indirect + golang.org/x/sys v0.10.0 // indirect ) replace github.com/dokku/dokku/plugins/common => ../common diff --git a/plugins/ports/go.sum b/plugins/ports/go.sum index 1a7ae7db2..763fb9de1 100644 --- a/plugins/ports/go.sum +++ b/plugins/ports/go.sum @@ -5,7 +5,7 @@ github.com/codeskyblue/go-sh v0.0.0-20190412065543-76bd3d59ff27/go.mod h1:VQx0hj github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/joho/godotenv v1.2.0 h1:vGTvz69FzUFp+X4/bAkb0j5BoLC+9bpqTWY8mjhA9pc= github.com/joho/godotenv v1.2.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= -github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= +github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/otiai10/copy v1.12.0 h1:cLMgSQnXBs1eehF0Wy/FAGsgDTDmAqFR7rQylBb1nDY= github.com/otiai10/copy v1.12.0/go.mod h1:rSaLseMUsZFFbsFGc7wCJnnkTAvdc5L6VWxPE4308Ww= github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks= @@ -13,10 +13,10 @@ github.com/ryanuber/columnize v2.1.2+incompatible h1:C89EOx/XBWwIXl8wm8OPJBd7kPF github.com/ryanuber/columnize v2.1.2+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/plugins/repo/go.sum b/plugins/repo/go.sum index 763fb9de1..3a1e768e2 100644 --- a/plugins/repo/go.sum +++ b/plugins/repo/go.sum @@ -3,8 +3,6 @@ github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcju github.com/codeskyblue/go-sh v0.0.0-20190412065543-76bd3d59ff27 h1:HHUr4P/aKh4quafGxDT9LDasjGdlGkzLbfmmrlng3kA= github.com/codeskyblue/go-sh v0.0.0-20190412065543-76bd3d59ff27/go.mod h1:VQx0hjo2oUeQkQUET7wRwradO6f+fN5jzXgB/zROxxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/joho/godotenv v1.2.0 h1:vGTvz69FzUFp+X4/bAkb0j5BoLC+9bpqTWY8mjhA9pc= -github.com/joho/godotenv v1.2.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/otiai10/copy v1.12.0 h1:cLMgSQnXBs1eehF0Wy/FAGsgDTDmAqFR7rQylBb1nDY= github.com/otiai10/copy v1.12.0/go.mod h1:rSaLseMUsZFFbsFGc7wCJnnkTAvdc5L6VWxPE4308Ww= diff --git a/plugins/scheduler-docker-local/go.mod b/plugins/scheduler-docker-local/go.mod index b1f47636a..d0890f313 100644 --- a/plugins/scheduler-docker-local/go.mod +++ b/plugins/scheduler-docker-local/go.mod @@ -17,7 +17,7 @@ require ( github.com/otiai10/copy v1.12.0 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect github.com/ryanuber/columnize v2.1.2+incompatible // indirect - golang.org/x/sys v0.8.0 // indirect + golang.org/x/sys v0.10.0 // indirect mvdan.cc/sh/v3 v3.7.0 // indirect ) diff --git a/plugins/scheduler-docker-local/go.sum b/plugins/scheduler-docker-local/go.sum index 3a0b55f7b..85df35feb 100644 --- a/plugins/scheduler-docker-local/go.sum +++ b/plugins/scheduler-docker-local/go.sum @@ -10,7 +10,7 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= -github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= +github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/otiai10/copy v1.12.0 h1:cLMgSQnXBs1eehF0Wy/FAGsgDTDmAqFR7rQylBb1nDY= github.com/otiai10/copy v1.12.0/go.mod h1:rSaLseMUsZFFbsFGc7wCJnnkTAvdc5L6VWxPE4308Ww= github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks= @@ -19,12 +19,12 @@ github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzG github.com/rogpeppe/go-internal v1.10.1-0.20230524175051-ec119421bb97 h1:3RPlVWzZ/PDqmVuf/FKHARG5EMid/tl7cv54Sw/QRVY= github.com/ryanuber/columnize v2.1.2+incompatible h1:C89EOx/XBWwIXl8wm8OPJBd7kPF25UfsK2X7Ph/zCAk= github.com/ryanuber/columnize v2.1.2+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= mvdan.cc/sh/v3 v3.7.0 h1:lSTjdP/1xsddtaKfGg7Myu7DnlHItd3/M2tomOcNNBg= mvdan.cc/sh/v3 v3.7.0/go.mod h1:K2gwkaesF/D7av7Kxl0HbF5kGOd2ArupNTX3X44+8l8= From 5717e71755c5fd3ddf95cbcb265650b651e7b2d2 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 31 Jul 2023 12:25:29 -0400 Subject: [PATCH 37/61] tests: use an alternative curl request to ensure requests go to the local nginx The alternative is to rely on DNS and /etc/hosts rules, which isn't quite great because it requires modifying the system. --- tests/unit/nginx-vhosts_5.bats | 9 ++++----- tests/unit/nginx-vhosts_7.bats | 2 +- tests/unit/test_helper.bash | 9 +++++++++ 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/tests/unit/nginx-vhosts_5.bats b/tests/unit/nginx-vhosts_5.bats index c48ea3855..76f967b7c 100644 --- a/tests/unit/nginx-vhosts_5.bats +++ b/tests/unit/nginx-vhosts_5.bats @@ -31,11 +31,10 @@ teardown() { check_urls http://${TEST_APP}.dokku.me:3003 check_urls http://www.test.app.dokku.me:3000 check_urls http://www.test.app.dokku.me:3003 - assert_http_success http://${TEST_APP}.dokku.me:3000 - assert_http_success http://${TEST_APP}.dokku.me:3003 - assert_http_success http://www.test.app.dokku.me:3000 - assert_http_success http://www.test.app.dokku.me:3003 - + assert_http_localhost_success "http" "${TEST_APP}.dokku.me" "3000" + assert_http_localhost_success "http" "${TEST_APP}.dokku.me" "3003" + assert_http_localhost_success "http" "www.test.app.dokku.me" "3000" + assert_http_localhost_success "http" "www.test.app.dokku.me" "3003" } @test "(nginx-vhosts) proxy:build-config (multiple networks)" { diff --git a/tests/unit/nginx-vhosts_7.bats b/tests/unit/nginx-vhosts_7.bats index 1cd3eca3b..94c20244b 100644 --- a/tests/unit/nginx-vhosts_7.bats +++ b/tests/unit/nginx-vhosts_7.bats @@ -39,7 +39,7 @@ teardown() { assert_success assert_nonssl_domain "www.test.app.dokku.me" - assert_http_success "customtemplate.dokku.me" + assert_http_localhost_success "http" "customtemplate.dokku.me" run /bin/bash -c "dokku nginx:show-config $TEST_APP" echo "output: $output" diff --git a/tests/unit/test_helper.bash b/tests/unit/test_helper.bash index 76b261000..3cc86a6c0 100644 --- a/tests/unit/test_helper.bash +++ b/tests/unit/test_helper.bash @@ -223,6 +223,15 @@ assert_http_success() { assert_output "200" } +assert_http_localhost_success() { + local scheme="$1" domain="$2" port="${3:-80}" path="${4:-}" + run curl --connect-to "$domain:$port:localhost:$port" -kSso /dev/null -w "%{http_code}" "$scheme://$domain:$port$path" + echo "curl: curl --connect-to $domain:$port:localhost:$port-kSso /dev/null -w %{http_code} $scheme://$domain:$port$path" + echo "output: $output" + echo "status: $status" + assert_output "200" +} + assert_ssl_domain() { local domain=$1 assert_app_domain "${domain}" From 1467aaea744d4150e441c73dc83fb97fa8cb97ff Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 31 Jul 2023 12:27:23 -0400 Subject: [PATCH 38/61] tests: fix issue where networks weren't being torn down during testing Due to how the variable was assigned, there were cases where the network was specified twice with a := as a delimiter. This would result in no network being removed. --- tests/unit/test_helper.bash | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/unit/test_helper.bash b/tests/unit/test_helper.bash index 3cc86a6c0..4e6dedb6f 100644 --- a/tests/unit/test_helper.bash +++ b/tests/unit/test_helper.bash @@ -554,42 +554,42 @@ add_requirements_txt() { } create_network() { - local NETWORK_NAME="$1:=$TEST_NETWORK" + local NETWORK_NAME="${1:-$TEST_NETWORK}" NETWORK=$(docker network ls -q -f name="$NETWORK_NAME") [[ -z "$NETWORK" ]] && docker network create "$NETWORK_NAME" } attach_network() { - local NETWORK_NAME="$1:=$TEST_NETWORK" + local NETWORK_NAME="${1:-$TEST_NETWORK}" NETWORK=$(docker network ls -q -f name="$NETWORK_NAME") [[ -n "$NETWORK" ]] && docker network connect "$NETWORK_NAME" "${TEST_APP}.web.1" } create_attach_network() { - local NETWORK_NAME="$1:=$TEST_NETWORK" + local NETWORK_NAME="${1:-$TEST_NETWORK}" create_network "$NETWORK_NAME" attach_network "$NETWORK_NAME" } delete_network() { - local NETWORK_NAME="$1:=$TEST_NETWORK" + local NETWORK_NAME="${1:-$TEST_NETWORK}" NETWORK=$(docker network ls -q -f name="$NETWORK_NAME") [[ -n "$NETWORK" ]] && docker network rm "$NETWORK_NAME" } detach_network() { - local NETWORK_NAME="$1:=$TEST_NETWORK" + local NETWORK_NAME="${1:-$TEST_NETWORK}" NETWORK=$(docker network ls -q -f name="$NETWORK_NAME") [[ -z "$NETWORK" ]] && docker network disconnect "$NETWORK_NAME" "${TEST_APP}.web.1" } detach_delete_network() { - local NETWORK_NAME="$1:=$TEST_NETWORK" + local NETWORK_NAME="${1:-$TEST_NETWORK}" detach_network "$NETWORK_NAME" delete_network "$NETWORK_NAME" From d6468f471380042d0af9fdf3da3568f04c7a1472 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 31 Jul 2023 12:31:31 -0400 Subject: [PATCH 39/61] tests: use assert_output_not_exists instead of asserting output is empty string --- tests/unit/apps_1.bats | 4 ++-- tests/unit/apps_2.bats | 4 ++-- tests/unit/checks.bats | 16 ++++++++-------- tests/unit/config.bats | 8 ++++---- tests/unit/git_1.bats | 2 +- tests/unit/logs.bats | 6 +++--- tests/unit/ports.bats | 2 +- tests/unit/storage.bats | 4 ++-- tests/unit/traefik.bats | 2 +- 9 files changed, 24 insertions(+), 24 deletions(-) diff --git a/tests/unit/apps_1.bats b/tests/unit/apps_1.bats index c5086bcd4..ab8b5cbae 100644 --- a/tests/unit/apps_1.bats +++ b/tests/unit/apps_1.bats @@ -139,11 +139,11 @@ teardown() { run /bin/bash -c "dokku apps:list | grep $TEST_APP" echo "output: $output" echo "status: $status" - assert_output "" + assert_output_not_exists run /bin/bash -c "curl --silent --write-out '%{http_code}\n' $(dokku url great-test-name) | grep 404" echo "output: $output" echo "status: $status" - assert_output "" + assert_output_not_exists run /bin/bash -c "dokku --force apps:destroy great-test-name" echo "output: $output" echo "status: $status" diff --git a/tests/unit/apps_2.bats b/tests/unit/apps_2.bats index d7a2a2cec..7580f874e 100644 --- a/tests/unit/apps_2.bats +++ b/tests/unit/apps_2.bats @@ -23,7 +23,7 @@ teardown() { run /bin/bash -c "curl --silent --write-out '%{http_code}\n' $(dokku url great-test-name) | grep 404" echo "output: $output" echo "status: $status" - assert_output "" + assert_output_not_exists run /bin/bash -c "dokku --force apps:destroy great-test-name" echo "output: $output" echo "status: $status" @@ -136,7 +136,7 @@ teardown() { run /bin/bash -c "dokku config:get app-without-ssl DOKKU_PROXY_SSL_PORT" echo "output: $output" echo "status: $status" - assert_output "" + assert_output_not_exists run /bin/bash -c "dokku --force apps:destroy app-without-ssl" echo "output: $output" echo "status: $status" diff --git a/tests/unit/checks.bats b/tests/unit/checks.bats index e337dad56..0c072fa75 100644 --- a/tests/unit/checks.bats +++ b/tests/unit/checks.bats @@ -56,7 +56,7 @@ teardown() { run /bin/bash -c "dokku config:get $TEST_APP DOKKU_CHECKS_SKIPPED" echo "output: $output" echo "status: $status" - assert_output "" + assert_output_not_exists run /bin/bash -c "dokku checks:enable $TEST_APP" echo "output: $output" @@ -66,12 +66,12 @@ teardown() { run /bin/bash -c "dokku config:get $TEST_APP DOKKU_CHECKS_DISABLED" echo "output: $output" echo "status: $status" - assert_output "" + assert_output_not_exists run /bin/bash -c "dokku config:get $TEST_APP DOKKU_CHECKS_SKIPPED" echo "output: $output" echo "status: $status" - assert_output "" + assert_output_not_exists } @test "(checks) checks:disable -> checks:skip" { @@ -88,7 +88,7 @@ teardown() { run /bin/bash -c "dokku config:get $TEST_APP DOKKU_CHECKS_SKIPPED" echo "output: $output" echo "status: $status" - assert_output "" + assert_output_not_exists run /bin/bash -c "dokku checks:skip $TEST_APP urgentworker,worker" echo "output: $output" @@ -132,7 +132,7 @@ teardown() { run /bin/bash -c "dokku config:get $TEST_APP DOKKU_CHECKS_DISABLED" echo "output: $output" echo "status: $status" - assert_output "" + assert_output_not_exists run /bin/bash -c "dokku checks:enable $TEST_APP" echo "output: $output" @@ -142,12 +142,12 @@ teardown() { run /bin/bash -c "dokku config:get $TEST_APP DOKKU_CHECKS_SKIPPED" echo "output: $output" echo "status: $status" - assert_output "" + assert_output_not_exists run /bin/bash -c "dokku config:get $TEST_APP DOKKU_CHECKS_DISABLED" echo "output: $output" echo "status: $status" - assert_output "" + assert_output_not_exists } @test "(checks) checks:skip -> checks:disable" { @@ -164,7 +164,7 @@ teardown() { run /bin/bash -c "dokku config:get $TEST_APP DOKKU_CHECKS_DISABLED" echo "output: $output" echo "status: $status" - assert_output "" + assert_output_not_exists run /bin/bash -c "dokku checks:disable $TEST_APP urgentworker,worker" echo "output: $output" diff --git a/tests/unit/config.bats b/tests/unit/config.bats index 6216fe441..58425609b 100644 --- a/tests/unit/config.bats +++ b/tests/unit/config.bats @@ -74,7 +74,7 @@ teardown() { run /bin/bash -c "dokku config:get --global test_var" echo "output: $output" echo "status: $status" - assert_output "" + assert_output_not_exists } @test "(config) config:set/get" { @@ -136,7 +136,7 @@ teardown() { run /bin/bash -c "dokku config:get $TEST_APP test_var" echo "output: $output" echo "status: $status" - assert_output "" + assert_output_not_exists } @test "(config) config:unset" { @@ -155,7 +155,7 @@ teardown() { run /bin/bash -c "dokku config:get $TEST_APP test_var" echo "output: $output" echo "status: $status" - assert_output "" + assert_output_not_exists run /bin/bash -c "dokku config:get $TEST_APP test_var3" echo "output: $output" echo "status: $status" @@ -167,7 +167,7 @@ teardown() { run /bin/bash -c "dokku config:get $TEST_APP test_var3" echo "output: $output" echo "status: $status" - assert_output "" + assert_output_not_exists run /bin/bash -c "dokku config:unset $TEST_APP" echo "output: $output" echo "status: $status" diff --git a/tests/unit/git_1.bats b/tests/unit/git_1.bats index 760b90f6f..2c24971d7 100644 --- a/tests/unit/git_1.bats +++ b/tests/unit/git_1.bats @@ -52,7 +52,7 @@ teardown() { run /bin/bash -c "dokku config:get $TEST_APP GIT_REV" echo "output: $output" echo "status: $status" - assert_output "" + assert_output_not_exists } @test "(git) customize the GIT_REV environment variable" { diff --git a/tests/unit/logs.bats b/tests/unit/logs.bats index 7d5f5e950..b8048b7ba 100644 --- a/tests/unit/logs.bats +++ b/tests/unit/logs.bats @@ -181,7 +181,7 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - assert_output "" + assert_output_not_exists run /bin/bash -c "dokku logs:report $TEST_APP --logs-max-size 2>&1" echo "output: $output" @@ -235,7 +235,7 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - assert_output "" + assert_output_not_exists } @test "(logs) logs:set escaped uri" { @@ -532,7 +532,7 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - assert_output "" + assert_output_not_exists } @test "(logs) logs:vector" { diff --git a/tests/unit/ports.bats b/tests/unit/ports.bats index 772deca21..46881c278 100644 --- a/tests/unit/ports.bats +++ b/tests/unit/ports.bats @@ -114,7 +114,7 @@ teardown() { run /bin/bash -c "dokku ports:report $TEST_APP --ports-map" echo "output: $output" echo "status: $status" - assert_output "" + assert_output_not_exists run /bin/bash -c "dokku ports:report $TEST_APP --ports-map-detected" echo "output: $output" diff --git a/tests/unit/storage.bats b/tests/unit/storage.bats index 13bd0c155..37205ecf4 100644 --- a/tests/unit/storage.bats +++ b/tests/unit/storage.bats @@ -141,7 +141,7 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - assert_output "" + assert_output_not_exists run /bin/bash -c "dokku storage:mount $TEST_APP /tmp/mount:/mount" echo "output: $output" @@ -158,7 +158,7 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - assert_output "" + assert_output_not_exists run /bin/bash -c "dokku storage:list $TEST_APP --format json | jq -r '. | length'" echo "output: $output" diff --git a/tests/unit/traefik.bats b/tests/unit/traefik.bats index 4ff1eca63..f4a326c45 100644 --- a/tests/unit/traefik.bats +++ b/tests/unit/traefik.bats @@ -194,7 +194,7 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - assert_output "" + assert_output_not_exists run /bin/bash -c "dokku traefik:set --global letsencrypt-email test@example.com" echo "output: $output" From c1279468d824500b2284dd2397731a2b9b31c9fd Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 31 Jul 2023 12:34:14 -0400 Subject: [PATCH 40/61] fix: use correct quoting in format template when fetching exposed image ports --- plugins/builder-dockerfile/internal-functions | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/builder-dockerfile/internal-functions b/plugins/builder-dockerfile/internal-functions index 02fe4dee1..9a24879ed 100755 --- a/plugins/builder-dockerfile/internal-functions +++ b/plugins/builder-dockerfile/internal-functions @@ -102,7 +102,8 @@ fn-builder-dockerfile-get-ports-from-image() { declare desc="return all exposed ports from passed image name" declare IMAGE="$1" - local ports="$("$DOCKER_BIN" image inspect --format "{{range $key, $value := .Config.ExposedPorts}}{{$key}} {{end}}" "$IMAGE" | xargs)" || true + # shellcheck disable=SC2016 + local ports="$("$DOCKER_BIN" image inspect --format '{{range $key, $value := .Config.ExposedPorts}}{{$key}} {{end}}' "$IMAGE" | xargs)" || true echo "$ports" } From 69e56fc976da7340060fca38e3d2d0a101745257 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 31 Jul 2023 12:41:45 -0400 Subject: [PATCH 41/61] fix: remove debugging code from builder-dockerfile plugin --- plugins/builder-dockerfile/builder-build | 3 --- 1 file changed, 3 deletions(-) diff --git a/plugins/builder-dockerfile/builder-build b/plugins/builder-dockerfile/builder-build index 0ff7d1f6b..42001d2eb 100755 --- a/plugins/builder-dockerfile/builder-build +++ b/plugins/builder-dockerfile/builder-build @@ -41,10 +41,7 @@ trigger-builder-dockerfile-builder-build() { eval "$(config_export app "$APP")" "$DOCKER_BIN" image build "${DOCKER_BUILD_LABEL_ARGS[@]}" $DOKKU_GLOBAL_BUILD_ARGS "${ARG_ARRAY[@]}" ${DOKKU_DOCKER_BUILD_OPTS} -t $IMAGE . - set -x - fn-builder-dockerfile-get-detect-port-map "$APP" "$IMAGE" "$SOURCECODE_WORK_DIR/Dockerfile" plugn trigger ports-set-detected "$APP" "$(fn-builder-dockerfile-get-detect-port-map "$APP" "$IMAGE" "$SOURCECODE_WORK_DIR/Dockerfile")" - set +x plugn trigger post-build-dockerfile "$APP" } From 622548057cb8d1381c66047f9f3d29f2ec4db5e4 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 31 Jul 2023 13:08:16 -0400 Subject: [PATCH 42/61] tests: add assertions to various bats tests Missing these assertions didn't necessarily break tests but they are more correct and having them should help suss out bugs. --- tests/unit/apps_1.bats | 41 +++++++++++--- tests/unit/apps_2.bats | 15 ++++-- tests/unit/build-env.bats | 15 ++++-- tests/unit/checks.bats | 10 +++- tests/unit/client.bats | 20 +++++-- tests/unit/core_1.bats | 11 +++- tests/unit/core_2.bats | 7 ++- tests/unit/domains.bats | 16 ++++-- tests/unit/events.bats | 17 +++++- tests/unit/git_2.bats | 5 +- tests/unit/init.bats | 18 +++++-- tests/unit/network.bats | 7 ++- tests/unit/nginx-vhosts_1.bats | 32 ++++++++--- tests/unit/nginx-vhosts_10.bats | 2 +- tests/unit/nginx-vhosts_11.bats | 22 ++++++-- tests/unit/nginx-vhosts_12.bats | 2 +- tests/unit/nginx-vhosts_2.bats | 95 +++++++++++++++++++++++++++------ tests/unit/nginx-vhosts_3.bats | 2 +- tests/unit/nginx-vhosts_4.bats | 44 ++++++++++++--- tests/unit/nginx-vhosts_5.bats | 26 +++++++-- tests/unit/nginx-vhosts_6.bats | 12 +++-- tests/unit/nginx-vhosts_7.bats | 31 ++++++++--- tests/unit/nginx-vhosts_8.bats | 17 ++++-- tests/unit/nginx-vhosts_9.bats | 65 +++++++++++++++++----- tests/unit/ports.bats | 8 ++- tests/unit/proxy.bats | 8 ++- tests/unit/ps-dockerfile-1.bats | 31 +++++++++-- tests/unit/ps-dockerfile-2.bats | 18 +++++-- tests/unit/ps-general-1.bats | 16 ++++-- tests/unit/ps-herokuish-1.bats | 6 ++- tests/unit/ps-herokuish-2.bats | 16 ++++-- tests/unit/report.bats | 11 +++- tests/unit/resource_1.bats | 42 ++++++++++++--- tests/unit/resource_2.bats | 36 ++++++++++--- tests/unit/run_1.bats | 6 ++- tests/unit/run_2.bats | 33 +++++++++--- tests/unit/test_helper.bash | 4 +- 37 files changed, 623 insertions(+), 144 deletions(-) diff --git a/tests/unit/apps_1.bats b/tests/unit/apps_1.bats index ab8b5cbae..7b415606f 100644 --- a/tests/unit/apps_1.bats +++ b/tests/unit/apps_1.bats @@ -55,11 +55,16 @@ teardown() { echo "output: $output" echo "status: $status" assert_success + run /bin/bash -c "dokku apps:list | grep $TEST_APP" echo "output: $output" echo "status: $status" assert_output $TEST_APP - destroy_app + + run destroy_app + echo "output: $output" + echo "status: $status" + assert_success run /bin/bash -c "dokku apps:create 1994testapp" echo "output: $output" @@ -95,12 +100,16 @@ teardown() { echo "output: $output" echo "status: $status" assert_success + run /bin/bash -c "dokku apps:list | grep $TEST_APP" echo "output: $output" echo "status: $status" assert_output $TEST_APP - destroy_app + run destroy_app + echo "output: $output" + echo "status: $status" + assert_success } @test "(apps) app autocreate disabled" { @@ -117,13 +126,21 @@ teardown() { } @test "(apps) apps:destroy" { - create_app + run create_app + echo "output: $output" + echo "status: $status" + assert_success + run /bin/bash -c "dokku --force apps:destroy $TEST_APP" echo "output: $output" echo "status: $status" assert_success - create_app + run create_app + echo "output: $output" + echo "status: $status" + assert_success + run /bin/bash -c "dokku --force --app $TEST_APP apps:destroy" echo "output: $output" echo "status: $status" @@ -131,7 +148,10 @@ teardown() { } @test "(apps) apps:rename" { - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success run /bin/bash -c "dokku apps:rename $TEST_APP great-test-name" echo "output: $output" echo "status: $status" @@ -248,7 +268,11 @@ teardown() { echo "status: $status" assert_failure - create_app + run create_app + echo "output: $output" + echo "status: $status" + assert_success + run /bin/bash -c "dokku apps:exists $TEST_APP" echo "output: $output" echo "status: $status" @@ -262,7 +286,10 @@ teardown() { } @test "(apps) apps:lock/locked/unlock" { - create_app + run create_app + echo "output: $output" + echo "status: $status" + assert_success run /bin/bash -c "dokku apps:report $TEST_APP --app-locked" echo "output: $output" diff --git a/tests/unit/apps_2.bats b/tests/unit/apps_2.bats index 7580f874e..6b62abdb8 100644 --- a/tests/unit/apps_2.bats +++ b/tests/unit/apps_2.bats @@ -11,7 +11,10 @@ teardown() { } @test "(apps) apps:clone" { - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success run /bin/bash -c "dokku apps:clone $TEST_APP great-test-name" echo "output: $output" echo "status: $status" @@ -144,7 +147,10 @@ teardown() { } @test "(apps) apps:clone --skip-deploy" { - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success run /bin/bash -c "dokku apps:clone --skip-deploy $TEST_APP great-test-name" echo "output: $output" echo "status: $status" @@ -172,7 +178,10 @@ teardown() { } @test "(apps) apps:clone --ignore-existing" { - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success run /bin/bash -c "dokku apps:create great-test-name" echo "output: $output" echo "status: $status" diff --git a/tests/unit/build-env.bats b/tests/unit/build-env.bats index f53fe5372..a3ebf54d1 100644 --- a/tests/unit/build-env.bats +++ b/tests/unit/build-env.bats @@ -18,7 +18,10 @@ teardown() { echo "status: $status" assert_success - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success run /bin/bash -c "dokku config $TEST_APP" assert_success } @@ -34,7 +37,10 @@ teardown() { echo "status: $status" assert_success - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success run /bin/bash -c "dokku config:get --global CURL_CONNECT_TIMEOUT | grep 90" echo "output: $output" echo "status: $status" @@ -76,7 +82,10 @@ teardown() { } @test "(build-env) DOKKU_ROOT cache bind is used by default" { - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success BUILD_CID=$(docker ps -a | grep $TEST_APP | grep /bin/bash | awk '{print $1}' | head -n1) run /bin/bash -c "docker inspect --format '{{ .HostConfig.Binds }}' $BUILD_CID | tr -d '[]' | cut -f1 -d:" diff --git a/tests/unit/checks.bats b/tests/unit/checks.bats index 0c072fa75..826e0cebf 100644 --- a/tests/unit/checks.bats +++ b/tests/unit/checks.bats @@ -188,7 +188,10 @@ teardown() { echo "status: $status" assert_success - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success run /bin/bash -c "dokku checks:run $TEST_APP" echo "output: $output" @@ -227,7 +230,10 @@ teardown() { echo "status: $status" assert_success - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success run /bin/bash -c "dokku checks:disable $TEST_APP worker" echo "output: $output" diff --git a/tests/unit/client.bats b/tests/unit/client.bats index a31b2d77b..9b2c3b3ef 100644 --- a/tests/unit/client.bats +++ b/tests/unit/client.bats @@ -171,7 +171,10 @@ teardown() { # # looks like docker exec is built to work with docker-under-libcontainer, # # but we're using docker-under-lxc. I don't have an estimated time for the fix, sorry # skip "circleci does not support docker exec at the moment." -# deploy_app +# run deploy_app +# echo "output: $output" +# echo "status: $status" +# assert_success # run /bin/bash -c "${BATS_TEST_DIRNAME}/../../contrib/dokku_client.sh ps $TEST_APP | grep -q 'node web.js'" # echo "output: $output" # echo "status: $status" @@ -179,7 +182,10 @@ teardown() { # } @test "(client) ps:start" { - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success run /bin/bash -c "${BATS_TEST_DIRNAME}/../../contrib/dokku_client.sh ps:stop $TEST_APP" echo "output: $output" echo "status: $status" @@ -197,7 +203,10 @@ teardown() { } @test "(client) ps:stop" { - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success run /bin/bash -c "${BATS_TEST_DIRNAME}/../../contrib/dokku_client.sh ps:stop $TEST_APP" echo "output: $output" echo "status: $status" @@ -211,7 +220,10 @@ teardown() { } @test "(client) ps:restart" { - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success run /bin/bash -c "${BATS_TEST_DIRNAME}/../../contrib/dokku_client.sh ps:restart $TEST_APP" echo "output: $output" echo "status: $status" diff --git a/tests/unit/core_1.bats b/tests/unit/core_1.bats index 9d2413484..23a5a47f6 100644 --- a/tests/unit/core_1.bats +++ b/tests/unit/core_1.bats @@ -16,7 +16,10 @@ teardown() { } @test "(core) remove exited containers" { - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success # make sure we have many exited containers of the same 'type' run /bin/bash -c "for cnt in 1 2 3; do dokku run $TEST_APP echo $TEST_APP; done" @@ -89,7 +92,11 @@ EOF CID=$(<"$DOKKU_ROOT/$TEST_APP/CONTAINER.web.1") docker commit --change "ENV USER postgres" "$CID" "dokku/${TEST_APP}:latest" - dokku config:set --no-restart "$TEST_APP" DOKKU_APP_USER=postgres + run /bin/bash -c "dokku config:set --no-restart $TEST_APP DOKKU_APP_USER=postgres" + echo "output: $output" + echo "status: $status" + assert_success + source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" source "$PLUGIN_CORE_AVAILABLE_PATH/config/functions" diff --git a/tests/unit/core_2.bats b/tests/unit/core_2.bats index 43c902616..bc1d1ab34 100644 --- a/tests/unit/core_2.bats +++ b/tests/unit/core_2.bats @@ -39,7 +39,12 @@ teardown() { @test "(core) urls (non-ssl)" { assert_urls "http://${TEST_APP}.dokku.me" - dokku domains:add $TEST_APP "test.dokku.me" + + run /bin/bash -c "dokku domains:add $TEST_APP test.dokku.me" + echo "output: $output" + echo "status: $status" + assert_success + assert_urls "http://${TEST_APP}.dokku.me" "http://test.dokku.me" } diff --git a/tests/unit/domains.bats b/tests/unit/domains.bats index 377ae71fe..6a56164ec 100644 --- a/tests/unit/domains.bats +++ b/tests/unit/domains.bats @@ -342,9 +342,16 @@ teardown() { echo "status: $status" assert_success + run /bin/bash -c "dokku --quiet apps:create test.dokku.test" + echo "output: $output" + echo "status: $status" + assert_success + # run domains:clear in order to invoke default vhost creation - dokku --quiet apps:create test.dokku.test - dokku --quiet domains:clear test.dokku.test + run /bin/bash -c "dokku --quiet domains:clear test.dokku.test" + echo "output: $output" + echo "status: $status" + assert_success run /bin/bash -c "dokku domains:report test.dokku.test --domains-app-vhosts" echo "output: $output" @@ -352,7 +359,10 @@ teardown() { assert_success assert_output "test.dokku.test" - dokku --force apps:destroy test.dokku.test + run /bin/bash -c "dokku --force apps:destroy test.dokku.test" + echo "output: $output" + echo "status: $status" + assert_success } @test "(domains) app rename only renames domains associated with global domains" { diff --git a/tests/unit/events.bats b/tests/unit/events.bats index 65a3cc7e6..ab80d2e94 100644 --- a/tests/unit/events.bats +++ b/tests/unit/events.bats @@ -24,14 +24,17 @@ teardown() { echo "output: $output" echo "status: $status" assert_success + run /bin/bash -c "test -f /etc/rsyslog.d/99-dokku.conf" echo "output: $output" echo "status: $status" assert_success + run /bin/bash -c "stat -c '%U:%G:%a' /var/log/dokku/" echo "output: $output" echo "status: $status" assert_output "syslog:dokku:775" + run /bin/bash -c "stat -c '%U:%G:%a' /var/log/dokku/events.log" echo "output: $output" echo "status: $status" @@ -40,10 +43,22 @@ teardown() { @test "(events) log commands" { run /bin/bash -c "dokku events:on" - deploy_app + echo "output: $output" + echo "status: $status" + assert_success + + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success + run /bin/bash -c "dokku events" echo "output: $output" echo "status: $status" assert_success + run /bin/bash -c "dokku events:off" + echo "output: $output" + echo "status: $status" + assert_success } diff --git a/tests/unit/git_2.bats b/tests/unit/git_2.bats index 9b13c1632..4e0d1ef2c 100644 --- a/tests/unit/git_2.bats +++ b/tests/unit/git_2.bats @@ -94,7 +94,10 @@ teardown() { echo "status: $status" assert_success - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success run /bin/bash -c "test -d $DOKKU_ROOT/$TEST_APP/refs" echo "output: $output" diff --git a/tests/unit/init.bats b/tests/unit/init.bats index 5455e0626..1cd143a32 100644 --- a/tests/unit/init.bats +++ b/tests/unit/init.bats @@ -15,7 +15,11 @@ teardown() { @test "(init) buildpack" { source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" local APP="zombies-buildpack" - deploy_app "$APP" + run deploy_app "$APP" + echo "output: $output" + echo "status: $status" + assert_success + local CIDS=$(get_app_container_ids "$APP") run "$DOCKER_BIN" container top "$CIDS" @@ -26,7 +30,11 @@ teardown() { @test "(init) dockerfile no tini" { source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" local APP="zombies-dockerfile-no-tini" - deploy_app "$APP" + run deploy_app "$APP" + echo "output: $output" + echo "status: $status" + assert_success + local CIDS=$(get_app_container_ids "$APP") run "$DOCKER_BIN" container top "$CIDS" @@ -37,7 +45,11 @@ teardown() { @test "(init) dockerfile with tini" { source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" local APP="zombies-dockerfile-tini" - deploy_app "$APP" + run deploy_app "$APP" + echo "output: $output" + echo "status: $status" + assert_success + local CIDS=$(get_app_container_ids "$APP") run "$DOCKER_BIN" container top "$CIDS" diff --git a/tests/unit/network.bats b/tests/unit/network.bats index 92599b517..c1ed797f6 100644 --- a/tests/unit/network.bats +++ b/tests/unit/network.bats @@ -9,7 +9,7 @@ setup() { } teardown() { - destroy_app 0 $TEST_APP + destroy_app [[ -f "$DOKKU_ROOT/VHOST.bak" ]] && mv "$DOKKU_ROOT/VHOST.bak" "$DOKKU_ROOT/VHOST" && chown dokku:dokku "$DOKKU_ROOT/VHOST" docker network rm create-network || true docker network rm deploy-network || true @@ -32,7 +32,10 @@ teardown() { } @test "(network) network:set bind-all-interfaces" { - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success assert_nonssl_domain "${TEST_APP}.dokku.me" run /bin/bash -c "dokku network:set $TEST_APP bind-all-interfaces true" diff --git a/tests/unit/nginx-vhosts_1.bats b/tests/unit/nginx-vhosts_1.bats index 56c21ab54..3862ea237 100644 --- a/tests/unit/nginx-vhosts_1.bats +++ b/tests/unit/nginx-vhosts_1.bats @@ -29,8 +29,15 @@ teardown() { } @test "(nginx-vhosts) proxy:build-config (domains:disable/enable)" { - deploy_app - dokku domains:disable $TEST_APP + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku domains:disable $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success HOSTNAME=$(<"$DOKKU_ROOT/VHOST") check_urls http://${HOSTNAME}:[0-9]+ @@ -40,19 +47,29 @@ teardown() { assert_http_success $URL done - dokku domains:enable $TEST_APP + run /bin/bash -c "dokku domains:enable $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success check_urls http://${TEST_APP}.dokku.me assert_http_success http://${TEST_APP}.dokku.me } @test "(nginx-vhosts) proxy:build-config (domains:add pre deploy)" { - create_app + run create_app + echo "output: $output" + echo "status: $status" + assert_success + run /bin/bash -c "dokku domains:add $TEST_APP www.test.app.dokku.me" echo "output: $output" echo "status: $status" assert_success - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success sleep 5 # wait for nginx to reload check_urls http://www.test.app.dokku.me @@ -61,7 +78,10 @@ teardown() { @test "(nginx-vhosts) proxy:build-config (with global VHOST)" { echo "dokku.me" >"$DOKKU_ROOT/VHOST" - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success check_urls http://${TEST_APP}.dokku.me assert_http_success http://${TEST_APP}.dokku.me diff --git a/tests/unit/nginx-vhosts_10.bats b/tests/unit/nginx-vhosts_10.bats index b18f07311..75878a3bc 100644 --- a/tests/unit/nginx-vhosts_10.bats +++ b/tests/unit/nginx-vhosts_10.bats @@ -9,7 +9,7 @@ setup() { } teardown() { - destroy_app 0 $TEST_APP + destroy_app [[ -f "$DOKKU_ROOT/VHOST.bak" ]] && mv "$DOKKU_ROOT/VHOST.bak" "$DOKKU_ROOT/VHOST" && chown dokku:dokku "$DOKKU_ROOT/VHOST" global_teardown } diff --git a/tests/unit/nginx-vhosts_11.bats b/tests/unit/nginx-vhosts_11.bats index cc0db0184..82b797f41 100644 --- a/tests/unit/nginx-vhosts_11.bats +++ b/tests/unit/nginx-vhosts_11.bats @@ -9,13 +9,16 @@ setup() { } teardown() { - destroy_app 0 $TEST_APP + destroy_app [[ -f "$DOKKU_ROOT/VHOST.bak" ]] && mv "$DOKKU_ROOT/VHOST.bak" "$DOKKU_ROOT/VHOST" && chown dokku:dokku "$DOKKU_ROOT/VHOST" global_teardown } @test "(nginx-vhosts) nginx:set proxy-buffer-size" { - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success run /bin/bash -c "dokku nginx:set $TEST_APP proxy-buffer-size 2k" echo "output: $output" @@ -49,7 +52,10 @@ teardown() { } @test "(nginx-vhosts) nginx:set proxy-buffering" { - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success run /bin/bash -c "dokku nginx:set $TEST_APP proxy-buffering off" echo "output: $output" @@ -83,7 +89,10 @@ teardown() { } @test "(nginx-vhosts) nginx:set proxy-buffers" { - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success run /bin/bash -c "dokku nginx:set $TEST_APP proxy-buffers \"64 4k\"" echo "output: $output" @@ -117,7 +126,10 @@ teardown() { } @test "(nginx-vhosts) nginx:set proxy-busy-buffers-size" { - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success run /bin/bash -c "dokku nginx:set $TEST_APP proxy-busy-buffers-size 10k" echo "output: $output" diff --git a/tests/unit/nginx-vhosts_12.bats b/tests/unit/nginx-vhosts_12.bats index d6070b5fd..81949d1f1 100644 --- a/tests/unit/nginx-vhosts_12.bats +++ b/tests/unit/nginx-vhosts_12.bats @@ -9,7 +9,7 @@ setup() { } teardown() { - destroy_app 0 $TEST_APP + destroy_app [[ -f "$DOKKU_ROOT/VHOST.bak" ]] && mv "$DOKKU_ROOT/VHOST.bak" "$DOKKU_ROOT/VHOST" && chown dokku:dokku "$DOKKU_ROOT/VHOST" global_teardown } diff --git a/tests/unit/nginx-vhosts_2.bats b/tests/unit/nginx-vhosts_2.bats index 178756522..f16c992bc 100644 --- a/tests/unit/nginx-vhosts_2.bats +++ b/tests/unit/nginx-vhosts_2.bats @@ -9,33 +9,69 @@ setup() { } teardown() { - destroy_app 0 $TEST_APP + destroy_app [[ -f "$DOKKU_ROOT/VHOST.bak" ]] && mv "$DOKKU_ROOT/VHOST.bak" "$DOKKU_ROOT/VHOST" && chown dokku:dokku "$DOKKU_ROOT/VHOST" global_teardown } @test "(nginx-vhosts) proxy:build-config (with SSL and unrelated domain)" { setup_test_tls - dokku domains:add $TEST_APP "node-js-app.dokku.me" - dokku domains:add $TEST_APP "test.dokku.me" - deploy_app - dokku nginx:show-config $TEST_APP + run /bin/bash -c "dokku domains:add $TEST_APP node-js-app.dokku.me" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku domains:add $TEST_APP test.dokku.me" + echo "output: $output" + echo "status: $status" + assert_success + + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku nginx:show-config $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + assert_ssl_domain "node-js-app.dokku.me" assert_http_redirect "http://test.dokku.me" "https://test.dokku.me:443/" } @test "(nginx-vhosts) proxy:build-config (wildcard SSL)" { setup_test_tls wildcard - dokku domains:add $TEST_APP "wildcard1.dokku.me" - dokku domains:add $TEST_APP "wildcard2.dokku.me" - deploy_app - dokku nginx:show-config $TEST_APP + run /bin/bash -c "dokku domains:add $TEST_APP wildcard1.dokku.me" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku domains:add $TEST_APP wildcard2.dokku.me" + echo "output: $output" + echo "status: $status" + assert_success + + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku nginx:show-config $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + assert_ssl_domain "wildcard1.dokku.me" assert_ssl_domain "wildcard2.dokku.me" } @test "(nginx-vhosts) proxy:build-config (wildcard SSL and unrelated domain) 1" { - destroy_app + run destroy_app + echo "output: $output" + echo "status: $status" + assert_success + TEST_APP="${TEST_APP}.example.com" run /bin/bash -c "dokku apps:create $TEST_APP" echo "output: $output" @@ -43,18 +79,45 @@ teardown() { assert_success setup_test_tls wildcard - deploy_app nodejs-express dokku@dokku.me:$TEST_APP + + run deploy_app nodejs-express dokku@dokku.me:$TEST_APP + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku nginx:show-config $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + run /bin/bash -c "dokku nginx:show-config $TEST_APP | grep -e '*.dokku.me' | wc -l" - dokku nginx:show-config $TEST_APP + echo "output: $output" + echo "status: $status" + assert_success assert_output "0" } @test "(nginx-vhosts) proxy:build-config (with SSL and Multiple SANs)" { setup_test_tls sans - dokku domains:add $TEST_APP "test.dokku.me" - dokku domains:add $TEST_APP "www.test.dokku.me" - dokku domains:add $TEST_APP "www.test.app.dokku.me" - deploy_app + run /bin/bash -c "dokku domains:add $TEST_APP test.dokku.me" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku domains:add $TEST_APP www.test.dokku.me" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku domains:add $TEST_APP www.test.app.dokku.me" + echo "output: $output" + echo "status: $status" + assert_success + + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success assert_ssl_domain "test.dokku.me" assert_ssl_domain "www.test.dokku.me" assert_ssl_domain "www.test.app.dokku.me" diff --git a/tests/unit/nginx-vhosts_3.bats b/tests/unit/nginx-vhosts_3.bats index 6f7a5fbb9..71ade8df4 100644 --- a/tests/unit/nginx-vhosts_3.bats +++ b/tests/unit/nginx-vhosts_3.bats @@ -11,7 +11,7 @@ setup() { } teardown() { - destroy_app 0 $TEST_APP + destroy_app [[ -f "$DOKKU_ROOT/VHOST.bak" ]] && mv "$DOKKU_ROOT/VHOST.bak" "$DOKKU_ROOT/VHOST" && chown dokku:dokku "$DOKKU_ROOT/VHOST" global_teardown } diff --git a/tests/unit/nginx-vhosts_4.bats b/tests/unit/nginx-vhosts_4.bats index 9cfd30a56..e1d0d9eb3 100644 --- a/tests/unit/nginx-vhosts_4.bats +++ b/tests/unit/nginx-vhosts_4.bats @@ -16,7 +16,10 @@ teardown() { @test "(nginx-vhosts) proxy:build-config (without global VHOST)" { rm "$DOKKU_ROOT/VHOST" - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success run /bin/bash -c "dokku --quiet urls $TEST_APP" echo "output: $output" @@ -35,7 +38,10 @@ teardown() { @test "(nginx-vhosts) proxy:build-config (without global VHOST and IPv4 address set as HOSTNAME)" { rm "$DOKKU_ROOT/VHOST" echo "127.0.0.1" >"$DOKKU_ROOT/VHOST" - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success HOSTNAME=$(<"$DOKKU_ROOT/VHOST") check_urls http://${HOSTNAME}:[0-9]+ @@ -49,7 +55,10 @@ teardown() { @test "(nginx-vhosts) proxy:build-config (without global VHOST and IPv6 address set as HOSTNAME)" { rm "$DOKKU_ROOT/VHOST" echo "fda5:c7db:a520:bb6d::aabb:ccdd:eeff" >"$DOKKU_ROOT/VHOST" - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success HOSTNAME=$(<"$DOKKU_ROOT/VHOST") check_urls http://${HOSTNAME}:[0-9]+ @@ -57,16 +66,35 @@ teardown() { @test "(nginx-vhosts) proxy:build-config (without global VHOST and domains:add pre deploy)" { rm "$DOKKU_ROOT/VHOST" - create_app - dokku domains:add $TEST_APP "www.test.app.dokku.me" - deploy_app + run create_app + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku domains:add $TEST_APP www.test.app.dokku.me" + echo "output: $output" + echo "status: $status" + assert_success + + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success assert_nonssl_domain "www.test.app.dokku.me" } @test "(nginx-vhosts) proxy:build-config (without global VHOST and domains:add post deploy)" { rm "$DOKKU_ROOT/VHOST" - deploy_app - dokku domains:add $TEST_APP "www.test.app.dokku.me" + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku domains:add $TEST_APP www.test.app.dokku.me" + echo "output: $output" + echo "status: $status" + assert_success + check_urls http://www.test.app.dokku.me assert_http_success http://www.test.app.dokku.me } diff --git a/tests/unit/nginx-vhosts_5.bats b/tests/unit/nginx-vhosts_5.bats index 76f967b7c..663e20d5e 100644 --- a/tests/unit/nginx-vhosts_5.bats +++ b/tests/unit/nginx-vhosts_5.bats @@ -17,16 +17,26 @@ teardown() { @test "(nginx-vhosts) proxy:build-config (sslip.io style hostnames)" { echo "127.0.0.1.sslip.io.dokku.me" >"$DOKKU_ROOT/VHOST" - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success check_urls http://${TEST_APP}.127.0.0.1.sslip.io.dokku.me assert_http_success http://${TEST_APP}.127.0.0.1.sslip.io.dokku.me } @test "(nginx-vhosts) proxy:build-config (dockerfile expose)" { - deploy_app dockerfile + run deploy_app dockerfile + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku domains:add $TEST_APP www.test.app.dokku.me" + echo "output: $output" + echo "status: $status" + assert_success - dokku domains:add $TEST_APP "www.test.app.dokku.me" check_urls http://${TEST_APP}.dokku.me:3000 check_urls http://${TEST_APP}.dokku.me:3003 check_urls http://www.test.app.dokku.me:3000 @@ -38,7 +48,10 @@ teardown() { } @test "(nginx-vhosts) proxy:build-config (multiple networks)" { - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success create_attach_network run /bin/bash -c "dokku proxy:build-config $TEST_APP" @@ -51,7 +64,10 @@ teardown() { local GLOBAL_PORT=30999 run /bin/bash -c "dokku config:set --global DOKKU_PROXY_PORT=${GLOBAL_PORT}" - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success check_urls http://${TEST_APP}.dokku.me:${GLOBAL_PORT} assert_http_success http://${TEST_APP}.dokku.me:${GLOBAL_PORT} diff --git a/tests/unit/nginx-vhosts_6.bats b/tests/unit/nginx-vhosts_6.bats index 707231d59..15ec92725 100644 --- a/tests/unit/nginx-vhosts_6.bats +++ b/tests/unit/nginx-vhosts_6.bats @@ -9,13 +9,16 @@ setup() { } teardown() { - destroy_app 0 $TEST_APP + destroy_app [[ -f "$DOKKU_ROOT/VHOST.bak" ]] && mv "$DOKKU_ROOT/VHOST.bak" "$DOKKU_ROOT/VHOST" && chown dokku:dokku "$DOKKU_ROOT/VHOST" global_teardown } @test "(nginx-vhosts) nginx (no server tokens)" { - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success run /bin/bash -c "curl -s -D - $(dokku url $TEST_APP) -o /dev/null | grep -E '^Server' | grep -E '[0-9]+'" echo "output: $output" echo "status: $status" @@ -205,7 +208,10 @@ teardown() { } @test "(nginx-vhosts) nginx:validate-config" { - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success run /bin/bash -c "dokku nginx:validate-config" echo "output: $output" echo "status: $status" diff --git a/tests/unit/nginx-vhosts_7.bats b/tests/unit/nginx-vhosts_7.bats index 94c20244b..32a4f543c 100644 --- a/tests/unit/nginx-vhosts_7.bats +++ b/tests/unit/nginx-vhosts_7.bats @@ -9,16 +9,27 @@ setup() { } teardown() { - destroy_app 0 $TEST_APP + destroy_app [[ -f "$DOKKU_ROOT/VHOST.bak" ]] && mv "$DOKKU_ROOT/VHOST.bak" "$DOKKU_ROOT/VHOST" && chown dokku:dokku "$DOKKU_ROOT/VHOST" global_teardown } @test "(nginx-vhosts) proxy:build-config (wildcard SSL and custom nginx template)" { setup_test_tls wildcard - dokku domains:add $TEST_APP "wildcard1.dokku.me" - dokku domains:add $TEST_APP "wildcard2.dokku.me" - deploy_app nodejs-express dokku@dokku.me:$TEST_APP custom_ssl_nginx_template + run /bin/bash -c "dokku domains:add $TEST_APP wildcard1.dokku.me" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku domains:add $TEST_APP wildcard2.dokku.me" + echo "output: $output" + echo "status: $status" + assert_success + + run deploy_app nodejs-express dokku@dokku.me:$TEST_APP custom_ssl_nginx_template + echo "output: $output" + echo "status: $status" + assert_success assert_ssl_domain "wildcard1.dokku.me" assert_ssl_domain "wildcard2.dokku.me" @@ -27,7 +38,11 @@ teardown() { } @test "(nginx-vhosts) proxy:build-config (custom nginx template - no ssl)" { - dokku domains:add $TEST_APP "www.test.app.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP www.test.app.dokku.me" + echo "output: $output" + echo "status: $status" + assert_success + run deploy_app nodejs-express dokku@dokku.me:$TEST_APP custom_nginx_template echo "output: $output" echo "status: $status" @@ -53,7 +68,11 @@ teardown() { echo "status: $status" assert_success - dokku domains:add $TEST_APP "www.test.app.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP www.test.app.dokku.me" + echo "output: $output" + echo "status: $status" + assert_success + run deploy_app nodejs-express dokku@dokku.me:$TEST_APP custom_nginx_template echo "output: $output" echo "status: $status" diff --git a/tests/unit/nginx-vhosts_8.bats b/tests/unit/nginx-vhosts_8.bats index d0b02ae4c..caf8885e4 100644 --- a/tests/unit/nginx-vhosts_8.bats +++ b/tests/unit/nginx-vhosts_8.bats @@ -9,13 +9,16 @@ setup() { } teardown() { - destroy_app 0 $TEST_APP + destroy_app [[ -f "$DOKKU_ROOT/VHOST.bak" ]] && mv "$DOKKU_ROOT/VHOST.bak" "$DOKKU_ROOT/VHOST" && chown dokku:dokku "$DOKKU_ROOT/VHOST" global_teardown } @test "(nginx-vhosts) nginx:set client-max-body-size" { - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success run /bin/bash -c "dokku nginx:set $TEST_APP client-max-body-size" echo "output: $output" @@ -49,7 +52,10 @@ teardown() { } @test "(nginx-vhosts) nginx:set proxy-read-timeout" { - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success run /bin/bash -c "dokku nginx:set $TEST_APP proxy-read-timeout 45s" echo "output: $output" @@ -84,7 +90,10 @@ teardown() { @test "(nginx-vhosts) nginx:set proxy-read-timeout (with SSL)" { setup_test_tls - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success run /bin/bash -c "dokku nginx:set $TEST_APP proxy-read-timeout 45s" echo "output: $output" diff --git a/tests/unit/nginx-vhosts_9.bats b/tests/unit/nginx-vhosts_9.bats index 702b961c9..44420ca47 100644 --- a/tests/unit/nginx-vhosts_9.bats +++ b/tests/unit/nginx-vhosts_9.bats @@ -9,13 +9,16 @@ setup() { } teardown() { - destroy_app 0 $TEST_APP + destroy_app [[ -f "$DOKKU_ROOT/VHOST.bak" ]] && mv "$DOKKU_ROOT/VHOST.bak" "$DOKKU_ROOT/VHOST" && chown dokku:dokku "$DOKKU_ROOT/VHOST" global_teardown } @test "(nginx-vhosts) logging" { - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success run [ -a "/var/log/nginx/$TEST_APP-access.log" ] echo "output: $output" @@ -39,7 +42,10 @@ teardown() { } @test "(nginx-vhosts) log-path" { - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success run /bin/bash -c "dokku nginx:set $TEST_APP access-log-path off" echo "output: $output" @@ -103,7 +109,10 @@ teardown() { } @test "(nginx-vhosts) access-log-format" { - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success run /bin/bash -c "dokku nginx:set $TEST_APP access-log-format combined" echo "output: $output" @@ -154,20 +163,52 @@ teardown() { @test "(nginx-vhosts) proxy:build-config (with SSL and unrelated domain)" { setup_test_tls - dokku domains:add $TEST_APP "node-js-app.dokku.me" - dokku domains:add $TEST_APP "test.dokku.me" - deploy_app - dokku nginx:show-config $TEST_APP + run /bin/bash -c "dokku domains:add $TEST_APP node-js-app.dokku.me" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku domains:add $TEST_APP test.dokku.me" + echo "output: $output" + echo "status: $status" + assert_success + + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku nginx:show-config $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + assert_ssl_domain "node-js-app.dokku.me" assert_http_redirect "http://test.dokku.me" "https://test.dokku.me:443/" } @test "(nginx-vhosts) proxy:build-config (wildcard SSL)" { setup_test_tls wildcard - dokku domains:add $TEST_APP "wildcard1.dokku.me" - dokku domains:add $TEST_APP "wildcard2.dokku.me" - deploy_app - dokku nginx:show-config $TEST_APP + run /bin/bash -c "dokku domains:add $TEST_APP wildcard1.dokku.me" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku domains:add $TEST_APP wildcard2.dokku.me" + echo "output: $output" + echo "status: $status" + assert_success + + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku nginx:show-config $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + assert_ssl_domain "wildcard1.dokku.me" assert_ssl_domain "wildcard2.dokku.me" } diff --git a/tests/unit/ports.bats b/tests/unit/ports.bats index 46881c278..82df14618 100644 --- a/tests/unit/ports.bats +++ b/tests/unit/ports.bats @@ -9,7 +9,7 @@ setup() { } teardown() { - destroy_app 0 $TEST_APP + destroy_app [[ -f "$DOKKU_ROOT/VHOST.bak" ]] && mv "$DOKKU_ROOT/VHOST.bak" "$DOKKU_ROOT/VHOST" && chown dokku:dokku "$DOKKU_ROOT/VHOST" global_teardown } @@ -91,7 +91,11 @@ teardown() { } @test "(ports:add) post-deploy add" { - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success + run /bin/bash -c "dokku ports:add $TEST_APP http:8080:5000 http:8081:5000" echo "output: $output" echo "status: $status" diff --git a/tests/unit/proxy.bats b/tests/unit/proxy.bats index 1de3c9db0..6cc149efd 100644 --- a/tests/unit/proxy.bats +++ b/tests/unit/proxy.bats @@ -9,7 +9,7 @@ setup() { } teardown() { - destroy_app 0 $TEST_APP + destroy_app [[ -f "$DOKKU_ROOT/VHOST.bak" ]] && mv "$DOKKU_ROOT/VHOST.bak" "$DOKKU_ROOT/VHOST" && chown dokku:dokku "$DOKKU_ROOT/VHOST" global_teardown } @@ -91,7 +91,11 @@ teardown() { } @test "(proxy) proxy:enable/disable" { - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success + assert_nonssl_domain "${TEST_APP}.dokku.me" run /bin/bash -c "dokku proxy:disable $TEST_APP" diff --git a/tests/unit/ps-dockerfile-1.bats b/tests/unit/ps-dockerfile-1.bats index f74aaf60f..a7bd6645d 100644 --- a/tests/unit/ps-dockerfile-1.bats +++ b/tests/unit/ps-dockerfile-1.bats @@ -13,11 +13,16 @@ teardown() { } @test "(ps) dockerfile" { - deploy_app dockerfile + run deploy_app dockerfile + echo "output: $output" + echo "status: $status" + assert_success + run /bin/bash -c "dokku ps:stop $TEST_APP" echo "output: $output" echo "status: $status" assert_success + for CID_FILE in $DOKKU_ROOT/$TEST_APP/CONTAINER.*; do run /bin/bash -c "docker ps -q --no-trunc | grep -q $(<$CID_FILE)" echo "output: $output" @@ -65,9 +70,21 @@ teardown() { echo "status: $status" assert_success - destroy_app - create_app - deploy_app dockerfile-procfile + run destroy_app + echo "output: $output" + echo "status: $status" + assert_success + + run create_app + echo "output: $output" + echo "status: $status" + assert_success + + run deploy_app dockerfile-procfile + echo "output: $output" + echo "status: $status" + assert_success + run /bin/bash -c "dokku ps:scale $TEST_APP non-existent=2" echo "output: $output" echo "status: $status" @@ -80,7 +97,11 @@ teardown() { echo "status: $status" assert_success - deploy_app dockerfile + run deploy_app dockerfile + echo "output: $output" + echo "status: $status" + assert_success + CIDS="" for CID_FILE in $DOKKU_ROOT/$TEST_APP/CONTAINER.web.*; do CIDS+=$(<$CID_FILE) diff --git a/tests/unit/ps-dockerfile-2.bats b/tests/unit/ps-dockerfile-2.bats index 3946efe06..dd4cf314d 100644 --- a/tests/unit/ps-dockerfile-2.bats +++ b/tests/unit/ps-dockerfile-2.bats @@ -18,7 +18,11 @@ teardown() { echo "status: $status" assert_success - deploy_app dockerfile-app-json-formations + run deploy_app dockerfile-app-json-formations + echo "output: $output" + echo "status: $status" + assert_success + CIDS="" for CID_FILE in $DOKKU_ROOT/$TEST_APP/CONTAINER.web.*; do CIDS+=$(<$CID_FILE) @@ -42,7 +46,11 @@ teardown() { } @test "(ps) dockerfile with procfile" { - deploy_app dockerfile-procfile + run deploy_app dockerfile-procfile + echo "output: $output" + echo "status: $status" + assert_success + run /bin/bash -c "dokku ps:stop $TEST_APP" echo "output: $output" echo "status: $status" @@ -106,7 +114,11 @@ teardown() { echo "status: $status" assert_success - deploy_app dockerfile-procfile + run deploy_app dockerfile-procfile + echo "output: $output" + echo "status: $status" + assert_success + for PROC_TYPE in web worker; do run /bin/bash -c "docker ps --format '{{.ID}} {{.Command}}' --no-trunc" echo "output: $output" diff --git a/tests/unit/ps-general-1.bats b/tests/unit/ps-general-1.bats index 4171f8fbb..9cab2caef 100644 --- a/tests/unit/ps-general-1.bats +++ b/tests/unit/ps-general-1.bats @@ -27,8 +27,15 @@ teardown() { } @test "(ps) ps:inspect" { - dokku config:set "$TEST_APP" key=value key=value=value - deploy_app dockerfile + run /bin/bash -c "dokku config:set $TEST_APP key=value key=value=value" + echo "output: $output" + echo "status: $status" + assert_success + + run deploy_app dockerfile + echo "output: $output" + echo "status: $status" + assert_success CID=$(<$DOKKU_ROOT/$TEST_APP/CONTAINER.web.1) run /bin/bash -c "dokku ps:inspect $TEST_APP" @@ -113,7 +120,10 @@ EOF echo "status: $status" assert_output "$test_restart_policy" - deploy_app dockerfile + run deploy_app dockerfile + echo "output: $output" + echo "status: $status" + assert_success CID=$(<$DOKKU_ROOT/$TEST_APP/CONTAINER.web.1) run /bin/bash -c "docker inspect -f '{{ .HostConfig.RestartPolicy.Name }}:{{ .HostConfig.RestartPolicy.MaximumRetryCount }}' $CID" diff --git a/tests/unit/ps-herokuish-1.bats b/tests/unit/ps-herokuish-1.bats index ce2c133c7..31ecd2c65 100644 --- a/tests/unit/ps-herokuish-1.bats +++ b/tests/unit/ps-herokuish-1.bats @@ -13,7 +13,11 @@ teardown() { } @test "(ps) herokuish" { - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success + run /bin/bash -c "dokku ps:stop $TEST_APP" echo "output: $output" echo "status: $status" diff --git a/tests/unit/ps-herokuish-2.bats b/tests/unit/ps-herokuish-2.bats index ef6ba5a1e..cab55128f 100644 --- a/tests/unit/ps-herokuish-2.bats +++ b/tests/unit/ps-herokuish-2.bats @@ -19,7 +19,10 @@ teardown() { echo "status: $status" assert_success - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success for PROC_TYPE in web worker; do CIDS="" for CID_FILE in $DOKKU_ROOT/$TEST_APP/CONTAINER.$PROC_TYPE.*; do @@ -71,8 +74,15 @@ teardown() { @test "(ps:restore) herokuish" { MYAPP="manual-randomtestapp-1" - create_app "$MYAPP" - deploy_app + run create_app "$MYAPP" + echo "output: $output" + echo "status: $status" + assert_success + + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success run /bin/bash -c "dokku ps:stop $TEST_APP" echo "output: $output" diff --git a/tests/unit/report.bats b/tests/unit/report.bats index 2c3d27f6b..fc280fae8 100644 --- a/tests/unit/report.bats +++ b/tests/unit/report.bats @@ -39,14 +39,21 @@ teardown() { assert_output_contains "App fake-app-name does not exist" assert_failure - dokku apps:create "${TEST_APP}-2" + run /bin/bash -c "dokku apps:create ${TEST_APP}-2" + echo "output: $output" + echo "status: $status" + assert_success + run /bin/bash -c "dokku report ${TEST_APP}-2 2>&1" echo "output: $output" echo "status: $status" assert_output_contains "Deployed: false" assert_success - dokku --force apps:destroy "${TEST_APP}-2" + run /bin/bash -c "dokku --force apps:destroy ${TEST_APP}-2" + echo "output: $output" + echo "status: $status" + assert_success } @test "(report) custom docker bin" { diff --git a/tests/unit/resource_1.bats b/tests/unit/resource_1.bats index 88836a461..c36031e89 100644 --- a/tests/unit/resource_1.bats +++ b/tests/unit/resource_1.bats @@ -32,7 +32,11 @@ teardown() { echo "status: $status" assert_output_contains "resource limits $TEST_APP information" - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success + run /bin/bash -c "dokku resource:limit --memory 512MB $TEST_APP" echo "output: $output" echo "status: $status" @@ -44,7 +48,11 @@ teardown() { echo "status: $status" assert_output "0" - dokku ps:rebuild "$TEST_APP" + run /bin/bash -c "dokku ps:rebuild $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + CID=$(<$DOKKU_ROOT/$TEST_APP/CONTAINER.web.1) run /bin/bash -c "docker inspect --format '{{.HostConfig.Memory}}' $CID" echo "output: $output" @@ -56,7 +64,11 @@ teardown() { echo "status: $status" assert_success - dokku ps:rebuild "$TEST_APP" + run /bin/bash -c "dokku ps:rebuild $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + CID=$(<$DOKKU_ROOT/$TEST_APP/CONTAINER.web.1) run /bin/bash -c "docker inspect --format '{{.HostConfig.Memory}}' $CID" echo "output: $output" @@ -68,7 +80,11 @@ teardown() { echo "status: $status" assert_success - dokku ps:rebuild "$TEST_APP" + run /bin/bash -c "dokku ps:rebuild $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + CID=$(<$DOKKU_ROOT/$TEST_APP/CONTAINER.web.1) run /bin/bash -c "docker inspect --format '{{.HostConfig.Memory}}' $CID" echo "output: $output" @@ -80,7 +96,11 @@ teardown() { echo "status: $status" assert_success - dokku ps:rebuild "$TEST_APP" + run /bin/bash -c "dokku ps:rebuild $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + CID=$(<$DOKKU_ROOT/$TEST_APP/CONTAINER.web.1) run /bin/bash -c "docker inspect --format '{{.HostConfig.Memory}}' $CID" echo "output: $output" @@ -92,7 +112,11 @@ teardown() { echo "status: $status" assert_success - dokku ps:rebuild "$TEST_APP" + run /bin/bash -c "dokku ps:rebuild $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + CID=$(<$DOKKU_ROOT/$TEST_APP/CONTAINER.web.1) run /bin/bash -c "docker inspect --format '{{.HostConfig.Memory}}' $CID" echo "output: $output" @@ -104,7 +128,11 @@ teardown() { echo "status: $status" assert_success - dokku ps:rebuild "$TEST_APP" + run /bin/bash -c "dokku ps:rebuild $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + CID=$(<$DOKKU_ROOT/$TEST_APP/CONTAINER.web.1) run /bin/bash -c "docker inspect --format '{{.HostConfig.Memory}}' $CID" echo "output: $output" diff --git a/tests/unit/resource_2.bats b/tests/unit/resource_2.bats index 44bc4198e..5ef9bfde1 100644 --- a/tests/unit/resource_2.bats +++ b/tests/unit/resource_2.bats @@ -18,7 +18,11 @@ teardown() { echo "status: $status" assert_output_contains "resource reservation $TEST_APP information" - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success + run /bin/bash -c "dokku resource:reserve --memory 512MB $TEST_APP" echo "output: $output" echo "status: $status" @@ -30,7 +34,11 @@ teardown() { echo "status: $status" assert_output "0" - dokku ps:rebuild "$TEST_APP" + run /bin/bash -c "dokku ps:rebuild $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + CID=$(<$DOKKU_ROOT/$TEST_APP/CONTAINER.web.1) run /bin/bash -c "docker inspect --format '{{.HostConfig.MemoryReservation}}' $CID" echo "output: $output" @@ -47,7 +55,11 @@ teardown() { echo "status: $status" assert_success - dokku ps:rebuild "$TEST_APP" + run /bin/bash -c "dokku ps:rebuild $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + CID=$(<$DOKKU_ROOT/$TEST_APP/CONTAINER.web.1) run /bin/bash -c "docker inspect --format '{{.HostConfig.MemoryReservation}}' $CID" echo "output: $output" @@ -59,7 +71,11 @@ teardown() { echo "status: $status" assert_success - dokku ps:rebuild "$TEST_APP" + run /bin/bash -c "dokku ps:rebuild $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + CID=$(<$DOKKU_ROOT/$TEST_APP/CONTAINER.web.1) run /bin/bash -c "docker inspect --format '{{.HostConfig.MemoryReservation}}' $CID" echo "output: $output" @@ -71,7 +87,11 @@ teardown() { echo "status: $status" assert_success - dokku ps:rebuild "$TEST_APP" + run /bin/bash -c "dokku ps:rebuild $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + CID=$(<$DOKKU_ROOT/$TEST_APP/CONTAINER.web.1) run /bin/bash -c "docker inspect --format '{{.HostConfig.MemoryReservation}}' $CID" echo "output: $output" @@ -83,7 +103,11 @@ teardown() { echo "status: $status" assert_success - dokku ps:rebuild "$TEST_APP" + run /bin/bash -c "dokku ps:rebuild $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + CID=$(<$DOKKU_ROOT/$TEST_APP/CONTAINER.web.1) run /bin/bash -c "docker inspect --format '{{.HostConfig.MemoryReservation}}' $CID" echo "output: $output" diff --git a/tests/unit/run_1.bats b/tests/unit/run_1.bats index d157ef4f0..a2a9aeea8 100644 --- a/tests/unit/run_1.bats +++ b/tests/unit/run_1.bats @@ -24,7 +24,11 @@ teardown() { } @test "(run) run (with --options)" { - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success + run /bin/bash -c "dokku --force --quiet run $TEST_APP python -V" echo "output: $output" echo "status: $status" diff --git a/tests/unit/run_2.bats b/tests/unit/run_2.bats index 5e7b4af3e..d6f8e860a 100644 --- a/tests/unit/run_2.bats +++ b/tests/unit/run_2.bats @@ -13,7 +13,10 @@ teardown() { } @test "(run) run" { - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success run /bin/bash -c "dokku run $TEST_APP echo $TEST_APP" echo "output: $output" @@ -27,7 +30,10 @@ teardown() { } @test "(run) run:detached" { - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success RANDOM_RUN_CID="$(dokku --label=com.dokku.test-label=value run:detached $TEST_APP sleep 300)" run /bin/bash -c "docker inspect -f '{{ .State.Status }}' $RANDOM_RUN_CID" @@ -53,7 +59,11 @@ teardown() { } @test "(run) run (with tty)" { - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success + run /bin/bash -c "dokku run $TEST_APP ls /app/null" echo "output: $output" echo "status: $status" @@ -61,7 +71,11 @@ teardown() { } @test "(run) run (without tty)" { - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success + run /bin/bash -c ": |dokku run $TEST_APP ls /app/null" echo "output: $output" echo "status: $status" @@ -69,7 +83,11 @@ teardown() { } @test "(run) run command from Procfile" { - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success + run /bin/bash -c "dokku run $TEST_APP custom 'hi dokku' | tail -n 1" echo "output: $output" echo "status: $status" @@ -79,7 +97,10 @@ teardown() { } @test "(run) list" { - deploy_app + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success run /bin/bash -c "dokku --quiet run:list $TEST_APP" echo "output: $output" diff --git a/tests/unit/test_helper.bash b/tests/unit/test_helper.bash index 4e6dedb6f..88993b5c3 100644 --- a/tests/unit/test_helper.bash +++ b/tests/unit/test_helper.bash @@ -195,9 +195,7 @@ create_key() { } destroy_app() { - local RC="$1" - local RC=${RC:=0} - local APP="$2" + declare RC="${1:-0}" APP="$2" local TEST_APP=${APP:=$TEST_APP} dokku --force apps:destroy "$TEST_APP" return "$RC" From aee691a5fb5fc772ee0345f54c92549bdf5b29a9 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Tue, 1 Aug 2023 01:37:35 -0400 Subject: [PATCH 43/61] fix: use correct namespace for haproxy service state tracking --- plugins/haproxy-vhosts/command-functions | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/haproxy-vhosts/command-functions b/plugins/haproxy-vhosts/command-functions index 648165602..565d532d4 100755 --- a/plugins/haproxy-vhosts/command-functions +++ b/plugins/haproxy-vhosts/command-functions @@ -129,7 +129,7 @@ cmd-haproxy-start() { local TMP_COMPOSE_FILE=$(mktemp "/tmp/dokku-${DOKKU_PID}-${FUNCNAME[0]}.XXXXXX") trap "rm -rf '$TMP_COMPOSE_FILE' >/dev/null" RETURN INT TERM EXIT - fn-plugin-property-write "caddy" "--global" "proxy-status" "started" + fn-plugin-property-write "haproxy" "--global" "proxy-status" "started" fn-haproxy-template-compose-file "$TMP_COMPOSE_FILE" "$DOCKER_BIN" compose -f "$TMP_COMPOSE_FILE" -p haproxy up -d --quiet-pull } From 72e8c8d9782b44cdeab41d07c65bae1fda52f538 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sat, 5 Aug 2023 01:37:28 -0400 Subject: [PATCH 44/61] tests: correct assertion for dockerfile builds when a variable is eval'd --- tests/unit/builder-dockerfile.bats | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/builder-dockerfile.bats b/tests/unit/builder-dockerfile.bats index c770138fc..1f304287b 100644 --- a/tests/unit/builder-dockerfile.bats +++ b/tests/unit/builder-dockerfile.bats @@ -64,5 +64,5 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - assert_output_contains "TOKEN is: custom-value" + assert_output_contains "TOKEN is: custom-value" 2 } From a84f9bca72b20c27a2ca499bb93f1ed808e287c5 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sat, 5 Aug 2023 01:40:47 -0400 Subject: [PATCH 45/61] tests: remove assertion for unconsumed build arguments Docker no longer outputs any logging for unconsumed build arguments with the switch to buildx. --- tests/unit/docker-options.bats | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/docker-options.bats b/tests/unit/docker-options.bats index ca0d350ff..74eba78f9 100644 --- a/tests/unit/docker-options.bats +++ b/tests/unit/docker-options.bats @@ -323,7 +323,7 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - assert_output_contains "One or more build-args \[PAYPAL_CLIENT_ID PAYPAL_CLIENT_MODE\] were not consumed" + # assert_output_contains "One or more build-args \[PAYPAL_CLIENT_ID PAYPAL_CLIENT_MODE\] were not consumed" CID=$(<$DOKKU_ROOT/$TEST_APP/CONTAINER.web.1) run /bin/bash -c "docker inspect -f '{{ .Config.Volumes }}' $CID | sed -e 's:map::g' | tr -d '[]' | tr ' ' $'\n' | sort | xargs" From 56e18be2cf2e62d5e2854ea8a8f3b5576fe9e022 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sat, 5 Aug 2023 01:57:54 -0400 Subject: [PATCH 46/61] feat: upgrade vector image from 0.23.x to 0.31.x --- plugins/logs/logs.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/logs/logs.go b/plugins/logs/logs.go index 752915a2d..52947051a 100644 --- a/plugins/logs/logs.go +++ b/plugins/logs/logs.go @@ -24,7 +24,7 @@ var ( ) // VectorImage contains the default vector image to run -const VectorImage = "timberio/vector:0.23.X-debian" +const VectorImage = "timberio/vector:0.31.X-debian" // VectorDefaultSink contains the default sink in use for vector log shipping const VectorDefaultSink = "blackhole://?print_interval_secs=1" From 3218a496c58dda7df45585d852b4150263c49790 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sat, 5 Aug 2023 01:57:18 -0400 Subject: [PATCH 47/61] feat: upgrade caddy image from 2.7 to 2.8 --- plugins/caddy-vhosts/internal-functions | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/caddy-vhosts/internal-functions b/plugins/caddy-vhosts/internal-functions index 1df11b0a8..b03a309d7 100755 --- a/plugins/caddy-vhosts/internal-functions +++ b/plugins/caddy-vhosts/internal-functions @@ -46,7 +46,7 @@ fn-caddy-template-compose-file() { } fn-caddy-image() { - fn-plugin-property-get-default "caddy" "--global" "image" "lucaslorentz/caddy-docker-proxy:2.7" + fn-plugin-property-get-default "caddy" "--global" "image" "lucaslorentz/caddy-docker-proxy:2.8" } fn-caddy-letsencrypt-email() { From d06f337c3cb346765af13d22d036176a5ce240e1 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sat, 5 Aug 2023 01:56:52 -0400 Subject: [PATCH 48/61] feat: upgrade traefik image from v2.8 to v2.10 --- plugins/traefik-vhosts/internal-functions | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/traefik-vhosts/internal-functions b/plugins/traefik-vhosts/internal-functions index 9bc89ee1a..51af09ef1 100755 --- a/plugins/traefik-vhosts/internal-functions +++ b/plugins/traefik-vhosts/internal-functions @@ -76,7 +76,7 @@ fn-traefik-dashboard-enabled() { } fn-traefik-image() { - fn-plugin-property-get-default "traefik" "--global" "image" "traefik:v2.8" + fn-plugin-property-get-default "traefik" "--global" "image" "traefik:v2.10" } fn-traefik-letsencrypt-email() { From 6168b0c71c3fbb2d553f97b321dcd74743e9c291 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sat, 5 Aug 2023 09:49:49 -0400 Subject: [PATCH 49/61] tests: correct assertion for dockerfile builds when a variable is eval'd --- tests/unit/docker-options.bats | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/docker-options.bats b/tests/unit/docker-options.bats index 74eba78f9..911f57125 100644 --- a/tests/unit/docker-options.bats +++ b/tests/unit/docker-options.bats @@ -368,5 +368,5 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - assert_output_contains "TOKEN is: hello" + assert_output_contains "TOKEN is: hello" 2 } From f8ccf5207930cda3368471f589d94795f6e38929 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Tue, 11 Jul 2023 21:06:40 -0400 Subject: [PATCH 50/61] refactor: only use detected port mapping if override is not specified Previously, we would always set the port mapping during a dockerfile build, making it difficult for users to override mappings. We also only _sometimes_ updated the detected port mapping, further confusing issues when users were migrating from Dockerfile to Buildpacks for builds. Now, we always detect the port mapping during the build process, and only use that detected port mapping if an override is not specified. This greatly simplifies the experience around port mapping, as now a user can create an app, set a port mapping, and that first deploy will respect the port mapping without an additional deploy. The builder always has the best context for what the app should be listening on, and thus we can always specify a "default" port mapping at this stage. Users can override this map as desired later. This change also results in the removal of a ton of internal code that is now centralized in the ports plugin. Closes #4067 --- docs/appendices/0.31.0-migration-guide.md | 14 +- docs/configuration/environment-variables.md | 1 - docs/development/plugin-triggers.md | 66 +++------ docs/networking/port-management.md | 22 --- docs/networking/proxies/nginx.md | 1 - .../20_events/ports-dockerfile-raw-tcp-ports | 1 - plugins/builder-dockerfile/builder-build | 4 - plugins/builder-dockerfile/internal-functions | 4 +- plugins/builder-herokuish/builder-build | 5 +- plugins/builder-lambda/builder-build | 4 +- plugins/builder-pack/builder-build | 4 +- plugins/checks/subcommands/run | 12 +- plugins/common/functions | 21 +-- plugins/common/ports-get-available | 6 - plugins/domains/internal-functions | 27 +--- plugins/network/Makefile | 2 +- plugins/network/network.go | 66 +-------- plugins/network/src/triggers/triggers.go | 11 -- plugins/network/triggers.go | 36 ----- plugins/nginx-vhosts/functions | 4 +- plugins/ports/Makefile | 2 +- plugins/ports/functions.go | 129 ++++++------------ plugins/ports/src/triggers/triggers.go | 7 +- plugins/ports/triggers.go | 45 +++--- .../bin/scheduler-deploy-process-container | 17 +-- tests/unit/builder-dockerfile.bats | 28 ++++ tests/unit/caddy.bats | 7 +- tests/unit/core_1.bats | 26 ---- tests/unit/haproxy.bats | 7 +- tests/unit/nginx-vhosts_4.bats | 5 + tests/unit/nginx-vhosts_5.bats | 6 + tests/unit/nginx-vhosts_7.bats | 4 +- tests/unit/nginx-vhosts_8.bats | 16 ++- tests/unit/ports.bats | 35 +++-- tests/unit/test_helper.bash | 2 +- tests/unit/traefik.bats | 7 +- 36 files changed, 238 insertions(+), 416 deletions(-) delete mode 120000 plugins/20_events/ports-dockerfile-raw-tcp-ports delete mode 100755 plugins/common/ports-get-available diff --git a/docs/appendices/0.31.0-migration-guide.md b/docs/appendices/0.31.0-migration-guide.md index 06f644699..7ed392dc8 100644 --- a/docs/appendices/0.31.0-migration-guide.md +++ b/docs/appendices/0.31.0-migration-guide.md @@ -7,15 +7,25 @@ - The `traefik` integration now mounts config to `/data` instead of the path `/acme.json`, fixing permissions issues under certain architectures. To take advantage of the new functionality, the traefik container should be stopped (via `dokku traefik:stop`) and then started (via `dokku traefik:start`). - Users no longer need to clear the `source-image` git property when transitioning from image-based deploys (`git:from-image` and `git:load-image`) to other deployment methods (git push, `git:from-archive`, `git:sync`). - For deploys via the `git:from-image` and `git:load-image` commands, the `CHECKS` file is now extracted from the configured `WORKDIR` property of the image. For all other deploys - git push, `git:from-archive`, `git:sync` - will have the `CHECKS` extracted directly from the source code. The filename in both cases is `CHECKS` and cannot be modified. -- The environment variable `DOKKU_PROXY_PORT_MAP` has been migrated to the properties system. Direct changes to the value will be ignored, and users should interact with port maps via the `ports` plugin. +- Port mappings are now auto-detected during the build process. Users may override detected port mappings via the `ports` plugin. A default port mapping of `http:80:5000` will be used if none is otherwise specified. ## Deprecations - The `proxy:ports*` commands have been replaced with the new `ports` plugin. Users will be able to use the old `proxy:ports*` commands for a single minor release, and they will be removed in the next minor release. -- The `common#get_app_raw_tcp_ports()` function has been deprecated and will be removed in the next release. Users should avoid interacting with this function for dockerfile ports and instead use the `ports-get` plugin trigger for fetching ports for an app. - The `common#get_available_port()` function has been deprecated and will be removed in the next release. Users should avoid interacting with this function and instead use the `ports-get-available` plugin trigger for fetching an available port. - The `proxy-configure-ports` plugin trigger has been deprecated and will be removed in the next release. Users should instead trigger the `ports-configure` plugin trigger. +- The `common#get_dockerfile_exposed_ports()` function is deprecated and will be removed in the next release. There is no replacement for this as it's only use in Dokku core was in the `builder-dockerfile` plugin. +- The `common#get_exposed_ports_from_image()` function is deprecated and will be removed in the next release. There is no replacement for this as it's only use in Dokku core was during the build process. +- The environment variable `DOKKU_PROXY_PORT_MAP` has been migrated to the properties system. Direct changes to the value will be ignored, and users should interact with port maps via the `ports` plugin. ## Un-Deprecations - The bare `apps` and `config` cli aliases are no longer deprecated. This better follows Heroku's output and is more useful to users. The subcommands will be treated as the primary, documented versions, while the aliases are there for convenience. + +## Removals + +- The variable `RAW_TCP_PORTS` is no longer exposed for `nginx.conf.sigil` templating. +- The environment variable `DOKKU_DOCKERFILE_PORTS` is no longer in use. Users wishing to change port mappings should instead use the `ports` plugin. +- The `common#get_app_raw_tcp_ports()` function has been removed in the next release. Users should instead use the `ports-get` plugin trigger for fetching ports for an app. +- The plugin trigger `network-compute-ports` has been removed. It's only use was in the `scheduler-docker-local` plugin, for exposing ports. It's functionality is now implemented via the `ports` plugin. +- The plugin trigger `network-get-port` has been removed. It's only use was in the `scheduler-docker-local` plugin, for recording ports. It's functionality is now implemented via the `ports` plugin. diff --git a/docs/configuration/environment-variables.md b/docs/configuration/environment-variables.md index eb8c02b59..464211d4a 100644 --- a/docs/configuration/environment-variables.md +++ b/docs/configuration/environment-variables.md @@ -111,7 +111,6 @@ The following config variables have special meanings and can be set in a variety | `DOKKU_DISABLE_APP_AUTOCREATION` | none | `dokku config:set` | Disables automatic creation of a non-existent app on deploy. | | `DOKKU_DOCKER_STOP_TIMEOUT` | `10` | `dokku config:set` | Configurable grace period given to the `docker stop` command. If a container has not stopped by this time, a `kill -9` signal or equivalent is sent in order to force-terminate the container. Both the `ps:stop` and `apps:destroy` commands _also_ respect this value. If not specified, the docker defaults for the [docker stop command](https://docs.docker.com/engine/reference/commandline/stop/) will be used.| | `DOKKU_DOCKERFILE_CACHE_BUILD` | none | `dokku config:set` | | -| `DOKKU_DOCKERFILE_PORTS` | dockerfile ports | `dokku config:set` | | | `DOKKU_DOCKERFILE_START_CMD` | none | `dokku config:set` | | | `DOKKU_PARALLEL_ARGUMENTS`. | none | `dokku config:set` | Allows passing custom arguments to parallel for `ps:*all` commands | | `DOKKU_PROXY_PORT` | automatically assigned | `/etc/environment`
`~dokku/.dokkurc`
`~dokku/.dokkurc/*`
`dokku config:set` | | diff --git a/docs/development/plugin-triggers.md b/docs/development/plugin-triggers.md index 4afe73354..f326282ca 100644 --- a/docs/development/plugin-triggers.md +++ b/docs/development/plugin-triggers.md @@ -1021,21 +1021,6 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x # TODO ``` -### `network-compute-ports` - -- Description: Computes the ports for a given app container -- Invoked by: `internally triggered by proxy-build-config within proxy implementations` -- Arguments: `$APP $PROC_TYPE $IS_HEROKUISH_CONTAINER $CONTAINER_INDEX` -- Example: - -```shell -#!/usr/bin/env bash - -set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x - -# TODO -``` - ### `network-config-exists` - Description: Returns whether the network configuration for a given app exists @@ -1081,21 +1066,6 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x # TODO ``` -### `network-get-port` - -- Description: Return the port for a given app container -- Invoked by: `internally triggered by a deploy` -- Arguments: `$APP $PROCESS_TYPE $CONTAINER_ID $IS_HEROKUISH_CONTAINER` -- Example: - -```shell -#!/usr/bin/env bash - -set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x - -# TODO -``` - ### `network-get-property` - Description: Return the value for an app's network property @@ -1258,24 +1228,7 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x ### `ports-configure` -- Description: Configures the port mapping -- Invoked by: `internally triggered by proxy plugins` -- Arguments: `$APP` -- Example: - -```shell -#!/usr/bin/env bash - -set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x - -# TODO -``` - -### `ports-dockerfile-raw-tcp-ports` - -> Warning: This trigger is for internal use and will be removed in a future release. Do not use it in your codebase. - -- Description: Extracts raw tcp port numbers from DOCKERFILE_PORTS config variable +- Description: Configures the initial proxy ports - Invoked by: `internally triggered by proxy plugins` - Arguments: `$APP` - Example: @@ -1318,7 +1271,24 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x # TODO ``` +### `ports-get-property` + +- Description: Return the value for an app's ports property +- Invoked by: +- Arguments: `$APP $KEY` +- Example: + +```shell +#!/usr/bin/env bash + +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +APP="$1"; PROPERTY="$2" + +# TODO +``` + ### `ports-set-detected` + - Description: Allows builders to specify detected port mappings for a given app - Invoked by: Builder plugins - Arguments: `$APP [$PORT_MAPPING...]` diff --git a/docs/networking/port-management.md b/docs/networking/port-management.md index f7a770de8..10d17a7ac 100644 --- a/docs/networking/port-management.md +++ b/docs/networking/port-management.md @@ -180,28 +180,6 @@ Any application that does not use an `EXPOSE` directive will result in Dokku def - modify your application to support the `PORT` environment variable. - switch to using an `EXPOSE` directive in your Dockerfile. -#### Switching between `EXPOSE` usage modes - -When switching between `EXPOSE` usage modes, it is important to reset your port management. The following two commands can be used to reset your state and redeploy your application. - -```shell -# assuming your application is called `node-js-app` -dokku config:unset --no-restart node-js-app DOKKU_DOCKERFILE_PORTS PORT -dokku ports:clear node-js-app -``` - -### Docker Image - -When deploying an image, we will use `docker inspect` to extract the `ExposedPorts` configuration and if defined, use that to populate port mapping. If this behavior is not desired, you can override that configuration variable with the following commands. - -```shell -# assuming your application is called `node-js-app` -dokku config:set node-js-app DOKKU_DOCKERFILE_PORTS="1234/tcp 80/tcp" -dokku ports:clear node-js-app -``` - -All other port-related behavior is the same as when deploying via Dockerfile. - ### Displaying ports reports for an app You can get a report about the app's ports status using the `ports:report` command: diff --git a/docs/networking/proxies/nginx.md b/docs/networking/proxies/nginx.md index aa34d5b10..2c85783c5 100644 --- a/docs/networking/proxies/nginx.md +++ b/docs/networking/proxies/nginx.md @@ -378,7 +378,6 @@ Unsetting this value is the same as enabling custom nginx config usage. {{ .NOSSL_SERVER_NAME }} List of non-SSL VHOSTS {{ .PROXY_PORT_MAP }} List of port mappings (same as the `map` ports property) {{ .PROXY_UPSTREAM_PORTS }} List of configured upstream ports (derived from the `map` ports property) -{{ .RAW_TCP_PORTS }} List of exposed tcp ports as defined by Dockerfile `EXPOSE` directive (**Dockerfile apps only**) {{ .SSL_INUSE }} Boolean set when an app is SSL-enabled {{ .SSL_SERVER_NAME }} List of SSL VHOSTS ``` diff --git a/plugins/20_events/ports-dockerfile-raw-tcp-ports b/plugins/20_events/ports-dockerfile-raw-tcp-ports deleted file mode 120000 index 5178a749f..000000000 --- a/plugins/20_events/ports-dockerfile-raw-tcp-ports +++ /dev/null @@ -1 +0,0 @@ -hook \ No newline at end of file diff --git a/plugins/builder-dockerfile/builder-build b/plugins/builder-dockerfile/builder-build index 42001d2eb..c5b17b9d9 100755 --- a/plugins/builder-dockerfile/builder-build +++ b/plugins/builder-dockerfile/builder-build @@ -23,10 +23,6 @@ trigger-builder-dockerfile-builder-build() { pushd "$SOURCECODE_WORK_DIR" &>/dev/null - # extract first port from Dockerfile - suppress_output dos2unix Dockerfile - local DOCKERFILE_PORTS=$(get_dockerfile_exposed_ports Dockerfile) - [[ -n "$DOCKERFILE_PORTS" ]] && config_set --no-restart "$APP" DOKKU_DOCKERFILE_PORTS="$DOCKERFILE_PORTS" plugn trigger pre-build-dockerfile "$APP" [[ "$DOKKU_DOCKERFILE_CACHE_BUILD" == "false" ]] && DOKKU_DOCKER_BUILD_OPTS="$DOKKU_DOCKER_BUILD_OPTS --no-cache" diff --git a/plugins/builder-dockerfile/internal-functions b/plugins/builder-dockerfile/internal-functions index 9a24879ed..21e36208b 100755 --- a/plugins/builder-dockerfile/internal-functions +++ b/plugins/builder-dockerfile/internal-functions @@ -130,6 +130,8 @@ fn-builder-dockerfile-get-detect-port-map() { done echo "$port_map" | xargs else - echo "http:80:5000" + # ensure we have a port mapping + plugn trigger ports-configure "$APP" + echo "http:$(plugn trigger ports-get-property "$APP" proxy-port):5000" fi } diff --git a/plugins/builder-herokuish/builder-build b/plugins/builder-herokuish/builder-build index d09f6df28..a275bc0d1 100755 --- a/plugins/builder-herokuish/builder-build +++ b/plugins/builder-herokuish/builder-build @@ -91,7 +91,10 @@ trigger-builder-herokuish-builder-build() { "$DOCKER_BIN" container commit "${DOCKER_COMMIT_LABEL_ARGS[@]}" "$CID" "$IMAGE" >/dev/null plugn trigger scheduler-register-retired "$APP" "$CID" - plugn trigger ports-set-detected "$APP" "http:80:5000" + + # ensure we have a port mapping + plugn trigger ports-configure "$APP" + plugn trigger ports-set-detected "$APP" "http:$(plugn trigger ports-get-property "$APP" proxy-port):5000" plugn trigger post-build-buildpack "$APP" "$SOURCECODE_WORK_DIR" } diff --git a/plugins/builder-lambda/builder-build b/plugins/builder-lambda/builder-build index 64beb30a0..0c6d708d8 100755 --- a/plugins/builder-lambda/builder-build +++ b/plugins/builder-lambda/builder-build @@ -36,7 +36,9 @@ trigger-builder-lambda-builder-build() { cp "$SOURCECODE_WORK_DIR/Procfile" "${DOKKU_LIB_ROOT}/data/builder-lambda/$APP/$GIT_REV.Procfile" fi - plugn trigger ports-set-detected "$APP" "http:80:5000" + # ensure we have a port mapping + plugn trigger ports-configure "$APP" + plugn trigger ports-set-detected "$APP" "http:$(plugn trigger ports-get-property "$APP" proxy-port):5000" plugn trigger post-build-lambda "$APP" } diff --git a/plugins/builder-pack/builder-build b/plugins/builder-pack/builder-build index 8a2f865b1..c3b3f4b2f 100755 --- a/plugins/builder-pack/builder-build +++ b/plugins/builder-pack/builder-build @@ -35,7 +35,9 @@ trigger-builder-pack-builder-build() { pack build "$IMAGE" --builder "$DOKKU_CNB_BUILDER" --path "$SOURCECODE_WORK_DIR" --default-process web "${ENV_ARGS[@]}" docker-image-labeler --label=dokku --label=org.label-schema.schema-version=1.0 --label=org.label-schema.vendor=dokku --label=com.dokku.image-stage=build --label=com.dokku.builder-type=pack --label=com.dokku.app-name=$APP "$IMAGE" - plugn trigger ports-set-detected "$APP" "http:80:5000" + # ensure we have a port mapping + plugn trigger ports-configure "$APP" + plugn trigger ports-set-detected "$APP" "http:$(plugn trigger ports-get-property "$APP" proxy-port):5000" plugn trigger post-build-pack "$APP" "$SOURCECODE_WORK_DIR" } diff --git a/plugins/checks/subcommands/run b/plugins/checks/subcommands/run index 8f1466957..f753778cd 100755 --- a/plugins/checks/subcommands/run +++ b/plugins/checks/subcommands/run @@ -60,7 +60,17 @@ check_process() { dokku_log_info1 "Running checks for app ($APP.$PROC_TYPE.$CONTAINER_INDEX)" CONTAINER_ID=$(<"$DOKKU_CONTAINER_ID_FILE") IP="$(plugn trigger network-get-ipaddr "$APP" "$PROC_TYPE" "$CONTAINER_ID")" - PORT="$(plugn trigger network-get-port "$APP" "$PROC_TYPE" "$CONTAINER_ID" "$IS_HEROKUISH_CONTAINER")" + PORT=5000 + + while read -r port_map; do + proxy_scheme="$(awk -F ':' '{ print $1 }' <<<"$port_map")" + proxy_host_port="$(awk -F ':' '{ print $2 }' <<<"$port_map")" + proxy_container_port="$(awk -F ':' '{ print $3 }' <<<"$port_map")" + if [[ "$proxy_scheme" != "udp" ]]; then + PORT="${proxy_container_port:="$proxy_container_port"}" + fi + done < <(plugn trigger ports-get "$APP") + plugn trigger check-deploy "$APP" "$CONTAINER_ID" "$PROC_TYPE" "$PORT" "$IP" } diff --git a/plugins/common/functions b/plugins/common/functions index 5357269ad..adf5066ae 100755 --- a/plugins/common/functions +++ b/plugins/common/functions @@ -632,18 +632,9 @@ release_and_deploy() { local APP="$1" local IMAGE_TAG="${2:-latest}" local IMAGE=$(get_app_image_name "$APP" "$IMAGE_TAG") - local DOKKU_DOCKERFILE_PORTS if verify_image "$IMAGE"; then IMAGE_SOURCE_TYPE="$(get_image_builder_type "$IMAGE" "$APP")" - if [[ "$IMAGE_SOURCE_TYPE" == "dockerfile" ]]; then - DOKKU_DOCKERFILE_PORTS=$(config_get "$APP" DOKKU_DOCKERFILE_PORTS || true) - if [[ -z "$DOKKU_DOCKERFILE_PORTS" ]]; then - local DOCKER_IMAGE_PORTS=$(get_exposed_ports_from_image "$IMAGE") - [[ -n "$DOCKER_IMAGE_PORTS" ]] && config_set --no-restart "$APP" DOKKU_DOCKERFILE_PORTS="$DOCKER_IMAGE_PORTS" - fi - fi - local DOKKU_APP_SKIP_DEPLOY="$(config_get "$APP" DOKKU_SKIP_DEPLOY || true)" local DOKKU_GLOBAL_SKIP_DEPLOY="$(config_get --global DOKKU_SKIP_DEPLOY || true)" @@ -768,12 +759,16 @@ get_ipv6_regex() { get_dockerfile_exposed_ports() { declare desc="return all exposed ports from passed file path" + dokku_log_warn "Deprecated: this function should not be used in app code" + local DOCKERFILE_PORTS=$(grep -E "^EXPOSE " "$1" | awk '{ print $2 }' | xargs) || true echo "$DOCKERFILE_PORTS" } get_exposed_ports_from_image() { declare desc="return all exposed ports from passed image name" + dokku_log_warn "Deprecated: this function should not be used in app code" + local IMAGE="$1" verify_image "$IMAGE" # shellcheck disable=SC2016 @@ -806,14 +801,6 @@ extract_directive_from_dockerfile() { echo "$FOUND_LINE" } -get_app_raw_tcp_ports() { - declare desc="extracts raw tcp port numbers from DOCKERFILE_PORTS config variable" - declare APP="$1" - - dokku_log_warn "Deprecated: this function should not be used in app code" - plugn trigger ports-dockerfile-raw-tcp-ports "$APP" | xargs -} - get_container_ports() { declare desc="returns published ports from app containers" local APP="$1" diff --git a/plugins/common/ports-get-available b/plugins/common/ports-get-available deleted file mode 100755 index f7d989333..000000000 --- a/plugins/common/ports-get-available +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash -source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" -set -eo pipefail -[[ $DOKKU_TRACE ]] && set -x - -get_available_port "$@" diff --git a/plugins/domains/internal-functions b/plugins/domains/internal-functions index cd5bcf2cd..c4a870c40 100755 --- a/plugins/domains/internal-functions +++ b/plugins/domains/internal-functions @@ -137,24 +137,15 @@ fn-domains-generate-urls() { fn-domains-generate-urls-from-config() { declare APP="$1" SCHEME="$2" VHOST="$3" DEFAULT_LISTEN_PORT="$4" local APP_PORT_MAP="$(plugn trigger ports-get "$APP")" - local RAW_TCP_PORTS="$(plugn trigger ports-dockerfile-raw-tcp-ports "$APP" | xargs)" if [[ "$(plugn trigger proxy-is-enabled "$APP")" == "false" ]]; then - if [[ -n "$RAW_TCP_PORTS" ]]; then - local APP_CONTAINER_PORTS="$(get_container_ports "$APP")" - local app_port - for listen_port in $APP_CONTAINER_PORTS; do - fn-domains-generate-url "$SCHEME" "$VHOST" "$listen_port" - done - else - local DOKKU_APP_WEB_LISTENERS PORT - DOKKU_APP_WEB_LISTENERS="$(plugn trigger network-get-listeners "$APP" "web" | xargs)" - for DOKKU_APP_WEB_LISTENER in $DOKKU_APP_WEB_LISTENERS; do - listen_port="$(echo "$DOKKU_APP_WEB_LISTENER" | cut -d ':' -f2)" - fn-domains-generate-url "$SCHEME" "$VHOST" "$listen_port" - done - shopt -u nullglob - fi + local DOKKU_APP_WEB_LISTENERS PORT + DOKKU_APP_WEB_LISTENERS="$(plugn trigger network-get-listeners "$APP" "web" | xargs)" + for DOKKU_APP_WEB_LISTENER in $DOKKU_APP_WEB_LISTENERS; do + listen_port="$(echo "$DOKKU_APP_WEB_LISTENER" | cut -d ':' -f2)" + fn-domains-generate-url "$SCHEME" "$VHOST" "$listen_port" + done + shopt -u nullglob elif [[ -n "$APP_PORT_MAP" ]]; then local port_map while IFS= read -r port_map; do @@ -162,10 +153,6 @@ fn-domains-generate-urls-from-config() { local listen_port="$(awk -F ':' '{ print $2 }' <<<"$port_map")" fn-domains-generate-url "$SCHEME" "$VHOST" "$listen_port" done <<<"$APP_PORT_MAP" - elif [[ -n "$RAW_TCP_PORTS" ]]; then - for listen_port in $RAW_TCP_PORTS; do - fn-domains-generate-url "$SCHEME" "$VHOST" "$listen_port" - done else fn-domains-generate-url "$SCHEME" "$VHOST" "$DEFAULT_LISTEN_PORT" fi diff --git a/plugins/network/Makefile b/plugins/network/Makefile index 7bf1212d6..094e19513 100644 --- a/plugins/network/Makefile +++ b/plugins/network/Makefile @@ -1,5 +1,5 @@ SUBCOMMANDS = subcommands/create subcommands/destroy subcommands/exists subcommands/info subcommands/list subcommands/rebuild subcommands/rebuildall subcommands/report subcommands/set -TRIGGERS = triggers/core-post-deploy triggers/docker-args-process-build triggers/docker-args-process-deploy triggers/docker-args-process-run triggers/install triggers/network-build-config triggers/network-compute-ports triggers/network-config-exists triggers/network-get-ipaddr triggers/network-get-listeners triggers/network-get-port triggers/network-get-property triggers/network-get-static-listeners triggers/network-write-ipaddr triggers/network-write-port triggers/post-app-clone-setup triggers/post-app-rename-setup triggers/post-container-create triggers/post-create triggers/post-delete triggers/report +TRIGGERS = triggers/core-post-deploy triggers/docker-args-process-build triggers/docker-args-process-deploy triggers/docker-args-process-run triggers/install triggers/network-build-config triggers/network-config-exists triggers/network-get-ipaddr triggers/network-get-listeners triggers/network-get-property triggers/network-get-static-listeners triggers/network-write-ipaddr triggers/network-write-port triggers/post-app-clone-setup triggers/post-app-rename-setup triggers/post-container-create triggers/post-create triggers/post-delete triggers/report BUILD = commands subcommands triggers PLUGIN_NAME = network diff --git a/plugins/network/network.go b/plugins/network/network.go index 26e636aef..13b1ed54b 100644 --- a/plugins/network/network.go +++ b/plugins/network/network.go @@ -2,7 +2,6 @@ package network import ( "fmt" - "io/ioutil" "net" "os" "path/filepath" @@ -10,9 +9,6 @@ import ( "strings" "github.com/dokku/dokku/plugins/common" - "github.com/dokku/dokku/plugins/config" - - sh "github.com/codeskyblue/go-sh" ) var ( @@ -65,8 +61,6 @@ func BuildConfig(appName string) error { return nil } - image := common.GetAppImageName(appName, "", "") - isHerokuishContainer := common.IsImageHerokuishBased(image, appName) common.LogInfo1(fmt.Sprintf("Ensuring network configuration is in sync for %s", appName)) for processType, procCount := range scale { @@ -82,8 +76,6 @@ func BuildConfig(appName string) error { } ipAddress := GetContainerIpaddress(appName, processType, containerID) - port := GetContainerPort(appName, processType, containerID, isHerokuishContainer) - if ipAddress != "" { args := []string{appName, processType, containerIndexString, ipAddress} _, err := common.PlugnTriggerOutput("network-write-ipaddr", args...) @@ -91,14 +83,6 @@ func BuildConfig(appName string) error { common.LogWarn(err.Error()) } } - - if port != "" { - args := []string{appName, processType, containerIndexString, port} - _, err := common.PlugnTriggerOutput("network-write-port", args...) - if err != nil { - common.LogWarn(err.Error()) - } - } } } @@ -147,53 +131,6 @@ func GetContainerIpaddress(appName, processType, containerID string) (ipAddr str return } -// GetContainerPort returns the port for a given app container -func GetContainerPort(appName, processType string, containerID string, isHerokuishContainer bool) (port string) { - if processType == "web" { - if staticWebListener := reportStaticWebListener(appName); staticWebListener != "" { - _, port, err := net.SplitHostPort(staticWebListener) - if err == nil { - return port - } - - return "80" - } - } - - dockerfilePorts := make([]string, 0) - if !isHerokuishContainer { - configValue := config.GetWithDefault(appName, "DOKKU_DOCKERFILE_PORTS", "") - if configValue != "" { - dockerfilePorts = strings.Split(configValue, " ") - } - } - - if len(dockerfilePorts) > 0 { - for _, p := range dockerfilePorts { - if strings.HasSuffix(p, "/udp") { - continue - } - port = strings.TrimSuffix(p, "/tcp") - if port != "" { - break - } - } - - if port != "" { - cmd := sh.Command(common.DockerBin(), "container", "port", containerID, port) - cmd.Stderr = ioutil.Discard - b, err := cmd.Output() - if err == nil { - port = strings.Split(string(b[:]), ":")[1] - } - } - } else { - port = "5000" - } - - return -} - // GetListeners returns a string array of app listeners func GetListeners(appName string, processType string) []string { if processType == "web" { @@ -214,6 +151,9 @@ func GetListeners(appName string, processType string) []string { portfile := strings.Replace(ipfile, ipPrefix, portPrefix, 1) ipAddress := common.ReadFirstLine(ipfile) port := common.ReadFirstLine(portfile) + if port == "" { + port = "5000" + } listeners = append(listeners, fmt.Sprintf("%s:%s", ipAddress, port)) } return listeners diff --git a/plugins/network/src/triggers/triggers.go b/plugins/network/src/triggers/triggers.go index c78856d44..1a54d0f43 100644 --- a/plugins/network/src/triggers/triggers.go +++ b/plugins/network/src/triggers/triggers.go @@ -32,11 +32,6 @@ func main() { case "network-build-config": appName := flag.Arg(0) err = network.BuildConfig(appName) - case "network-compute-ports": - appName := flag.Arg(0) - processType := flag.Arg(1) - isHerokuishContainer := common.ToBool(flag.Arg(2)) - err = network.TriggerNetworkComputePorts(appName, processType, isHerokuishContainer) case "network-config-exists": appName := flag.Arg(0) err = network.TriggerNetworkConfigExists(appName) @@ -49,12 +44,6 @@ func main() { appName := flag.Arg(0) processType := flag.Arg(1) err = network.TriggerNetworkGetListeners(appName, processType) - case "network-get-port": - appName := flag.Arg(0) - processType := flag.Arg(1) - containerID := flag.Arg(2) - isHerokuishContainer := common.ToBool(flag.Arg(3)) - err = network.TriggerNetworkGetPort(appName, processType, containerID, isHerokuishContainer) case "network-get-property": appName := flag.Arg(0) property := flag.Arg(1) diff --git a/plugins/network/triggers.go b/plugins/network/triggers.go index 02673cdca..0669a8581 100644 --- a/plugins/network/triggers.go +++ b/plugins/network/triggers.go @@ -5,10 +5,8 @@ import ( "io/ioutil" "os" "strings" - "unicode/utf8" "github.com/dokku/dokku/plugins/common" - "github.com/dokku/dokku/plugins/config" ) // TriggerDockerArgsProcess outputs the network plugin docker options for an app @@ -58,33 +56,6 @@ func TriggerInstall() error { return nil } -// TriggerNetworkComputePorts computes the ports for a given app container -func TriggerNetworkComputePorts(appName string, processType string, isHerokuishContainer bool) error { - var dockerfilePorts []string - if !isHerokuishContainer { - dokkuDockerfilePorts := strings.Trim(config.GetWithDefault(appName, "DOKKU_DOCKERFILE_PORTS", ""), " ") - if utf8.RuneCountInString(dokkuDockerfilePorts) > 0 { - dockerfilePorts = strings.Split(dokkuDockerfilePorts, " ") - } - } - - var ports []string - if len(dockerfilePorts) == 0 { - ports = append(ports, "5000") - } else { - for _, port := range dockerfilePorts { - port = strings.TrimSuffix(strings.TrimSpace(port), "/tcp") - if port == "" || strings.HasSuffix(port, "/udp") { - continue - } - ports = append(ports, port) - } - } - - fmt.Println(strings.Join(ports, " ")) - return nil -} - // TriggerNetworkConfigExists writes true or false to stdout whether a given app has network config func TriggerNetworkConfigExists(appName string) error { if HasNetworkConfig(appName) { @@ -114,13 +85,6 @@ func TriggerNetworkGetListeners(appName string, processType string) error { return nil } -// TriggerNetworkGetPort writes the port to stdout for a given app container -func TriggerNetworkGetPort(appName string, processType string, containerID string, isHerokuishContainer bool) error { - port := GetContainerPort(appName, processType, containerID, isHerokuishContainer) - fmt.Println(port) - return nil -} - // TriggerNetworkGetProperty writes the network property to stdout for a given app container func TriggerNetworkGetProperty(appName string, property string) error { computedValueMap := map[string]common.ReportFunc{ diff --git a/plugins/nginx-vhosts/functions b/plugins/nginx-vhosts/functions index 902f3eb7d..6bfc97622 100755 --- a/plugins/nginx-vhosts/functions +++ b/plugins/nginx-vhosts/functions @@ -336,7 +336,6 @@ nginx_build_config() { local SCHEME=http local NGINX_TEMPLATE_SOURCE="built-in" local APP_SSL_PATH="$DOKKU_ROOT/$APP/tls" - local RAW_TCP_PORTS="$(plugn trigger ports-dockerfile-raw-tcp-ports "$APP" | xargs)" local DOKKU_APP_LISTENERS CUSTOM_NGINX_TEMPLATE="$(plugn trigger nginx-app-template-source "$APP" "app-config")" @@ -363,7 +362,6 @@ nginx_build_config() { plugn trigger ports-configure "$APP" local PROXY_PORT=$(config_get "$APP" DOKKU_PROXY_PORT) local PROXY_SSL_PORT=$(config_get "$APP" DOKKU_PROXY_SSL_PORT) - local PROXY_PORT_MAP=$(plugn trigger ports-get "$APP") local PORT_MAP PROXY_PORT_MAP proxy_port_map while read -r PORT_MAP; do @@ -492,7 +490,7 @@ nginx_build_config() { PROXY_BUSY_BUFFERS_SIZE="$PROXY_BUSY_BUFFERS_SIZE" # Deprecated: Remove this after a few versions NGINX_PORT="$PROXY_PORT" NGINX_SSL_PORT="$PROXY_SSL_PORT" - PROXY_PORT="$PROXY_PORT" PROXY_SSL_PORT="$PROXY_SSL_PORT" RAW_TCP_PORTS="$RAW_TCP_PORTS" + PROXY_PORT="$PROXY_PORT" PROXY_SSL_PORT="$PROXY_SSL_PORT" PROXY_PORT_MAP="$PROXY_PORT_MAP" PROXY_UPSTREAM_PORTS="$PROXY_UPSTREAM_PORTS" PROXY_X_FORWARDED_FOR="$PROXY_X_FORWARDED_FOR" PROXY_X_FORWARDED_PORT="$PROXY_X_FORWARDED_PORT" diff --git a/plugins/ports/Makefile b/plugins/ports/Makefile index f1d13cfbd..6affadf3a 100644 --- a/plugins/ports/Makefile +++ b/plugins/ports/Makefile @@ -1,5 +1,5 @@ SUBCOMMANDS = subcommands/list subcommands/add subcommands/clear subcommands/remove subcommands/set subcommands/report -TRIGGERS = triggers/install triggers/ports-clear triggers/ports-configure triggers/ports-dockerfile-raw-tcp-ports triggers/ports-get triggers/ports-get-available triggers/ports-set-detected triggers/post-app-clone-setup triggers/post-app-rename-setup triggers/post-certs-remove triggers/post-certs-update triggers/post-delete triggers/report +TRIGGERS = triggers/install triggers/ports-clear triggers/ports-configure triggers/ports-get triggers/ports-get-available triggers/ports-get-property triggers/ports-set-detected triggers/post-app-clone-setup triggers/post-app-rename-setup triggers/post-certs-remove triggers/post-certs-update triggers/post-delete triggers/report BUILD = commands subcommands triggers PLUGIN_NAME = ports diff --git a/plugins/ports/functions.go b/plugins/ports/functions.go index ba9358b20..842166136 100644 --- a/plugins/ports/functions.go +++ b/plugins/ports/functions.go @@ -5,7 +5,6 @@ import ( "fmt" "net" "os" - "path/filepath" "sort" "strconv" "strings" @@ -23,7 +22,11 @@ func addPortMaps(appName string, portMaps []PortMap) error { } func clearPorts(appName string) error { - return common.PropertyDelete("ports", appName, "map") + if err := common.PropertyDelete("ports", appName, "map"); err != nil { + return err + } + + return common.PropertyDelete("ports", appName, "map-detected") } func doesCertExist(appName string) bool { @@ -63,11 +66,33 @@ func getAvailablePort() int { } } +func getComputedProxyPort(appName string) int { + port := getProxyPort(appName) + if port == 0 { + port = getGlobalProxyPort() + } + + return port +} + +func getComputedProxySSLPort(appName string) int { + port := getProxySSLPort(appName) + if port == 0 { + port = getGlobalProxySSLPort() + } + + return port +} + func getDetectedPortMaps(appName string) []PortMap { + basePort := getComputedProxyPort(appName) + if basePort == 0 { + basePort = 80 + } defaultMapping := []PortMap{ { ContainerPort: 5000, - HostPort: 80, + HostPort: basePort, Scheme: "http", }, } @@ -84,6 +109,11 @@ func getDetectedPortMaps(appName string) []PortMap { if doesCertExist(appName) { setSSLPort := false + baseSSLPort := getComputedProxySSLPort(appName) + if baseSSLPort == 0 { + baseSSLPort = 443 + } + for _, portMap := range portMaps { if portMap.Scheme != "http" || portMap.HostPort != 80 { continue @@ -92,7 +122,7 @@ func getDetectedPortMaps(appName string) []PortMap { setSSLPort = true portMaps = append(portMaps, PortMap{ ContainerPort: portMap.ContainerPort, - HostPort: 443, + HostPort: baseSSLPort, Scheme: "https", }) } @@ -111,37 +141,6 @@ func getDetectedPortMaps(appName string) []PortMap { return portMaps } -func getDockerfileRawTCPPorts(appName string) []int { - b, _ := common.PlugnTriggerOutput("config-get", []string{appName, "DOKKU_DOCKERFILE_PORTS"}...) - dockerfilePorts := strings.TrimSpace(string(b[:])) - - ports := []int{} - for _, dockerfilePort := range strings.Split(dockerfilePorts, " ") { - dockerfilePort = strings.TrimSpace(dockerfilePort) - if strings.HasSuffix(dockerfilePort, "/udp") { - continue - } - - dockerfilePort = strings.TrimSuffix(dockerfilePort, "/tcp") - if dockerfilePort == "" { - continue - } - - port, err := strconv.Atoi(dockerfilePort) - if err != nil { - continue - } - - if port == 0 { - continue - } - - ports = append(ports, port) - } - - return ports -} - func getGlobalProxyPort() int { port := 0 b, _ := common.PlugnTriggerOutput("config-get-global", []string{"DOKKU_PROXY_PORT"}...) @@ -192,21 +191,18 @@ func getProxySSLPort(appName string) int { return port } -func initializeProxyPort(appName string, rawTCPPorts []int) error { +func initializeProxyPort(appName string) error { port := getProxyPort(appName) if port != 0 { return nil } - if len(rawTCPPorts) > 0 { - return nil - } - if isAppVhostEnabled(appName) { port = getGlobalProxyPort() } else { common.LogInfo1("No port set, setting to random open high port") port = getAvailablePort() + common.LogInfo1(fmt.Sprintf("Random port %d", port)) } if port == 0 { @@ -219,7 +215,7 @@ func initializeProxyPort(appName string, rawTCPPorts []int) error { return nil } -func initializeProxySSLPort(appName string, rawTCPPorts []int) error { +func initializeProxySSLPort(appName string) error { port := getProxySSLPort(appName) if port != 0 { return nil @@ -234,7 +230,7 @@ func initializeProxySSLPort(appName string, rawTCPPorts []int) error { port = 443 } - if len(rawTCPPorts) == 0 && !isAppVhostEnabled(appName) { + if !isAppVhostEnabled(appName) { common.LogInfo1("No ssl port set, setting to random open high port") port = getAvailablePort() } @@ -246,55 +242,6 @@ func initializeProxySSLPort(appName string, rawTCPPorts []int) error { return nil } -func initializePortMap(appName string, rawTCPPorts []int) error { - portMaps := getPortMaps(appName) - if len(portMaps) > 0 { - return nil - } - - if len(rawTCPPorts) > 0 { - for _, rawTcpPort := range rawTCPPorts { - portMaps = append(portMaps, PortMap{ - ContainerPort: rawTcpPort, - HostPort: rawTcpPort, - Scheme: "http", - }) - } - } else { - upstreamPort := 5000 - portFile := filepath.Join(common.AppRoot(appName), "PORT.web.1") - if common.FileExists(portFile) { - if port, err := strconv.Atoi(common.ReadFirstLine(portFile)); err == nil { - upstreamPort = port - } - } - - dokkuProxyPort := getProxyPort(appName) - dokkuProxySSLPort := getProxySSLPort(appName) - - if dokkuProxyPort != 0 { - portMaps = append(portMaps, PortMap{ - ContainerPort: upstreamPort, - HostPort: dokkuProxyPort, - Scheme: "http", - }) - } - if dokkuProxySSLPort != 0 { - portMaps = append(portMaps, PortMap{ - ContainerPort: upstreamPort, - HostPort: dokkuProxySSLPort, - Scheme: "https", - }) - } - } - - if len(portMaps) > 0 { - return setPortMaps(appName, portMaps) - } - - return nil -} - func inRange(value int, min int, max int) bool { return min < value && value < max } diff --git a/plugins/ports/src/triggers/triggers.go b/plugins/ports/src/triggers/triggers.go index 3adc5c27a..d0e7cfe25 100644 --- a/plugins/ports/src/triggers/triggers.go +++ b/plugins/ports/src/triggers/triggers.go @@ -26,14 +26,15 @@ func main() { case "ports-configure": appName := flag.Arg(0) err = ports.TriggerPortsConfigure(appName) - case "ports-dockerfile-raw-tcp-ports": - appName := flag.Arg(0) - err = ports.TriggerPortsDockerfileRawTCPPorts(appName) case "ports-get": appName := flag.Arg(0) err = ports.TriggerPortsGet(appName) case "ports-get-available": err = ports.TriggerPortsGetAvailable() + case "ports-get-property": + appName := flag.Arg(0) + property := flag.Arg(1) + err = ports.TriggerPortsGetProperty(appName, property) case "ports-set-detected": appName := flag.Arg(0) appName, portMapString := common.ShiftString(flag.Args()) diff --git a/plugins/ports/triggers.go b/plugins/ports/triggers.go index 0bb59ddf6..e872aea2d 100644 --- a/plugins/ports/triggers.go +++ b/plugins/ports/triggers.go @@ -52,36 +52,25 @@ func TriggerPortsClear(appName string) error { // TriggerPortsConfigure ensures we have a port mapping func TriggerPortsConfigure(appName string) error { - rawTCPPorts := getDockerfileRawTCPPorts(appName) - - if err := initializeProxyPort(appName, rawTCPPorts); err != nil { + if err := initializeProxyPort(appName); err != nil { return err } - if err := initializeProxySSLPort(appName, rawTCPPorts); err != nil { + if err := initializeProxySSLPort(appName); err != nil { return err } - if err := initializePortMap(appName, rawTCPPorts); err != nil { - return err - } - - return nil -} - -// TriggerRawTCPPorts extracts raw tcp port numbers from DOCKERFILE_PORTS config variable -func TriggerPortsDockerfileRawTCPPorts(appName string) error { - ports := getDockerfileRawTCPPorts(appName) - for _, port := range ports { - common.Log(fmt.Sprint(port)) - } - return nil } // TriggerPortsGet prints out the port mapping for a given app func TriggerPortsGet(appName string) error { - for _, portMap := range getPortMaps(appName) { + portMaps := getPortMaps(appName) + if len(portMaps) == 0 { + portMaps = getDetectedPortMaps(appName) + } + + for _, portMap := range portMaps { if portMap.AllowsPersistence() { continue } @@ -102,7 +91,23 @@ func TriggerPortsGetAvailable() error { return nil } -// TriggerPortsGetAvailable prints out an available port greater than 1024 +// TriggerPortsGetProperty writes the ports key to stdout for a given app container +func TriggerPortsGetProperty(appName string, key string) error { + if key == "proxy-port" { + fmt.Println(getComputedProxyPort(appName)) + return nil + } + + if key == "proxy-ssl-port" { + fmt.Println(getComputedProxySSLPort(appName)) + return nil + } + + fmt.Println(common.PropertyGet("ports", appName, key)) + return nil +} + +// TriggerPortsSetDetected writes out detected ports func TriggerPortsSetDetected(appName string, portMapString string) error { portMaps, _ := parsePortMapString(portMapString) diff --git a/plugins/scheduler-docker-local/bin/scheduler-deploy-process-container b/plugins/scheduler-docker-local/bin/scheduler-deploy-process-container index 772ee88c5..511e52348 100755 --- a/plugins/scheduler-docker-local/bin/scheduler-deploy-process-container +++ b/plugins/scheduler-docker-local/bin/scheduler-deploy-process-container @@ -33,14 +33,16 @@ main() { local DOKKU_PORT="" if [[ "$PROC_TYPE" == "web" ]]; then - ports=($(plugn trigger network-compute-ports "$APP" "$PROC_TYPE" "$DOKKU_HEROKUISH" "$CONTAINER_INDEX")) - for p in "${ports[@]}"; do - if [[ ! "$p" =~ .*udp.* ]]; then - DOKKU_PORT=${DOKKU_PORT:="$p"} + ports=($(plugn trigger ports-get "$APP")) + for port_map in "${ports[@]}"; do + local scheme="$(echo "$port_map" | cut -d':' -f1)" + local container_port="$(echo "$port_map" | cut -d':' -f3)" + if [[ "$scheme" != "udp" ]]; then + DOKKU_PORT="${DOKKU_PORT:="$container_port"}" fi if [[ "$DOKKU_NETWORK_BIND_ALL" == "true" ]]; then - DOCKER_ARGS+=" -p $p" + DOCKER_ARGS+=" -p $container_port" fi done fi @@ -59,7 +61,6 @@ main() { "$DOCKER_BIN" container start "$cid" >/dev/null || true ipaddr=$(plugn trigger network-get-ipaddr "$APP" "$PROC_TYPE" "$cid") - port=$(plugn trigger network-get-port "$APP" "$PROC_TYPE" "$cid" "$DOKKU_HEROKUISH") kill_new() { declare desc="wrapper function to kill newly started app container" @@ -81,7 +82,7 @@ main() { trap "kill_new $cid $PROC_TYPE $CONTAINER_INDEX" INT TERM EXIT if [[ "$DOKKU_CHECKS_DISABLED" == "false" ]]; then dokku_log_verbose "Attempting pre-flight checks ($PROC_TYPE.$CONTAINER_INDEX)" - plugn trigger check-deploy "$APP" "$cid" "$PROC_TYPE" "$port" "$ipaddr" "$CONTAINER_INDEX" + plugn trigger check-deploy "$APP" "$cid" "$PROC_TYPE" "$DOKKU_PORT" "$ipaddr" "$CONTAINER_INDEX" fi trap - INT TERM EXIT @@ -94,7 +95,7 @@ main() { # now using the new container [[ -n "$cid" ]] && echo "$cid" >"$DOKKU_CONTAINER_ID_FILE" [[ -n "$ipaddr" ]] && plugn trigger network-write-ipaddr "$APP" "$PROC_TYPE" "$CONTAINER_INDEX" "$ipaddr" - [[ -n "$port" ]] && plugn trigger network-write-port "$APP" "$PROC_TYPE" "$CONTAINER_INDEX" "$port" + [[ -n "$DOKKU_PORT" ]] && plugn trigger network-write-port "$APP" "$PROC_TYPE" "$CONTAINER_INDEX" "$DOKKU_PORT" # cleanup pre-migration files rm -f "$DOKKU_ROOT/$APP/CONTAINER" "$DOKKU_ROOT/$APP/IP" "$DOKKU_ROOT/$APP/PORT" diff --git a/tests/unit/builder-dockerfile.bats b/tests/unit/builder-dockerfile.bats index 1f304287b..34c80520b 100644 --- a/tests/unit/builder-dockerfile.bats +++ b/tests/unit/builder-dockerfile.bats @@ -4,9 +4,11 @@ load test_helper setup() { create_app + DOCKERFILE="$BATS_TMPDIR/Dockerfile" } teardown() { + rm -f "$DOCKERFILE" destroy_app } @@ -66,3 +68,29 @@ teardown() { assert_success assert_output_contains "TOKEN is: custom-value" 2 } + +@test "(builder-dockerfile) port exposure (dockerfile raw port)" { + source "$PLUGIN_CORE_AVAILABLE_PATH/builder-dockerfile/internal-functions" + cat <$DOCKERFILE +EXPOSE 3001/udp +EXPOSE 3003 +EXPOSE 3000/tcp +EOF + run fn-builder-dockerfile-get-ports-from-dockerfile $DOCKERFILE + echo "output: $output" + echo "status: $status" + assert_output "3001/udp 3003 3000/tcp" +} + +@test "(builder-dockerfile) port exposure (dockerfile tcp port)" { + source "$PLUGIN_CORE_AVAILABLE_PATH/builder-dockerfile/internal-functions" + cat <$DOCKERFILE +EXPOSE 3001/udp +EXPOSE 3000/tcp +EXPOSE 3003 +EOF + run fn-builder-dockerfile-get-ports-from-dockerfile $DOCKERFILE + echo "output: $output" + echo "status: $status" + assert_output "3001/udp 3000/tcp 3003" +} diff --git a/tests/unit/caddy.bats b/tests/unit/caddy.bats index 7a3c38651..f105d2347 100644 --- a/tests/unit/caddy.bats +++ b/tests/unit/caddy.bats @@ -137,7 +137,12 @@ teardown() { assert_success assert_output "$TEST_APP.dokku.me" - run /bin/bash -c "dokku ports:report $TEST_APP --ports-map" + run /bin/bash -c "dokku --quiet ports:report $TEST_APP --ports-map" + echo "output: $output" + echo "status: $status" + assert_output_not_exists + + run /bin/bash -c "dokku --quiet ports:report $TEST_APP --ports-map-detected" echo "output: $output" echo "status: $status" assert_output "http:80:5000" diff --git a/tests/unit/core_1.bats b/tests/unit/core_1.bats index 23a5a47f6..1c9d32606 100644 --- a/tests/unit/core_1.bats +++ b/tests/unit/core_1.bats @@ -46,32 +46,6 @@ teardown() { docker rm $RANDOM_RUN_CID } -@test "(core) port exposure (dockerfile raw port)" { - source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" - cat <$DOCKERFILE -EXPOSE 3001/udp -EXPOSE 3003 -EXPOSE 3000/tcp -EOF - run get_dockerfile_exposed_ports $DOCKERFILE - echo "output: $output" - echo "status: $status" - assert_output "3001/udp 3003 3000/tcp" -} - -@test "(core) port exposure (dockerfile tcp port)" { - source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" - cat <$DOCKERFILE -EXPOSE 3001/udp -EXPOSE 3000/tcp -EXPOSE 3003 -EOF - run get_dockerfile_exposed_ports $DOCKERFILE - echo "output: $output" - echo "status: $status" - assert_output "3001/udp 3000/tcp 3003" -} - @test "(core) image type detection (herokuish default user)" { run deploy_app echo "output: $output" diff --git a/tests/unit/haproxy.bats b/tests/unit/haproxy.bats index faa757bf1..0486a6e50 100644 --- a/tests/unit/haproxy.bats +++ b/tests/unit/haproxy.bats @@ -154,7 +154,12 @@ teardown() { assert_success assert_output "true" - run /bin/bash -c "dokku ports:report $TEST_APP --ports-map" + run /bin/bash -c "dokku --quiet ports:report $TEST_APP --ports-map" + echo "output: $output" + echo "status: $status" + assert_output_not_exists + + run /bin/bash -c "dokku --quiet ports:report $TEST_APP --ports-map-detected" echo "output: $output" echo "status: $status" assert_output "http:80:5000" diff --git a/tests/unit/nginx-vhosts_4.bats b/tests/unit/nginx-vhosts_4.bats index e1d0d9eb3..757942b2b 100644 --- a/tests/unit/nginx-vhosts_4.bats +++ b/tests/unit/nginx-vhosts_4.bats @@ -26,6 +26,11 @@ teardown() { echo "status: $status" assert_success + run /bin/bash -c "dokku ports:report $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + HOSTNAME="$(hostname -f)" check_urls http://${HOSTNAME}:[0-9]+ diff --git a/tests/unit/nginx-vhosts_5.bats b/tests/unit/nginx-vhosts_5.bats index 663e20d5e..f162ef21f 100644 --- a/tests/unit/nginx-vhosts_5.bats +++ b/tests/unit/nginx-vhosts_5.bats @@ -63,6 +63,9 @@ teardown() { @test "(nginx-vhosts) proxy:build-config (global DOKKU_PROXY_PORT)" { local GLOBAL_PORT=30999 run /bin/bash -c "dokku config:set --global DOKKU_PROXY_PORT=${GLOBAL_PORT}" + echo "output: $output" + echo "status: $status" + assert_success run deploy_app echo "output: $output" @@ -72,4 +75,7 @@ teardown() { assert_http_success http://${TEST_APP}.dokku.me:${GLOBAL_PORT} run /bin/bash -c "dokku config:unset --global DOKKU_PROXY_PORT" + echo "output: $output" + echo "status: $status" + assert_success } diff --git a/tests/unit/nginx-vhosts_7.bats b/tests/unit/nginx-vhosts_7.bats index 32a4f543c..a7ab2af78 100644 --- a/tests/unit/nginx-vhosts_7.bats +++ b/tests/unit/nginx-vhosts_7.bats @@ -43,7 +43,7 @@ teardown() { echo "status: $status" assert_success - run deploy_app nodejs-express dokku@dokku.me:$TEST_APP custom_nginx_template + run deploy_app python dokku@dokku.me:$TEST_APP custom_nginx_template echo "output: $output" echo "status: $status" assert_success @@ -73,7 +73,7 @@ teardown() { echo "status: $status" assert_success - run deploy_app nodejs-express dokku@dokku.me:$TEST_APP custom_nginx_template + run deploy_app python dokku@dokku.me:$TEST_APP custom_nginx_template echo "output: $output" echo "status: $status" assert_success diff --git a/tests/unit/nginx-vhosts_8.bats b/tests/unit/nginx-vhosts_8.bats index caf8885e4..c56a3cca0 100644 --- a/tests/unit/nginx-vhosts_8.bats +++ b/tests/unit/nginx-vhosts_8.bats @@ -133,19 +133,25 @@ teardown() { echo "status: $status" assert_output_contains "Ignoring detected https port mapping without an accompanying ssl certificate" 0 - teardown_test_tls - run /bin/bash -c "dokku ports:report $TEST_APP --ports-map" + run /bin/bash -c "dokku ports:report $TEST_APP --ports-map-detected" echo "output: $output" echo "status: $status" assert_output "http:80:5000 https:443:5000" + teardown_test_tls + + run /bin/bash -c "dokku ports:report $TEST_APP --ports-map-detected" + echo "output: $output" + echo "status: $status" + assert_output "http:80:5000" run /bin/bash -c "dokku proxy:build-config $TEST_APP" echo "output: $output" echo "status: $status" - assert_output_contains "Ignoring detected https port mapping without an accompanying ssl certificate" 1 + run /bin/bash -c "dokku nginx:show-config $TEST_APP" + assert_output_contains "Ignoring detected https port mapping without an accompanying ssl certificate" 0 - run /bin/bash -c "dokku ports:report $TEST_APP --ports-map" + run /bin/bash -c "dokku ports:report $TEST_APP --ports-map-detected" echo "output: $output" echo "status: $status" - assert_output "http:80:5000 https:443:5000" + assert_output "http:80:5000" } diff --git a/tests/unit/ports.bats b/tests/unit/ports.bats index 82df14618..2dbb30772 100644 --- a/tests/unit/ports.bats +++ b/tests/unit/ports.bats @@ -34,60 +34,65 @@ teardown() { echo "status: $status" assert_success - run /bin/bash -c "dokku --quiet ports:list $TEST_APP | xargs" + run /bin/bash -c "dokku --quiet ports:report $TEST_APP --ports-map" echo "output: $output" echo "status: $status" - assert_output "http 1234 5001" + assert_output "http:1234:5001" run /bin/bash -c "dokku ports:add $TEST_APP http:8080:5002 https:8443:5003" echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "dokku --quiet ports:list $TEST_APP | xargs" + run /bin/bash -c "dokku --quiet ports:report $TEST_APP --ports-map" echo "output: $output" echo "status: $status" - assert_output "http 1234 5001 http 8080 5002 https 8443 5003" + assert_output "http:1234:5001 http:8080:5002 https:8443:5003" run /bin/bash -c "dokku ports:set $TEST_APP http:8080:5000 https:8443:5000 http:1234:5001" echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "dokku --quiet ports:list $TEST_APP | xargs" + run /bin/bash -c "dokku --quiet ports:report $TEST_APP --ports-map" echo "output: $output" echo "status: $status" - assert_output "http 1234 5001 http 8080 5000 https 8443 5000" + assert_output "http:1234:5001 http:8080:5000 https:8443:5000" run /bin/bash -c "dokku ports:remove $TEST_APP 8080" echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "dokku --quiet ports:list $TEST_APP | xargs" + run /bin/bash -c "dokku --quiet ports:report $TEST_APP --ports-map" echo "output: $output" echo "status: $status" - assert_output "http 1234 5001 https 8443 5000" + assert_output "http:1234:5001 https:8443:5000" run /bin/bash -c "dokku ports:remove $TEST_APP http:1234:5001" echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "dokku --quiet ports:list $TEST_APP | xargs" + run /bin/bash -c "dokku --quiet ports:report $TEST_APP --ports-map" echo "output: $output" echo "status: $status" - assert_output "https 8443 5000" + assert_output "https:8443:5000" run /bin/bash -c "dokku ports:clear $TEST_APP" echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "dokku --quiet ports:list $TEST_APP | xargs" + run /bin/bash -c "dokku --quiet ports:report $TEST_APP --ports-map" echo "output: $output" echo "status: $status" - assert_output "http 80 5000" + assert_output_not_exists + + run /bin/bash -c "dokku --quiet ports:report $TEST_APP --ports-map-detected" + echo "output: $output" + echo "status: $status" + assert_output "http:80:5000" } @test "(ports:add) post-deploy add" { @@ -138,7 +143,7 @@ teardown() { run /bin/bash -c "dokku ports:report $TEST_APP --ports-map" echo "output: $output" echo "status: $status" - assert_output "http:80:5000 https:443:5000" + assert_output_not_exists run /bin/bash -c "dokku ports:report $TEST_APP --ports-map-detected" echo "output: $output" @@ -155,7 +160,7 @@ teardown() { run /bin/bash -c "dokku ports:report $TEST_APP --ports-map" echo "output: $output" echo "status: $status" - assert_output "http:3000:3000 http:3003:3003" + assert_output_not_exists run /bin/bash -c "dokku ports:report $TEST_APP --ports-map-detected" echo "output: $output" @@ -170,7 +175,7 @@ teardown() { run /bin/bash -c "dokku ports:report $TEST_APP --ports-map" echo "output: $output" echo "status: $status" - assert_output "http:3000:3000 http:3003:3003" + assert_output_not_exists run /bin/bash -c "dokku ports:report $TEST_APP --ports-map-detected" echo "output: $output" diff --git a/tests/unit/test_helper.bash b/tests/unit/test_helper.bash index 88993b5c3..9aea3bc32 100644 --- a/tests/unit/test_helper.bash +++ b/tests/unit/test_helper.bash @@ -224,7 +224,7 @@ assert_http_success() { assert_http_localhost_success() { local scheme="$1" domain="$2" port="${3:-80}" path="${4:-}" run curl --connect-to "$domain:$port:localhost:$port" -kSso /dev/null -w "%{http_code}" "$scheme://$domain:$port$path" - echo "curl: curl --connect-to $domain:$port:localhost:$port-kSso /dev/null -w %{http_code} $scheme://$domain:$port$path" + echo "curl: curl --connect-to $domain:$port:localhost:$port -kSso /dev/null -w %{http_code} $scheme://$domain:$port$path" echo "output: $output" echo "status: $status" assert_output "200" diff --git a/tests/unit/traefik.bats b/tests/unit/traefik.bats index f4a326c45..3f7de41b5 100644 --- a/tests/unit/traefik.bats +++ b/tests/unit/traefik.bats @@ -233,7 +233,12 @@ teardown() { assert_success assert_output "5000" - run /bin/bash -c "dokku ports:report $TEST_APP --ports-map" + run /bin/bash -c "dokku --quiet ports:report $TEST_APP --ports-map" + echo "output: $output" + echo "status: $status" + assert_output_not_exists + + run /bin/bash -c "dokku --quiet ports:report $TEST_APP --ports-map-detected" echo "output: $output" echo "status: $status" assert_output "http:80:5000" From 7f7a044a072aaef18fac311761ebfb825e75780b Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sat, 5 Aug 2023 11:07:55 -0400 Subject: [PATCH 51/61] docs: clean up references in proxy plugins --- docs/networking/proxies/caddy.md | 2 +- docs/networking/proxies/haproxy.md | 2 +- docs/networking/proxies/traefik.md | 2 +- plugins/caddy-vhosts/command-functions | 4 ++-- plugins/haproxy-vhosts/command-functions | 4 ++-- plugins/traefik-vhosts/command-functions | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/networking/proxies/caddy.md b/docs/networking/proxies/caddy.md index 0eaf00eb6..fa1e70112 100644 --- a/docs/networking/proxies/caddy.md +++ b/docs/networking/proxies/caddy.md @@ -103,7 +103,7 @@ You can use these modifiers as follows: dokku caddy:logs --tail --num 10 ``` -The above command will show logs continually from the vector container, with an initial history of 10 log lines +The above command will show logs continually from the caddy container, with an initial history of 10 log lines ### Changing the Caddy log level diff --git a/docs/networking/proxies/haproxy.md b/docs/networking/proxies/haproxy.md index d4e64d8c6..94d57faa7 100644 --- a/docs/networking/proxies/haproxy.md +++ b/docs/networking/proxies/haproxy.md @@ -15,7 +15,7 @@ haproxy:stop # Stops the haproxy server ## Requirements -Using the `caddy` plugin integration requires the `docker-compose-plugin` for Docker. See [this document](https://docs.docker.com/compose/install/) from the Docker documentation for more information on the installation process for the `docker-compose-plugin`. +Using the `haproxy` plugin integration requires the `docker-compose-plugin` for Docker. See [this document](https://docs.docker.com/compose/install/) from the Docker documentation for more information on the installation process for the `docker-compose-plugin`. ## Usage diff --git a/docs/networking/proxies/traefik.md b/docs/networking/proxies/traefik.md index 9a793870f..ceff53c77 100644 --- a/docs/networking/proxies/traefik.md +++ b/docs/networking/proxies/traefik.md @@ -102,7 +102,7 @@ You can use these modifiers as follows: dokku traefik:logs --tail --num 10 ``` -The above command will show logs continually from the vector container, with an initial history of 10 log lines +The above command will show logs continually from the traefik container, with an initial history of 10 log lines ### Changing the Traefik log level diff --git a/plugins/caddy-vhosts/command-functions b/plugins/caddy-vhosts/command-functions index 9636ef451..226ffd300 100755 --- a/plugins/caddy-vhosts/command-functions +++ b/plugins/caddy-vhosts/command-functions @@ -137,8 +137,8 @@ cmd-caddy-start() { } cmd-caddy-stop() { - declare desc="Starts the caddy server" - declare cmd="caddy:start" + declare desc="Stops the caddy server" + declare cmd="caddy:stop" [[ "$1" == "$cmd" ]] && shift 1 if ! fn-is-compose-installed; then diff --git a/plugins/haproxy-vhosts/command-functions b/plugins/haproxy-vhosts/command-functions index 565d532d4..a27a31a13 100755 --- a/plugins/haproxy-vhosts/command-functions +++ b/plugins/haproxy-vhosts/command-functions @@ -135,8 +135,8 @@ cmd-haproxy-start() { } cmd-haproxy-stop() { - declare desc="Starts the haproxy server" - declare cmd="haproxy:start" + declare desc="Stops the haproxy server" + declare cmd="haproxy:stop" [[ "$1" == "$cmd" ]] && shift 1 if ! fn-is-compose-installed; then diff --git a/plugins/traefik-vhosts/command-functions b/plugins/traefik-vhosts/command-functions index 6136fd178..68f194d5b 100755 --- a/plugins/traefik-vhosts/command-functions +++ b/plugins/traefik-vhosts/command-functions @@ -143,8 +143,8 @@ cmd-traefik-start() { } cmd-traefik-stop() { - declare desc="Starts the traefik server" - declare cmd="traefik:start" + declare desc="Stops the traefik server" + declare cmd="traefik:stop" [[ "$1" == "$cmd" ]] && shift 1 if ! fn-is-compose-installed; then From 5846301a4879ac23de89e473375ba9eaea1ec34c Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sat, 5 Aug 2023 16:32:58 -0400 Subject: [PATCH 52/61] tests: make it possible to specify an alternative base domain for tests When developing Dokku, it is useful to use a real domain to test actual functionality. This is impossible if the domain is hardcoded to dokku.me in testing. --- .github/commands/ci-setup | 16 ++-- tests.mk | 69 ++++++++-------- tests/unit/app-json-2.bats | 2 +- tests/unit/app-json-3.bats | 4 +- tests/unit/app-json.bats | 8 +- tests/unit/build-env.bats | 2 +- tests/unit/builder-herokuish.bats | 2 +- tests/unit/builder-lambda.bats | 2 +- tests/unit/builder-pack.bats | 2 +- tests/unit/builder.bats | 8 +- tests/unit/buildpacks.bats | 2 +- tests/unit/caddy.bats | 16 ++-- tests/unit/checks.bats | 2 +- tests/unit/client.bats | 16 ++-- tests/unit/config.bats | 12 +-- tests/unit/core_2.bats | 24 +++--- tests/unit/cron.bats | 18 ++-- tests/unit/docker-options.bats | 2 +- tests/unit/domains.bats | 110 ++++++++++++------------- tests/unit/git_4.bats | 4 +- tests/unit/git_5.bats | 10 +-- tests/unit/git_7.bats | 4 +- tests/unit/haproxy.bats | 12 +-- tests/unit/logs.bats | 2 +- tests/unit/network.bats | 12 +-- tests/unit/nginx-vhosts_1.bats | 16 ++-- tests/unit/nginx-vhosts_11.bats | 2 +- tests/unit/nginx-vhosts_12.bats | 2 +- tests/unit/nginx-vhosts_2.bats | 32 +++---- tests/unit/nginx-vhosts_3.bats | 6 +- tests/unit/nginx-vhosts_4.bats | 10 +-- tests/unit/nginx-vhosts_5.bats | 28 +++---- tests/unit/nginx-vhosts_7.bats | 26 +++--- tests/unit/nginx-vhosts_9.bats | 16 ++-- tests/unit/ports.bats | 6 +- tests/unit/proxied-app.bats | 2 +- tests/unit/proxy.bats | 4 +- tests/unit/ps-cnb.bats | 2 +- tests/unit/ps-general-1.bats | 2 +- tests/unit/run_1.bats | 4 +- tests/unit/scheduler-docker-local.bats | 6 +- tests/unit/test_helper.bash | 11 +-- tests/unit/traefik.bats | 14 ++-- 43 files changed, 278 insertions(+), 272 deletions(-) diff --git a/.github/commands/ci-setup b/.github/commands/ci-setup index 43c3ac6f6..f756adff8 100755 --- a/.github/commands/ci-setup +++ b/.github/commands/ci-setup @@ -2,19 +2,23 @@ set -eo pipefail set -x +DOKKU_DOMAIN=${DOKKU_DOMAIN:="dokku.me"} + echo "=====> set hostname" hostname internal_ip="$(ifconfig eth0 | grep 'inet ' | awk '{print $2}')" -sudo hostname "dokku.me" -echo "dokku.me" | sudo tee /etc/hostname +sudo hostname "$DOKKU_DOMAIN" +echo "$DOKKU_DOMAIN" | sudo tee /etc/hostname hostname sudo cat /etc/hostname -echo "=====> resolve dokku.me" +echo "=====> resolve $DOKKU_DOMAIN" sudo apt -qq -y --no-install-recommends install net-tools -# dokku.me now resolves to 10.0.0.2. add 10.0.0.2/24 to eth0 -ifconfig -sudo ip addr add 10.0.0.2/24 broadcast 10.0.0.255 dev eth0 +if [[ "$DOKKU_DOMAIN" == "dokku.me" ]]; then + # dokku.me now resolves to 10.0.0.2. add 10.0.0.2/24 to eth0 + ifconfig + sudo ip addr add 10.0.0.2/24 broadcast 10.0.0.255 dev eth0 +fi echo "=====> install ci-dependencies" make ci-dependencies diff --git a/tests.mk b/tests.mk index 2b57677e5..a6493efdb 100644 --- a/tests.mk +++ b/tests.mk @@ -1,5 +1,6 @@ SYSTEM := $(shell sh -c 'uname -s 2>/dev/null') DOKKU_SSH_PORT ?= 22 +DOKKU_DOMAIN ?= dokku.me bats: ifeq ($(SYSTEM),Darwin) @@ -48,8 +49,8 @@ ifdef ENABLE_DOKKU_TRACE echo "-----> Enable dokku trace" dokku trace:on endif - @echo "Setting dokku.me in /etc/hosts" - sudo /bin/bash -c "[[ `ping -c1 dokku.me >/dev/null 2>&1; echo $$?` -eq 0 ]] || echo \"127.0.0.1 dokku.me *.dokku.me www.test.app.dokku.me\" >> /etc/hosts" + @echo "Setting $(DOKKU_DOMAIN) in /etc/hosts" + sudo /bin/bash -c "[[ `ping -c1 $(DOKKU_DOMAIN) >/dev/null 2>&1; echo $$?` -eq 0 ]] || echo \"127.0.0.1 $(DOKKU_DOMAIN) *.$(DOKKU_DOMAIN) www.test.app.$(DOKKU_DOMAIN)\" >> /etc/hosts" @echo "-----> Generating keypair..." mkdir -p /root/.ssh @@ -61,10 +62,10 @@ endif @echo "-----> Setting up ssh config..." ifneq ($(shell ls /root/.ssh/config >/dev/null 2>&1 ; echo $$?),0) - echo "Host dokku.me \\r\\n Port $(DOKKU_SSH_PORT) \\r\\n RequestTTY yes \\r\\n IdentityFile /root/.ssh/dokku_test_rsa" >> /root/.ssh/config + echo "Host $(DOKKU_DOMAIN) \\r\\n Port $(DOKKU_SSH_PORT) \\r\\n RequestTTY yes \\r\\n IdentityFile /root/.ssh/dokku_test_rsa" >> /root/.ssh/config echo "Host 127.0.0.1 \\r\\n Port 22333 \\r\\n RequestTTY yes \\r\\n IdentityFile /root/.ssh/dokku_test_rsa" >> /root/.ssh/config -else ifeq ($(shell grep dokku.me /root/.ssh/config),) - echo "Host dokku.me \\r\\n Port $(DOKKU_SSH_PORT) \\r\\n RequestTTY yes \\r\\n IdentityFile /root/.ssh/dokku_test_rsa" >> /root/.ssh/config +else ifeq ($(shell grep $(DOKKU_DOMAIN) /root/.ssh/config),) + echo "Host $(DOKKU_DOMAIN) \\r\\n Port $(DOKKU_SSH_PORT) \\r\\n RequestTTY yes \\r\\n IdentityFile /root/.ssh/dokku_test_rsa" >> /root/.ssh/config echo "Host 127.0.0.1 \\r\\n Port 22333 \\r\\n RequestTTY yes \\r\\n IdentityFile /root/.ssh/dokku_test_rsa" >> /root/.ssh/config else sed --in-place 's/Port 22 \r/Port $(DOKKU_SSH_PORT) \r/g' /root/.ssh/config @@ -86,9 +87,9 @@ endif chmod 700 /home/dokku/.ssh chmod 600 /home/dokku/.ssh/authorized_keys -ifeq ($(shell grep dokku.me /home/dokku/VHOST 2>/dev/null),) - @echo "-----> Setting default VHOST to dokku.me..." - echo "dokku.me" > /home/dokku/VHOST +ifeq ($(shell grep $(DOKKU_DOMAIN) /home/dokku/VHOST 2>/dev/null),) + @echo "-----> Setting default VHOST to $(DOKKU_DOMAIN)..." + echo "$(DOKKU_DOMAIN)" > /home/dokku/VHOST endif ifeq ($(DOKKU_SSH_PORT), 22) $(MAKE) prime-ssh-known-hosts @@ -105,8 +106,8 @@ endif prime-ssh-known-hosts: @echo "-----> Intitial SSH connection to populate known_hosts..." - @echo "=====> SSH dokku.me" - ssh -o StrictHostKeyChecking=no dokku@dokku.me help >/dev/null + @echo "=====> SSH $(DOKKU_DOMAIN)" + ssh -o StrictHostKeyChecking=no dokku@$(DOKKU_DOMAIN) help >/dev/null @echo "=====> SSH 127.0.0.1" ssh -o StrictHostKeyChecking=no dokku@127.0.0.1 help >/dev/null @@ -181,87 +182,87 @@ endif deploy-test-go-fail-predeploy: @echo deploying go-fail-predeploy app... - cd tests && ./test_deploy ./apps/go-fail-predeploy dokku.me '' true + cd tests && ./test_deploy ./apps/go-fail-predeploy $(DOKKU_DOMAIN) '' true deploy-test-go-fail-postdeploy: @echo deploying go-fail-postdeploy app... - cd tests && ./test_deploy ./apps/go-fail-postdeploy dokku.me '' true + cd tests && ./test_deploy ./apps/go-fail-postdeploy $(DOKKU_DOMAIN) '' true deploy-test-checks-root: @echo deploying checks-root app... - cd tests && ./test_deploy ./apps/checks-root dokku.me '' true + cd tests && ./test_deploy ./apps/checks-root $(DOKKU_DOMAIN) '' true deploy-test-main-branch: @echo deploying checks-root app to main branch... - cd tests && ./test_deploy ./apps/checks-root dokku.me '' true main + cd tests && ./test_deploy ./apps/checks-root $(DOKKU_DOMAIN) '' true main deploy-test-clojure: @echo deploying config app... - cd tests && ./test_deploy ./apps/clojure dokku.me + cd tests && ./test_deploy ./apps/clojure $(DOKKU_DOMAIN) deploy-test-config: @echo deploying config app... - cd tests && ./test_deploy ./apps/config dokku.me + cd tests && ./test_deploy ./apps/config $(DOKKU_DOMAIN) deploy-test-dockerfile: @echo deploying dockerfile app... - cd tests && ./test_deploy ./apps/dockerfile dokku.me + cd tests && ./test_deploy ./apps/dockerfile $(DOKKU_DOMAIN) deploy-test-dockerfile-noexpose: @echo deploying dockerfile-noexpose app... - cd tests && ./test_deploy ./apps/dockerfile-noexpose dokku.me + cd tests && ./test_deploy ./apps/dockerfile-noexpose $(DOKKU_DOMAIN) deploy-test-dockerfile-procfile: @echo deploying dockerfile-procfile app... - cd tests && ./test_deploy ./apps/dockerfile-procfile dokku.me + cd tests && ./test_deploy ./apps/dockerfile-procfile $(DOKKU_DOMAIN) deploy-test-gitsubmodules: @echo deploying gitsubmodules app... - cd tests && ./test_deploy ./apps/gitsubmodules dokku.me + cd tests && ./test_deploy ./apps/gitsubmodules $(DOKKU_DOMAIN) deploy-test-go: @echo deploying go app... - cd tests && ./test_deploy ./apps/go dokku.me + cd tests && ./test_deploy ./apps/go $(DOKKU_DOMAIN) deploy-test-java: @echo deploying java app... - cd tests && ./test_deploy ./apps/java dokku.me + cd tests && ./test_deploy ./apps/java $(DOKKU_DOMAIN) deploy-test-multi: @echo deploying multi app... - cd tests && ./test_deploy ./apps/multi dokku.me + cd tests && ./test_deploy ./apps/multi $(DOKKU_DOMAIN) deploy-test-nodejs-express: @echo deploying nodejs-express app... - cd tests && ./test_deploy ./apps/nodejs-express dokku.me + cd tests && ./test_deploy ./apps/nodejs-express $(DOKKU_DOMAIN) deploy-test-nodejs-express-noprocfile: @echo deploying nodejs-express app with no Procfile... - cd tests && ./test_deploy ./apps/nodejs-express-noprocfile dokku.me + cd tests && ./test_deploy ./apps/nodejs-express-noprocfile $(DOKKU_DOMAIN) deploy-test-nodejs-worker: @echo deploying nodejs-worker app... - cd tests && ./test_deploy ./apps/nodejs-worker dokku.me + cd tests && ./test_deploy ./apps/nodejs-worker $(DOKKU_DOMAIN) deploy-test-php: @echo deploying php app... - cd tests && ./test_deploy ./apps/php dokku.me + cd tests && ./test_deploy ./apps/php $(DOKKU_DOMAIN) deploy-test-python-flask: @echo deploying python-flask app... - cd tests && ./test_deploy ./apps/python-flask dokku.me + cd tests && ./test_deploy ./apps/python-flask $(DOKKU_DOMAIN) deploy-test-ruby: @echo deploying ruby app... - cd tests && ./test_deploy ./apps/ruby dokku.me + cd tests && ./test_deploy ./apps/ruby $(DOKKU_DOMAIN) deploy-test-scala: @echo deploying scala app... - cd tests && ./test_deploy ./apps/scala dokku.me + cd tests && ./test_deploy ./apps/scala $(DOKKU_DOMAIN) deploy-test-static: @echo deploying static app... - cd tests && ./test_deploy ./apps/static dokku.me + cd tests && ./test_deploy ./apps/static $(DOKKU_DOMAIN) deploy-tests: @echo running deploy tests... @@ -305,7 +306,7 @@ generate-ssl-tars: generate-ssl-tar generate-ssl-sans-tar generate-ssl-wildcard- generate-ssl-tar: rm -rf /tmp/dokku-server_ssl mkdir -p /tmp/dokku-server_ssl - openssl req -x509 -newkey rsa:4096 -sha256 -nodes -keyout /tmp/dokku-server_ssl/server.key -out /tmp/dokku-server_ssl/server.crt -subj "/CN=dokku.me" -days 3650 + openssl req -x509 -newkey rsa:4096 -sha256 -nodes -keyout /tmp/dokku-server_ssl/server.key -out /tmp/dokku-server_ssl/server.crt -subj "/CN=$(DOKKU_DOMAIN)" -days 3650 rm tests/unit/server_ssl.tar cd /tmp/dokku-server_ssl && tar cvf $(PWD)/tests/unit/server_ssl.tar server.key server.crt tar -tvf tests/unit/server_ssl.tar @@ -313,7 +314,7 @@ generate-ssl-tar: generate-ssl-sans-tar: rm -rf /tmp/dokku-server_ssl_sans mkdir -p /tmp/dokku-server_ssl_sans - openssl req -x509 -newkey rsa:4096 -sha256 -nodes -keyout /tmp/dokku-server_ssl_sans/server.key -out /tmp/dokku-server_ssl_sans/server.crt -subj "/CN=test.dokku.me" -days 3650 -addext "subjectAltName = DNS:www.test.dokku.me, DNS:www.test.app.dokku.me" + openssl req -x509 -newkey rsa:4096 -sha256 -nodes -keyout /tmp/dokku-server_ssl_sans/server.key -out /tmp/dokku-server_ssl_sans/server.crt -subj "/CN=test.$(DOKKU_DOMAIN)" -days 3650 -addext "subjectAltName = DNS:www.test.$(DOKKU_DOMAIN), DNS:www.test.app.$(DOKKU_DOMAIN)" rm tests/unit/server_ssl_sans.tar cd /tmp/dokku-server_ssl_sans && tar cvf $(PWD)/tests/unit/server_ssl_sans.tar server.key server.crt tar -tvf tests/unit/server_ssl_sans.tar @@ -321,7 +322,7 @@ generate-ssl-sans-tar: generate-ssl-wildcard-tar: rm -rf /tmp/dokku-server_ssl_wildcard mkdir -p /tmp/dokku-server_ssl_wildcard - openssl req -x509 -newkey rsa:4096 -sha256 -nodes -keyout /tmp/dokku-server_ssl_wildcard/server.key -out /tmp/dokku-server_ssl_wildcard/server.crt -subj "/CN=*.dokku.me" -days 3650 + openssl req -x509 -newkey rsa:4096 -sha256 -nodes -keyout /tmp/dokku-server_ssl_wildcard/server.key -out /tmp/dokku-server_ssl_wildcard/server.crt -subj "/CN=*.$(DOKKU_DOMAIN)" -days 3650 rm tests/unit/server_ssl_wildcard.tar cd /tmp/dokku-server_ssl_wildcard && tar cvf $(PWD)/tests/unit/server_ssl_wildcard.tar server.key server.crt tar -tvf tests/unit/server_ssl_wildcard.tar diff --git a/tests/unit/app-json-2.bats b/tests/unit/app-json-2.bats index d5697fb10..bacf838dc 100644 --- a/tests/unit/app-json-2.bats +++ b/tests/unit/app-json-2.bats @@ -45,7 +45,7 @@ teardown() { } @test "(app-json) app.json dockerfile entrypoint release" { - run deploy_app dockerfile-entrypoint dokku@dokku.me:$TEST_APP add_release_command + run deploy_app dockerfile-entrypoint "dokku@$DOKKU_DOMAIN:$TEST_APP" add_release_command echo "output: $output" echo "status: $status" assert_success diff --git a/tests/unit/app-json-3.bats b/tests/unit/app-json-3.bats index 837699579..efc8de7f8 100644 --- a/tests/unit/app-json-3.bats +++ b/tests/unit/app-json-3.bats @@ -12,10 +12,10 @@ teardown() { } @test "(app-json) persist scale" { - local CUSTOM_TMP=$(mktemp -d "/tmp/dokku.me.XXXXX") + local CUSTOM_TMP=$(mktemp -d "/tmp/${DOKKU_DOMAIN}.XXXXX") trap 'popd &>/dev/null || true; rm -rf "$TMP"' INT TERM - CUSTOM_TMP="$CUSTOM_TMP" run deploy_app python dokku@dokku.me:$TEST_APP persist_scale_callback_a + CUSTOM_TMP="$CUSTOM_TMP" run deploy_app python "dokku@$DOKKU_DOMAIN:$TEST_APP" persist_scale_callback_a echo "output: $output" echo "status: $status" assert_success diff --git a/tests/unit/app-json.bats b/tests/unit/app-json.bats index 693d28e3b..009009033 100644 --- a/tests/unit/app-json.bats +++ b/tests/unit/app-json.bats @@ -47,7 +47,7 @@ teardown() { } @test "(app-json) app.json scripts postdeploy" { - run deploy_app python dokku@dokku.me:$TEST_APP add_postdeploy_command + run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP add_postdeploy_command echo "output: $output" echo "status: $status" assert_success @@ -73,7 +73,7 @@ teardown() { echo "status: $status" assert_success - run deploy_app python dokku@dokku.me:$TEST_APP add_requirements_txt + run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP add_requirements_txt echo "output: $output" echo "status: $status" assert_success @@ -99,7 +99,7 @@ teardown() { echo "status: $status" assert_success - run deploy_app python dokku@dokku.me:$TEST_APP add_requirements_txt + run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP add_requirements_txt echo "output: $output" echo "status: $status" assert_success @@ -181,7 +181,7 @@ teardown() { } @test "(app-json:set)" { - run deploy_app python dokku@dokku.me:$TEST_APP copy_app_json_to_sub_app + run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP copy_app_json_to_sub_app echo "output: $output" echo "status: $status" assert_success diff --git a/tests/unit/build-env.bats b/tests/unit/build-env.bats index a3ebf54d1..c6407a4ad 100644 --- a/tests/unit/build-env.bats +++ b/tests/unit/build-env.bats @@ -70,7 +70,7 @@ teardown() { echo "status: $status" assert_success - run deploy_app python dokku@dokku.me:$TEST_APP move_dockerfile_into_place + run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP move_dockerfile_into_place echo "output: $output" echo "status: $status" assert_success diff --git a/tests/unit/builder-herokuish.bats b/tests/unit/builder-herokuish.bats index 356788e84..d462398e1 100644 --- a/tests/unit/builder-herokuish.bats +++ b/tests/unit/builder-herokuish.bats @@ -11,7 +11,7 @@ teardown() { } @test "(builder-herouish:build .env)" { - run deploy_app python dokku@dokku.me:$TEST_APP + run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP echo "output: $output" echo "status: $status" assert_success diff --git a/tests/unit/builder-lambda.bats b/tests/unit/builder-lambda.bats index fe69ae51a..b6794a1a0 100644 --- a/tests/unit/builder-lambda.bats +++ b/tests/unit/builder-lambda.bats @@ -25,7 +25,7 @@ teardown() { echo "status: $status" assert_success - run deploy_app lambda-python dokku@dokku.me:$TEST_APP inject_lambda_yml + run deploy_app lambda-python dokku@$DOKKU_DOMAIN:$TEST_APP inject_lambda_yml echo "output: $output" echo "status: $status" assert_success diff --git a/tests/unit/builder-pack.bats b/tests/unit/builder-pack.bats index 4fd6d4cff..84918aba9 100644 --- a/tests/unit/builder-pack.bats +++ b/tests/unit/builder-pack.bats @@ -30,7 +30,7 @@ teardown() { echo "status: $status" assert_success - run deploy_app python dokku@dokku.me:$TEST_APP inject_requirements_txt + run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP inject_requirements_txt echo "output: $output" echo "status: $status" assert_success diff --git a/tests/unit/builder.bats b/tests/unit/builder.bats index 9caf52f3c..8d6907fd6 100644 --- a/tests/unit/builder.bats +++ b/tests/unit/builder.bats @@ -11,7 +11,7 @@ teardown() { } @test "(builder) builder-detect [set]" { - local TMP=$(mktemp -d "/tmp/dokku.me.XXXXX") + local TMP=$(mktemp -d "/tmp/${DOKKU_DOMAIN}.XXXXX") trap 'popd &>/dev/null || true; rm -rf "$TMP"' INT TERM # test project.toml @@ -79,7 +79,7 @@ teardown() { } @test "(builder) builder-detect [pack]" { - local TMP=$(mktemp -d "/tmp/dokku.me.XXXXX") + local TMP=$(mktemp -d "/tmp/${DOKKU_DOMAIN}.XXXXX") trap 'popd &>/dev/null || true; rm -rf "$TMP"' INT TERM # test project.toml @@ -115,7 +115,7 @@ teardown() { } @test "(builder) builder-detect [dockerfile]" { - local TMP=$(mktemp -d "/tmp/dokku.me.XXXXX") + local TMP=$(mktemp -d "/tmp/${DOKKU_DOMAIN}.XXXXX") trap 'popd &>/dev/null || true; rm -rf "$TMP"' INT TERM run touch "$TMP/Dockerfile" @@ -132,7 +132,7 @@ teardown() { } @test "(builder) builder-detect [herokuish]" { - local TMP=$(mktemp -d "/tmp/dokku.me.XXXXX") + local TMP=$(mktemp -d "/tmp/${DOKKU_DOMAIN}.XXXXX") trap 'popd &>/dev/null || true; rm -rf "$TMP"' INT TERM touch "$TMP/Dockerfile" diff --git a/tests/unit/buildpacks.bats b/tests/unit/buildpacks.bats index cb0064380..acef76307 100644 --- a/tests/unit/buildpacks.bats +++ b/tests/unit/buildpacks.bats @@ -298,7 +298,7 @@ teardown() { } @test "(buildpacks) cleanup existing .buildpacks file" { - run deploy_app python dokku@dokku.me:$TEST_APP template_buildpacks_cleanup + run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP template_buildpacks_cleanup echo "output: $output" echo "status: $status" assert_success diff --git a/tests/unit/caddy.bats b/tests/unit/caddy.bats index f105d2347..76a7c370e 100644 --- a/tests/unit/caddy.bats +++ b/tests/unit/caddy.bats @@ -38,7 +38,7 @@ teardown() { echo "status: $status" assert_success - run deploy_app python dokku@dokku.me:$TEST_APP convert_to_dockerfile + run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP convert_to_dockerfile echo "output: $output" echo "status: $status" assert_success @@ -56,28 +56,28 @@ teardown() { echo "status: $status" assert_success - run /bin/bash -c "dokku domains:add $TEST_APP $TEST_APP.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP $TEST_APP.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "dokku domains:add $TEST_APP $TEST_APP-2.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP $TEST_APP-2.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success - run deploy_app python dokku@dokku.me:$TEST_APP convert_to_dockerfile + run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP convert_to_dockerfile echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "curl --silent $TEST_APP.dokku.me" + run /bin/bash -c "curl --silent $TEST_APP.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success assert_output "python/http.server" - run /bin/bash -c "curl --silent $TEST_APP-2.dokku.me" + run /bin/bash -c "curl --silent $TEST_APP-2.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success @@ -104,7 +104,7 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - assert_output "$TEST_APP.dokku.me:80" + assert_output "$TEST_APP.${DOKKU_DOMAIN}:80" run /bin/bash -c "dokku caddy:set --global letsencrypt-email test@example.com" echo "output: $output" @@ -135,7 +135,7 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - assert_output "$TEST_APP.dokku.me" + assert_output "$TEST_APP.${DOKKU_DOMAIN}" run /bin/bash -c "dokku --quiet ports:report $TEST_APP --ports-map" echo "output: $output" diff --git a/tests/unit/checks.bats b/tests/unit/checks.bats index 826e0cebf..d91bb21a9 100644 --- a/tests/unit/checks.bats +++ b/tests/unit/checks.bats @@ -262,7 +262,7 @@ teardown() { echo "status: $status" assert_success - run deploy_app nodejs-express dokku@dokku.me:$TEST_APP template_checks_file + run deploy_app nodejs-express dokku@$DOKKU_DOMAIN:$TEST_APP template_checks_file echo "output: $output" echo "status: $status" assert_output_contains "/healthcheck" 2 diff --git a/tests/unit/client.bats b/tests/unit/client.bats index 9b2c3b3ef..bfb51a64d 100644 --- a/tests/unit/client.bats +++ b/tests/unit/client.bats @@ -4,7 +4,7 @@ load test_helper setup() { global_setup - export DOKKU_HOST=dokku.me + export "DOKKU_HOST=${DOKKU_DOMAIN}" create_app } @@ -134,29 +134,29 @@ teardown() { } @test "(client) domains:add" { - run /bin/bash -c "${BATS_TEST_DIRNAME}/../../contrib/dokku_client.sh domains:add $TEST_APP www.test.app.dokku.me" + run /bin/bash -c "${BATS_TEST_DIRNAME}/../../contrib/dokku_client.sh domains:add $TEST_APP www.test.app.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "${BATS_TEST_DIRNAME}/../../contrib/dokku_client.sh domains:add $TEST_APP test.app.dokku.me" + run /bin/bash -c "${BATS_TEST_DIRNAME}/../../contrib/dokku_client.sh domains:add $TEST_APP test.app.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success } @test "(client) domains:remove" { - run /bin/bash -c "${BATS_TEST_DIRNAME}/../../contrib/dokku_client.sh domains:add $TEST_APP test.app.dokku.me" + run /bin/bash -c "${BATS_TEST_DIRNAME}/../../contrib/dokku_client.sh domains:add $TEST_APP test.app.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "${BATS_TEST_DIRNAME}/../../contrib/dokku_client.sh domains:remove $TEST_APP test.app.dokku.me" + run /bin/bash -c "${BATS_TEST_DIRNAME}/../../contrib/dokku_client.sh domains:remove $TEST_APP test.app.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" - refute_line "test.app.dokku.me" + refute_line "test.app.${DOKKU_DOMAIN}" } @test "(client) domains:clear" { - run /bin/bash -c "${BATS_TEST_DIRNAME}/../../contrib/dokku_client.sh domains:add $TEST_APP test.app.dokku.me" + run /bin/bash -c "${BATS_TEST_DIRNAME}/../../contrib/dokku_client.sh domains:add $TEST_APP test.app.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success @@ -273,7 +273,7 @@ teardown() { assert_success assert_output "dokku" - run /bin/bash -c "${BATS_TEST_DIRNAME}/../../contrib/dokku_client.sh remote:add dokku2 dokku@dokku.me:dokku2" + run /bin/bash -c "${BATS_TEST_DIRNAME}/../../contrib/dokku_client.sh remote:add dokku2 dokku@${DOKKU_DOMAIN}:dokku2" echo "output: $output" echo "status: $status" assert_success diff --git a/tests/unit/config.bats b/tests/unit/config.bats index 58425609b..d8d8d1808 100644 --- a/tests/unit/config.bats +++ b/tests/unit/config.bats @@ -33,14 +33,14 @@ teardown() { } @test "(config) config:set --global" { - run ssh dokku@dokku.me config:set --global test_var=true test_var2=\"hello world\" test_var3='double\"quotes' + run ssh "dokku@$DOKKU_DOMAIN" config:set --global test_var=true test_var2=\"hello world\" test_var3='double\"quotes' echo "output: $output" echo "status: $status" assert_success } @test "(config) config:get --global" { - run ssh dokku@dokku.me config:set --global test_var=true test_var2=\"hello world\" test_var3=\"with\\nnewline\" test_var4='double\"quotes' + run ssh "dokku@$DOKKU_DOMAIN" config:set --global test_var=true test_var2=\"hello world\" test_var3=\"with\\nnewline\" test_var4='double\"quotes' echo "output: $output" echo "status: $status" assert_success @@ -59,7 +59,7 @@ teardown() { } @test "(config) config:unset --global" { - run ssh dokku@dokku.me config:set --global test_var=true test_var2=\"hello world\" + run ssh "dokku@$DOKKU_DOMAIN" config:set --global test_var=true test_var2=\"hello world\" echo "output: $output" echo "status: $status" assert_success @@ -78,7 +78,7 @@ teardown() { } @test "(config) config:set/get" { - run ssh dokku@dokku.me config:set $TEST_APP test_var1=true test_var2=\"hello world\" test_var3='double\"quotes' + run ssh "dokku@$DOKKU_DOMAIN" config:set $TEST_APP test_var1=true test_var2=\"hello world\" test_var3='double\"quotes' echo "output: $output" echo "status: $status" assert_success @@ -125,7 +125,7 @@ teardown() { } @test "(config) config:clear" { - run ssh dokku@dokku.me config:set $TEST_APP test_var=true test_var2=\"hello world\" test_var3=\"with\\nnewline\" + run ssh "dokku@$DOKKU_DOMAIN" config:set $TEST_APP test_var=true test_var2=\"hello world\" test_var3=\"with\\nnewline\" echo "output: $output" echo "status: $status" assert_success @@ -140,7 +140,7 @@ teardown() { } @test "(config) config:unset" { - run ssh dokku@dokku.me config:set $TEST_APP test_var=true test_var2=\"hello world\" test_var3=\"with\\nnewline\" + run ssh "dokku@$DOKKU_DOMAIN" config:set $TEST_APP test_var=true test_var2=\"hello world\" test_var3=\"with\\nnewline\" echo "output: $output" echo "status: $status" assert_success diff --git a/tests/unit/core_2.bats b/tests/unit/core_2.bats index bc1d1ab34..26cf2708c 100644 --- a/tests/unit/core_2.bats +++ b/tests/unit/core_2.bats @@ -38,18 +38,18 @@ teardown() { } @test "(core) urls (non-ssl)" { - assert_urls "http://${TEST_APP}.dokku.me" + assert_urls "http://${TEST_APP}.${DOKKU_DOMAIN}" - run /bin/bash -c "dokku domains:add $TEST_APP test.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP test.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success - assert_urls "http://${TEST_APP}.dokku.me" "http://test.dokku.me" + assert_urls "http://${TEST_APP}.${DOKKU_DOMAIN}" "http://test.${DOKKU_DOMAIN}" } @test "(core) urls (app ssl)" { - assert_urls "http://${TEST_APP}.dokku.me" + assert_urls "http://${TEST_APP}.${DOKKU_DOMAIN}" run setup_test_tls echo "output: $output" @@ -61,14 +61,14 @@ teardown() { echo "status: $status" assert_success - assert_urls "https://${TEST_APP}.dokku.me" + assert_urls "https://${TEST_APP}.${DOKKU_DOMAIN}" - run /bin/bash -c "dokku domains:add $TEST_APP test.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP test.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success - assert_urls "http://${TEST_APP}.dokku.me" "https://${TEST_APP}.dokku.me" "https://test.dokku.me" "http://test.dokku.me" + assert_urls "http://${TEST_APP}.${DOKKU_DOMAIN}" "https://${TEST_APP}.${DOKKU_DOMAIN}" "https://test.${DOKKU_DOMAIN}" "http://test.${DOKKU_DOMAIN}" } @test "(core) url (app ssl)" { @@ -82,7 +82,7 @@ teardown() { echo "status: $status" assert_success - assert_url "https://${TEST_APP}.dokku.me" + assert_url "https://${TEST_APP}.${DOKKU_DOMAIN}" } @test "(core) urls (wildcard ssl)" { @@ -96,20 +96,20 @@ teardown() { echo "status: $status" assert_success - assert_urls "https://${TEST_APP}.dokku.me" + assert_urls "https://${TEST_APP}.${DOKKU_DOMAIN}" - run /bin/bash -c "dokku domains:add $TEST_APP test.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP test.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success - assert_urls "http://${TEST_APP}.dokku.me" "https://${TEST_APP}.dokku.me" "https://test.dokku.me" "http://test.dokku.me" + assert_urls "http://${TEST_APP}.${DOKKU_DOMAIN}" "https://${TEST_APP}.${DOKKU_DOMAIN}" "https://test.${DOKKU_DOMAIN}" "http://test.${DOKKU_DOMAIN}" run /bin/bash -c "dokku domains:add $TEST_APP dokku.example.com" echo "output: $output" echo "status: $status" assert_success - assert_urls "http://dokku.example.com" "http://${TEST_APP}.dokku.me" "https://dokku.example.com" "https://${TEST_APP}.dokku.me" "https://test.dokku.me" "http://test.dokku.me" + assert_urls "http://dokku.example.com" "http://${TEST_APP}.${DOKKU_DOMAIN}" "https://dokku.example.com" "https://${TEST_APP}.${DOKKU_DOMAIN}" "https://test.${DOKKU_DOMAIN}" "http://test.${DOKKU_DOMAIN}" } @test "(core) git-remote (off-port)" { diff --git a/tests/unit/cron.bats b/tests/unit/cron.bats index a7a2e409c..8247e0252 100644 --- a/tests/unit/cron.bats +++ b/tests/unit/cron.bats @@ -45,28 +45,28 @@ teardown() { } @test "(cron) invalid [missing-keys]" { - run deploy_app python dokku@dokku.me:$TEST_APP template_cron_file_invalid + run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP template_cron_file_invalid echo "output: $output" echo "status: $status" assert_failure } @test "(cron) invalid [schedule]" { - run deploy_app python dokku@dokku.me:$TEST_APP template_cron_file_invalid_schedule + run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP template_cron_file_invalid_schedule echo "output: $output" echo "status: $status" assert_failure } @test "(cron) invalid [seconds]" { - run deploy_app python dokku@dokku.me:$TEST_APP template_cron_file_invalid_schedule_seconds + run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP template_cron_file_invalid_schedule_seconds echo "output: $output" echo "status: $status" assert_failure } @test "(cron) create [empty]" { - run deploy_app python dokku@dokku.me:$TEST_APP + run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP echo "output: $output" echo "status: $status" assert_success @@ -78,7 +78,7 @@ teardown() { } @test "(cron) create [single-verbose]" { - run deploy_app python dokku@dokku.me:$TEST_APP template_cron_file_valid_single + run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP template_cron_file_valid_single echo "output: $output" echo "status: $status" assert_success @@ -91,7 +91,7 @@ teardown() { } @test "(cron) create [single-short]" { - run deploy_app python dokku@dokku.me:$TEST_APP template_cron_file_valid_short + run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP template_cron_file_valid_short echo "output: $output" echo "status: $status" assert_success @@ -104,7 +104,7 @@ teardown() { } @test "(cron) create [multiple]" { - run deploy_app python dokku@dokku.me:$TEST_APP template_cron_file_valid_multiple + run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP template_cron_file_valid_multiple echo "output: $output" echo "status: $status" assert_success @@ -178,7 +178,7 @@ teardown() { } @test "(cron) cron:list --format json" { - run deploy_app python dokku@dokku.me:$TEST_APP template_cron_file_valid + run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP template_cron_file_valid echo "output: $output" echo "status: $status" assert_success @@ -198,7 +198,7 @@ teardown() { } @test "(cron) cron:run" { - run deploy_app python dokku@dokku.me:$TEST_APP template_cron_file_valid + run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP template_cron_file_valid echo "output: $output" echo "status: $status" assert_success diff --git a/tests/unit/docker-options.bats b/tests/unit/docker-options.bats index 911f57125..8ea8cf61f 100644 --- a/tests/unit/docker-options.bats +++ b/tests/unit/docker-options.bats @@ -333,7 +333,7 @@ teardown() { } @test "(docker-options) docker-options:add (all phases over SSH)" { - run ssh dokku@dokku.me docker-options:add $TEST_APP build,deploy,run "-v /tmp" + run ssh "dokku@$DOKKU_DOMAIN" docker-options:add $TEST_APP build,deploy,run "-v /tmp" echo "output: $output" echo "status: $status" assert_success diff --git a/tests/unit/domains.bats b/tests/unit/domains.bats index 6a56164ec..24f0571d5 100644 --- a/tests/unit/domains.bats +++ b/tests/unit/domains.bats @@ -29,19 +29,19 @@ teardown() { } @test "(domains) domains" { - run /bin/bash -c "dokku domains:report $TEST_APP 2>/dev/null | grep ${TEST_APP}.dokku.me" + run /bin/bash -c "dokku domains:report $TEST_APP 2>/dev/null | grep ${TEST_APP}.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" - assert_output_contains "${TEST_APP}.dokku.me" + assert_output_contains "${TEST_APP}.${DOKKU_DOMAIN}" } @test "(domains) domains:add" { - run /bin/bash -c "dokku domains:add $TEST_APP www.test.app.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP www.test.app.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "dokku domains:add $TEST_APP 2.app.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP 2.app.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success @@ -51,7 +51,7 @@ teardown() { echo "status: $status" assert_success - run /bin/bash -c "dokku domains:add $TEST_APP .dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP .${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success @@ -65,14 +65,14 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - assert_output_contains www.test.app.dokku.me - assert_output_contains 2.app.dokku.me + assert_output_contains "www.test.app.${DOKKU_DOMAIN}" + assert_output_contains "2.app.${DOKKU_DOMAIN}" assert_output_contains a--domain.with--hyphens assert_output_contains _ } @test "(domains) domains:add (multiple)" { - run /bin/bash -c "dokku domains:add $TEST_APP www.test.app.dokku.me 2.app.dokku.me a--domain.with--hyphens" + run /bin/bash -c "dokku domains:add $TEST_APP www.test.app.${DOKKU_DOMAIN} 2.app.${DOKKU_DOMAIN} a--domain.with--hyphens" echo "output: $output" echo "status: $status" assert_success @@ -81,37 +81,37 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - assert_output_contains www.test.app.dokku.me - assert_output_contains 2.app.dokku.me + assert_output_contains "www.test.app.${DOKKU_DOMAIN}" + assert_output_contains "2.app.${DOKKU_DOMAIN}" assert_output_contains a--domain.with--hyphens } @test "(domains) domains:add (duplicate)" { - run /bin/bash -c "dokku domains:add $TEST_APP test.app.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP test.app.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "dokku domains:add $TEST_APP test.app.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP test.app.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success } @test "(domains) domains:add (invalid)" { - run /bin/bash -c "dokku domains:add $TEST_APP http://test.app.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP http://test.app.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_failure } @test "(domains) domains:remove" { - run /bin/bash -c "dokku domains:add $TEST_APP test.app.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP test.app.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "dokku domains:remove $TEST_APP test.app.dokku.me" + run /bin/bash -c "dokku domains:remove $TEST_APP test.app.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success @@ -120,16 +120,16 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - assert_output_contains test.app.dokku.me 0 + assert_output_contains "test.app.${DOKKU_DOMAIN}" 0 } @test "(domains) domains:remove (multiple)" { - run /bin/bash -c "dokku domains:add $TEST_APP www.test.app.dokku.me test.app.dokku.me 2.app.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP www.test.app.${DOKKU_DOMAIN} test.app.${DOKKU_DOMAIN} 2.app.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "dokku domains:remove $TEST_APP www.test.app.dokku.me test.app.dokku.me" + run /bin/bash -c "dokku domains:remove $TEST_APP www.test.app.${DOKKU_DOMAIN} test.app.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success @@ -138,13 +138,13 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - assert_output_contains www.test.app.dokku.me 0 - assert_output_contains test.app.dokku.me 0 - assert_output_contains 2.app.dokku.me + assert_output_contains "www.test.app.${DOKKU_DOMAIN}" 0 + assert_output_contains "test.app.${DOKKU_DOMAIN}" 0 + assert_output_contains "2.app.${DOKKU_DOMAIN}" } @test "(domains) domains:remove (wildcard domain)" { - run /bin/bash -c "dokku domains:add $TEST_APP '*.dokku.me'" + run /bin/bash -c "dokku domains:add $TEST_APP '*.${DOKKU_DOMAIN}'" echo "output: $output" echo "status: $status" assert_success @@ -153,9 +153,9 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - assert_output "$TEST_APP.dokku.me *.dokku.me" + assert_output "$TEST_APP.${DOKKU_DOMAIN} *.${DOKKU_DOMAIN}" - run /bin/bash -c "dokku domains:remove $TEST_APP '*.dokku.me'" + run /bin/bash -c "dokku domains:remove $TEST_APP '*.${DOKKU_DOMAIN}'" echo "output: $output" echo "status: $status" assert_success @@ -164,16 +164,16 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - assert_output "$TEST_APP.dokku.me" + assert_output "$TEST_APP.${DOKKU_DOMAIN}" } @test "(domains) domains:set" { - run /bin/bash -c "dokku domains:add $TEST_APP www.test.app.dokku.me test.app.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP www.test.app.${DOKKU_DOMAIN} test.app.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "dokku domains:set $TEST_APP 2.app.dokku.me a--domain.with--hyphens" + run /bin/bash -c "dokku domains:set $TEST_APP 2.app.${DOKKU_DOMAIN} a--domain.with--hyphens" echo "output: $output" echo "status: $status" assert_success @@ -182,14 +182,14 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - assert_output_contains www.test.app.dokku.me 0 - assert_output_contains test.app.dokku.me 0 - assert_output_contains 2.app.dokku.me + assert_output_contains "www.test.app.${DOKKU_DOMAIN}" 0 + assert_output_contains "test.app.${DOKKU_DOMAIN}" 0 + assert_output_contains "2.app.${DOKKU_DOMAIN}" assert_output_contains a--domain.with--hyphens } @test "(domains) domains:clear" { - run /bin/bash -c "dokku domains:add $TEST_APP test.app.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP test.app.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success @@ -208,11 +208,11 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - assert_output_contains test.app.dokku.me 0 + assert_output_contains "test.app.${DOKKU_DOMAIN}" 0 } @test "(domains) domains:add-global" { - run /bin/bash -c "dokku domains:add-global global.dokku.me" + run /bin/bash -c "dokku domains:add-global global.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success @@ -222,29 +222,29 @@ teardown() { echo "status: $status" assert_success - run /bin/bash -c "dokku domains:report 2>/dev/null | grep -qw 'global.dokku.me'" + run /bin/bash -c "dokku domains:report 2>/dev/null | grep -qw 'global.${DOKKU_DOMAIN}'" echo "output: $output" echo "status: $status" assert_success } @test "(domains) domains:add-global (multiple)" { - run /bin/bash -c "dokku domains:add-global global1.dokku.me global2.dokku.me global3.dokku.me" + run /bin/bash -c "dokku domains:add-global global1.${DOKKU_DOMAIN} global2.${DOKKU_DOMAIN} global3.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "dokku domains:report 2>/dev/null | grep -q global1.dokku.me" + run /bin/bash -c "dokku domains:report 2>/dev/null | grep -q global1.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "dokku domains:report 2>/dev/null | grep -q global2.dokku.me" + run /bin/bash -c "dokku domains:report 2>/dev/null | grep -q global2.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "dokku domains:report 2>/dev/null | grep -q global3.dokku.me" + run /bin/bash -c "dokku domains:report 2>/dev/null | grep -q global3.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success @@ -256,7 +256,7 @@ teardown() { echo "status: $status" assert_success - run /bin/bash -c "dokku domains:add-global global.dokku.me" + run /bin/bash -c "dokku domains:add-global global.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success @@ -266,23 +266,23 @@ teardown() { echo "status: $status" assert_success refute_line "global.dokku.invalid" - refute_line "global.dokku.me" + refute_line "global.${DOKKU_DOMAIN}" } @test "(domains) domains:remove-global" { - run /bin/bash -c "dokku domains:add-global global.dokku.me" + run /bin/bash -c "dokku domains:add-global global.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "dokku domains:remove-global global.dokku.me" + run /bin/bash -c "dokku domains:remove-global global.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" - refute_line "global.dokku.me" + refute_line "global.${DOKKU_DOMAIN}" } @test "(domains) domains (multiple global domains)" { - run /bin/bash -c "dokku domains:add-global global1.dokku.me global2.dokku.me" + run /bin/bash -c "dokku domains:add-global global1.${DOKKU_DOMAIN} global2.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success @@ -301,17 +301,17 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - assert_output_contains ${TEST_APP}.global1.dokku.me - assert_output_contains ${TEST_APP}.global2.dokku.me + assert_output_contains "${TEST_APP}.global1.${DOKKU_DOMAIN}" + assert_output_contains "${TEST_APP}.global2.${DOKKU_DOMAIN}" } @test "(domains) domains:set-global" { - run /bin/bash -c "dokku domains:add-global global1.dokku.me global2.dokku.me" + run /bin/bash -c "dokku domains:add-global global1.${DOKKU_DOMAIN} global2.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "dokku domains:set-global global3.dokku.me global4.dokku.me" + run /bin/bash -c "dokku domains:set-global global3.${DOKKU_DOMAIN} global4.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success @@ -330,10 +330,10 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - assert_output_contains ${TEST_APP}.global1.dokku.me 0 - assert_output_contains ${TEST_APP}.global2.dokku.me 0 - assert_output_contains ${TEST_APP}.global3.dokku.me - assert_output_contains ${TEST_APP}.global4.dokku.me + assert_output_contains "${TEST_APP}.global1.${DOKKU_DOMAIN}" 0 + assert_output_contains "${TEST_APP}.global2.${DOKKU_DOMAIN}" 0 + assert_output_contains "${TEST_APP}.global3.${DOKKU_DOMAIN}" + assert_output_contains "${TEST_APP}.global4.${DOKKU_DOMAIN}" } @test "(domains) app name overlaps with global domain.tld" { @@ -366,12 +366,12 @@ teardown() { } @test "(domains) app rename only renames domains associated with global domains" { - run /bin/bash -c "dokku domains:set-global dokku.me" + run /bin/bash -c "dokku domains:set-global ${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "dokku domains:set $TEST_APP $TEST_APP.dokku.me $TEST_APP.dokku.test" + run /bin/bash -c "dokku domains:set $TEST_APP $TEST_APP.${DOKKU_DOMAIN} $TEST_APP.dokku.test" echo "output: $output" echo "status: $status" assert_success @@ -385,7 +385,7 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - assert_output "other-name.dokku.me $TEST_APP.dokku.test" + assert_output "other-name.${DOKKU_DOMAIN} $TEST_APP.dokku.test" run /bin/bash -c "dokku apps:rename other-name $TEST_APP" echo "output: $output" diff --git a/tests/unit/git_4.bats b/tests/unit/git_4.bats index 601df2b64..2d29aec68 100644 --- a/tests/unit/git_4.bats +++ b/tests/unit/git_4.bats @@ -83,7 +83,7 @@ teardown() { } @test "(git) git:from-image [failing deploy]" { - local CUSTOM_TMP=$(mktemp -d "/tmp/dokku.me.XXXXX") + local CUSTOM_TMP=$(mktemp -d "/tmp/${DOKKU_DOMAIN}.XXXXX") trap 'popd &>/dev/null || true; rm -rf "$CUSTOM_TMP"' INT TERM rmdir "$CUSTOM_TMP" && cp -r "${BATS_TEST_DIRNAME}/../../tests/apps/python" "$CUSTOM_TMP" @@ -157,7 +157,7 @@ teardown() { } @test "(git) git:from-image [onbuild]" { - local TMP=$(mktemp -d "/tmp/dokku.me.XXXXX") + local TMP=$(mktemp -d "/tmp/${DOKKU_DOMAIN}.XXXXX") trap 'popd &>/dev/null || true; rm -rf "$TMP"' INT TERM run /bin/bash -c "dokku storage:mount $TEST_APP /var/run/docker.sock:/var/run/docker.sock" diff --git a/tests/unit/git_5.bats b/tests/unit/git_5.bats index 016eab882..be7ef97bd 100644 --- a/tests/unit/git_5.bats +++ b/tests/unit/git_5.bats @@ -34,7 +34,7 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - assert_http_success http://${TEST_APP}.dokku.me + assert_http_success "http://${TEST_APP}.${DOKKU_DOMAIN}" } @test "(git) git:from-archive [tar.gz]" { @@ -42,7 +42,7 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - assert_http_success http://${TEST_APP}.dokku.me + assert_http_success "http://${TEST_APP}.${DOKKU_DOMAIN}" } @test "(git) git:from-archive [zip]" { @@ -50,7 +50,7 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - assert_http_success http://${TEST_APP}.dokku.me + assert_http_success "http://${TEST_APP}.${DOKKU_DOMAIN}" } @test "(git) git:from-archive [stdin-file]" { @@ -63,7 +63,7 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - assert_http_success http://${TEST_APP}.dokku.me + assert_http_success "http://${TEST_APP}.${DOKKU_DOMAIN}" } @test "(git) git:from-archive [stdin-curl]" { @@ -71,5 +71,5 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - assert_http_success http://${TEST_APP}.dokku.me + assert_http_success "http://${TEST_APP}.${DOKKU_DOMAIN}" } diff --git a/tests/unit/git_7.bats b/tests/unit/git_7.bats index b26fe0729..03c81f264 100644 --- a/tests/unit/git_7.bats +++ b/tests/unit/git_7.bats @@ -103,7 +103,7 @@ teardown() { } @test "(git) git:load-image [failing deploy]" { - local CUSTOM_TMP=$(mktemp -d "/tmp/dokku.me.XXXXX") + local CUSTOM_TMP=$(mktemp -d "/tmp/${DOKKU_DOMAIN}.XXXXX") trap 'popd &>/dev/null || true; rm -rf "$CUSTOM_TMP"' INT TERM rmdir "$CUSTOM_TMP" && cp -r "${BATS_TEST_DIRNAME}/../../tests/apps/python" "$CUSTOM_TMP" @@ -187,7 +187,7 @@ teardown() { } @test "(git) git:load-image [onbuild]" { - local TMP=$(mktemp -d "/tmp/dokku.me.XXXXX") + local TMP=$(mktemp -d "/tmp/${DOKKU_DOMAIN}.XXXXX") trap 'popd &>/dev/null || true; rm -rf "$TMP"' INT TERM run /bin/bash -c "dokku storage:mount $TEST_APP /var/run/docker.sock:/var/run/docker.sock" diff --git a/tests/unit/haproxy.bats b/tests/unit/haproxy.bats index 0486a6e50..31c781730 100644 --- a/tests/unit/haproxy.bats +++ b/tests/unit/haproxy.bats @@ -55,7 +55,7 @@ teardown() { echo "status: $status" assert_success - run deploy_app python dokku@dokku.me:$TEST_APP convert_to_dockerfile + run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP convert_to_dockerfile echo "output: $output" echo "status: $status" assert_success @@ -73,28 +73,28 @@ teardown() { echo "status: $status" assert_success - run /bin/bash -c "dokku domains:add $TEST_APP $TEST_APP.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP $TEST_APP.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "dokku domains:add $TEST_APP $TEST_APP-2.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP $TEST_APP-2.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success - run deploy_app python dokku@dokku.me:$TEST_APP convert_to_dockerfile + run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP convert_to_dockerfile echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "curl --silent $TEST_APP.dokku.me" + run /bin/bash -c "curl --silent $TEST_APP.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success assert_output "python/http.server" - run /bin/bash -c "curl --silent $TEST_APP-2.dokku.me" + run /bin/bash -c "curl --silent $TEST_APP-2.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success diff --git a/tests/unit/logs.bats b/tests/unit/logs.bats index b8048b7ba..6d3624114 100644 --- a/tests/unit/logs.bats +++ b/tests/unit/logs.bats @@ -425,7 +425,7 @@ teardown() { fi driver="$(jq -r '."log-driver"' /etc/docker/daemon.json)" - local TMP_FILE=$(mktemp "/tmp/dokku.me.XXXX") + local TMP_FILE=$(mktemp "/tmp/${DOKKU_DOMAIN}.XXXX") run create_app echo "output: $output" diff --git a/tests/unit/network.bats b/tests/unit/network.bats index c1ed797f6..db2831c65 100644 --- a/tests/unit/network.bats +++ b/tests/unit/network.bats @@ -36,7 +36,7 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - assert_nonssl_domain "${TEST_APP}.dokku.me" + assert_nonssl_domain "${TEST_APP}.${DOKKU_DOMAIN}" run /bin/bash -c "dokku network:set $TEST_APP bind-all-interfaces true" echo "output: $output" @@ -47,7 +47,7 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - assert_http_success "${TEST_APP}.dokku.me" + assert_http_success "${TEST_APP}.${DOKKU_DOMAIN}" for CID_FILE in $DOKKU_ROOT/$TEST_APP/CONTAINER.web.*; do assert_external_port $(<$CID_FILE) @@ -62,7 +62,7 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - assert_http_success "${TEST_APP}.dokku.me" + assert_http_success "${TEST_APP}.${DOKKU_DOMAIN}" for CID_FILE in $DOKKU_ROOT/$TEST_APP/CONTAINER.web.*; do assert_not_external_port $(<$CID_FILE) @@ -193,7 +193,7 @@ teardown() { echo "output: $output" echo "status: $status" assert_failure - assert_http_success "${TEST_APP}.dokku.me" + assert_http_success "${TEST_APP}.${DOKKU_DOMAIN}" run /bin/bash -c "dokku network:set $TEST_APP attach-post-create create-network" echo "output: $output" @@ -219,7 +219,7 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - assert_http_success "${TEST_APP}.dokku.me" + assert_http_success "${TEST_APP}.${DOKKU_DOMAIN}" run /bin/bash -c "dokku network:set $TEST_APP attach-post-create create-network create-network-2" echo "output: $output" @@ -235,7 +235,7 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - assert_http_success "${TEST_APP}.dokku.me" + assert_http_success "${TEST_APP}.${DOKKU_DOMAIN}" run /bin/bash -c "dokku --force network:destroy create-network" echo "output: $output" diff --git a/tests/unit/nginx-vhosts_1.bats b/tests/unit/nginx-vhosts_1.bats index 3862ea237..265a84202 100644 --- a/tests/unit/nginx-vhosts_1.bats +++ b/tests/unit/nginx-vhosts_1.bats @@ -51,8 +51,8 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - check_urls http://${TEST_APP}.dokku.me - assert_http_success http://${TEST_APP}.dokku.me + check_urls "http://${TEST_APP}.${DOKKU_DOMAIN}" + assert_http_success "http://${TEST_APP}.${DOKKU_DOMAIN}" } @test "(nginx-vhosts) proxy:build-config (domains:add pre deploy)" { @@ -61,7 +61,7 @@ teardown() { echo "status: $status" assert_success - run /bin/bash -c "dokku domains:add $TEST_APP www.test.app.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP www.test.app.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success @@ -72,17 +72,17 @@ teardown() { assert_success sleep 5 # wait for nginx to reload - check_urls http://www.test.app.dokku.me - assert_http_success http://www.test.app.dokku.me + check_urls "http://www.test.app.${DOKKU_DOMAIN}" + assert_http_success "http://www.test.app.${DOKKU_DOMAIN}" } @test "(nginx-vhosts) proxy:build-config (with global VHOST)" { - echo "dokku.me" >"$DOKKU_ROOT/VHOST" + echo "${DOKKU_DOMAIN}" >"$DOKKU_ROOT/VHOST" run deploy_app echo "output: $output" echo "status: $status" assert_success - check_urls http://${TEST_APP}.dokku.me - assert_http_success http://${TEST_APP}.dokku.me + check_urls "http://${TEST_APP}.${DOKKU_DOMAIN}" + assert_http_success "http://${TEST_APP}.${DOKKU_DOMAIN}" } diff --git a/tests/unit/nginx-vhosts_11.bats b/tests/unit/nginx-vhosts_11.bats index 82b797f41..0bc9d11e7 100644 --- a/tests/unit/nginx-vhosts_11.bats +++ b/tests/unit/nginx-vhosts_11.bats @@ -163,7 +163,7 @@ teardown() { } @test "(nginx-vhosts) nginx:set nginx.conf.sigil" { - local TMP=$(mktemp -d "/tmp/dokku.me.XXXXX") + local TMP=$(mktemp -d "/tmp/${DOKKU_DOMAIN}.XXXXX") trap 'popd &>/dev/null || true; rm -rf "$TMP"' INT TERM export CUSTOM_TMP="$TMP" diff --git a/tests/unit/nginx-vhosts_12.bats b/tests/unit/nginx-vhosts_12.bats index 81949d1f1..95b6af069 100644 --- a/tests/unit/nginx-vhosts_12.bats +++ b/tests/unit/nginx-vhosts_12.bats @@ -15,7 +15,7 @@ teardown() { } @test "(nginx-vhosts) git:from-image nginx.conf.sigil" { - local CUSTOM_TMP=$(mktemp -d "/tmp/dokku.me.XXXXX") + local CUSTOM_TMP=$(mktemp -d "/tmp/${DOKKU_DOMAIN}.XXXXX") trap 'popd &>/dev/null || true; rm -rf "$CUSTOM_TMP"' INT TERM rmdir "$CUSTOM_TMP" && cp -r "${BATS_TEST_DIRNAME}/../../tests/apps/python" "$CUSTOM_TMP" diff --git a/tests/unit/nginx-vhosts_2.bats b/tests/unit/nginx-vhosts_2.bats index f16c992bc..441ef1ed2 100644 --- a/tests/unit/nginx-vhosts_2.bats +++ b/tests/unit/nginx-vhosts_2.bats @@ -16,12 +16,12 @@ teardown() { @test "(nginx-vhosts) proxy:build-config (with SSL and unrelated domain)" { setup_test_tls - run /bin/bash -c "dokku domains:add $TEST_APP node-js-app.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP node-js-app.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "dokku domains:add $TEST_APP test.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP test.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success @@ -36,18 +36,18 @@ teardown() { echo "status: $status" assert_success - assert_ssl_domain "node-js-app.dokku.me" - assert_http_redirect "http://test.dokku.me" "https://test.dokku.me:443/" + assert_ssl_domain "node-js-app.${DOKKU_DOMAIN}" + assert_http_redirect "http://test.${DOKKU_DOMAIN}" "https://test.${DOKKU_DOMAIN}:443/" } @test "(nginx-vhosts) proxy:build-config (wildcard SSL)" { setup_test_tls wildcard - run /bin/bash -c "dokku domains:add $TEST_APP wildcard1.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP wildcard1.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "dokku domains:add $TEST_APP wildcard2.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP wildcard2.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success @@ -62,8 +62,8 @@ teardown() { echo "status: $status" assert_success - assert_ssl_domain "wildcard1.dokku.me" - assert_ssl_domain "wildcard2.dokku.me" + assert_ssl_domain "wildcard1.${DOKKU_DOMAIN}" + assert_ssl_domain "wildcard2.${DOKKU_DOMAIN}" } @test "(nginx-vhosts) proxy:build-config (wildcard SSL and unrelated domain) 1" { @@ -80,7 +80,7 @@ teardown() { setup_test_tls wildcard - run deploy_app nodejs-express dokku@dokku.me:$TEST_APP + run deploy_app nodejs-express dokku@$DOKKU_DOMAIN:$TEST_APP echo "output: $output" echo "status: $status" assert_success @@ -90,7 +90,7 @@ teardown() { echo "status: $status" assert_success - run /bin/bash -c "dokku nginx:show-config $TEST_APP | grep -e '*.dokku.me' | wc -l" + run /bin/bash -c "dokku nginx:show-config $TEST_APP | grep -e '*.${DOKKU_DOMAIN}' | wc -l" echo "output: $output" echo "status: $status" assert_success @@ -99,17 +99,17 @@ teardown() { @test "(nginx-vhosts) proxy:build-config (with SSL and Multiple SANs)" { setup_test_tls sans - run /bin/bash -c "dokku domains:add $TEST_APP test.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP test.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "dokku domains:add $TEST_APP www.test.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP www.test.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "dokku domains:add $TEST_APP www.test.app.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP www.test.app.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success @@ -118,7 +118,7 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - assert_ssl_domain "test.dokku.me" - assert_ssl_domain "www.test.dokku.me" - assert_ssl_domain "www.test.app.dokku.me" + assert_ssl_domain "test.${DOKKU_DOMAIN}" + assert_ssl_domain "www.test.${DOKKU_DOMAIN}" + assert_ssl_domain "www.test.app.${DOKKU_DOMAIN}" } diff --git a/tests/unit/nginx-vhosts_3.bats b/tests/unit/nginx-vhosts_3.bats index 71ade8df4..46669928d 100644 --- a/tests/unit/nginx-vhosts_3.bats +++ b/tests/unit/nginx-vhosts_3.bats @@ -32,7 +32,7 @@ teardown() { echo "status: $status" assert_success - run /bin/bash -c "docker run --rm ${TEST_APP}-docker-image /go/bin/greeter_client -address ${TEST_APP}.dokku.me:80 -name grpc" + run /bin/bash -c "docker run --rm ${TEST_APP}-docker-image /go/bin/greeter_client -address ${TEST_APP}.${DOKKU_DOMAIN}:80 -name grpc" echo "output: $output" echo "status: $status" assert_success @@ -50,7 +50,7 @@ teardown() { echo "status: $status" assert_success - run /bin/bash -c "docker run --rm ${TEST_APP}-docker-image /go/bin/greeter_client -address ${TEST_APP}.dokku.me:8080 -name grpc8080" + run /bin/bash -c "docker run --rm ${TEST_APP}-docker-image /go/bin/greeter_client -address ${TEST_APP}.${DOKKU_DOMAIN}:8080 -name grpc8080" echo "output: $output" echo "status: $status" assert_success @@ -69,7 +69,7 @@ teardown() { echo "status: $status" assert_success - run /bin/bash -c "docker run --rm ${TEST_APP}-docker-image /go/bin/greeter_client -address ${TEST_APP}.dokku.me:443 -name grpcs -tls" + run /bin/bash -c "docker run --rm ${TEST_APP}-docker-image /go/bin/greeter_client -address ${TEST_APP}.${DOKKU_DOMAIN}:443 -name grpcs -tls" echo "output: $output" echo "status: $status" assert_success diff --git a/tests/unit/nginx-vhosts_4.bats b/tests/unit/nginx-vhosts_4.bats index 757942b2b..d9a281179 100644 --- a/tests/unit/nginx-vhosts_4.bats +++ b/tests/unit/nginx-vhosts_4.bats @@ -76,7 +76,7 @@ teardown() { echo "status: $status" assert_success - run /bin/bash -c "dokku domains:add $TEST_APP www.test.app.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP www.test.app.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success @@ -85,7 +85,7 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - assert_nonssl_domain "www.test.app.dokku.me" + assert_nonssl_domain "www.test.app.${DOKKU_DOMAIN}" } @test "(nginx-vhosts) proxy:build-config (without global VHOST and domains:add post deploy)" { @@ -95,11 +95,11 @@ teardown() { echo "status: $status" assert_success - run /bin/bash -c "dokku domains:add $TEST_APP www.test.app.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP www.test.app.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success - check_urls http://www.test.app.dokku.me - assert_http_success http://www.test.app.dokku.me + check_urls "http://www.test.app.${DOKKU_DOMAIN}" + assert_http_success "http://www.test.app.${DOKKU_DOMAIN}" } diff --git a/tests/unit/nginx-vhosts_5.bats b/tests/unit/nginx-vhosts_5.bats index f162ef21f..bd0fdf763 100644 --- a/tests/unit/nginx-vhosts_5.bats +++ b/tests/unit/nginx-vhosts_5.bats @@ -16,14 +16,14 @@ teardown() { } @test "(nginx-vhosts) proxy:build-config (sslip.io style hostnames)" { - echo "127.0.0.1.sslip.io.dokku.me" >"$DOKKU_ROOT/VHOST" + echo "127.0.0.1.sslip.io.${DOKKU_DOMAIN}" >"$DOKKU_ROOT/VHOST" run deploy_app echo "output: $output" echo "status: $status" assert_success - check_urls http://${TEST_APP}.127.0.0.1.sslip.io.dokku.me - assert_http_success http://${TEST_APP}.127.0.0.1.sslip.io.dokku.me + check_urls "http://${TEST_APP}.127.0.0.1.sslip.io.${DOKKU_DOMAIN}" + assert_http_success "http://${TEST_APP}.127.0.0.1.sslip.io.${DOKKU_DOMAIN}" } @test "(nginx-vhosts) proxy:build-config (dockerfile expose)" { @@ -32,19 +32,19 @@ teardown() { echo "status: $status" assert_success - run /bin/bash -c "dokku domains:add $TEST_APP www.test.app.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP www.test.app.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success - check_urls http://${TEST_APP}.dokku.me:3000 - check_urls http://${TEST_APP}.dokku.me:3003 - check_urls http://www.test.app.dokku.me:3000 - check_urls http://www.test.app.dokku.me:3003 - assert_http_localhost_success "http" "${TEST_APP}.dokku.me" "3000" - assert_http_localhost_success "http" "${TEST_APP}.dokku.me" "3003" - assert_http_localhost_success "http" "www.test.app.dokku.me" "3000" - assert_http_localhost_success "http" "www.test.app.dokku.me" "3003" + check_urls "http://${TEST_APP}.${DOKKU_DOMAIN}:3000" + check_urls "http://${TEST_APP}.${DOKKU_DOMAIN}:3003" + check_urls "http://www.test.app.${DOKKU_DOMAIN}:3000" + check_urls "http://www.test.app.${DOKKU_DOMAIN}:3003" + assert_http_localhost_success "http" "${TEST_APP}.${DOKKU_DOMAIN}" "3000" + assert_http_localhost_success "http" "${TEST_APP}.${DOKKU_DOMAIN}" "3003" + assert_http_localhost_success "http" "www.test.app.${DOKKU_DOMAIN}" "3000" + assert_http_localhost_success "http" "www.test.app.${DOKKU_DOMAIN}" "3003" } @test "(nginx-vhosts) proxy:build-config (multiple networks)" { @@ -71,8 +71,8 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - check_urls http://${TEST_APP}.dokku.me:${GLOBAL_PORT} - assert_http_success http://${TEST_APP}.dokku.me:${GLOBAL_PORT} + check_urls "http://${TEST_APP}.${DOKKU_DOMAIN}:${GLOBAL_PORT}" + assert_http_success "http://${TEST_APP}.${DOKKU_DOMAIN}:${GLOBAL_PORT}" run /bin/bash -c "dokku config:unset --global DOKKU_PROXY_PORT" echo "output: $output" diff --git a/tests/unit/nginx-vhosts_7.bats b/tests/unit/nginx-vhosts_7.bats index a7ab2af78..2b35c1f6c 100644 --- a/tests/unit/nginx-vhosts_7.bats +++ b/tests/unit/nginx-vhosts_7.bats @@ -16,34 +16,34 @@ teardown() { @test "(nginx-vhosts) proxy:build-config (wildcard SSL and custom nginx template)" { setup_test_tls wildcard - run /bin/bash -c "dokku domains:add $TEST_APP wildcard1.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP wildcard1.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "dokku domains:add $TEST_APP wildcard2.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP wildcard2.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success - run deploy_app nodejs-express dokku@dokku.me:$TEST_APP custom_ssl_nginx_template + run deploy_app nodejs-express dokku@$DOKKU_DOMAIN:$TEST_APP custom_ssl_nginx_template echo "output: $output" echo "status: $status" assert_success - assert_ssl_domain "wildcard1.dokku.me" - assert_ssl_domain "wildcard2.dokku.me" + assert_ssl_domain "wildcard1.${DOKKU_DOMAIN}" + assert_ssl_domain "wildcard2.${DOKKU_DOMAIN}" assert_http_redirect "http://${CUSTOM_TEMPLATE_SSL_DOMAIN}" "https://${CUSTOM_TEMPLATE_SSL_DOMAIN}:443/" assert_http_success "https://${CUSTOM_TEMPLATE_SSL_DOMAIN}" } @test "(nginx-vhosts) proxy:build-config (custom nginx template - no ssl)" { - run /bin/bash -c "dokku domains:add $TEST_APP www.test.app.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP www.test.app.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success - run deploy_app python dokku@dokku.me:$TEST_APP custom_nginx_template + run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP custom_nginx_template echo "output: $output" echo "status: $status" assert_success @@ -53,8 +53,8 @@ teardown() { echo "status: $status" assert_success - assert_nonssl_domain "www.test.app.dokku.me" - assert_http_localhost_success "http" "customtemplate.dokku.me" + assert_nonssl_domain "www.test.app.${DOKKU_DOMAIN}" + assert_http_localhost_success "http" "customtemplate.${DOKKU_DOMAIN}" run /bin/bash -c "dokku nginx:show-config $TEST_APP" echo "output: $output" @@ -68,12 +68,12 @@ teardown() { echo "status: $status" assert_success - run /bin/bash -c "dokku domains:add $TEST_APP www.test.app.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP www.test.app.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success - run deploy_app python dokku@dokku.me:$TEST_APP custom_nginx_template + run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP custom_nginx_template echo "output: $output" echo "status: $status" assert_success @@ -83,7 +83,7 @@ teardown() { echo "status: $status" assert_success - assert_nonssl_domain "www.test.app.dokku.me" + assert_nonssl_domain "www.test.app.${DOKKU_DOMAIN}" run /bin/bash -c "dokku nginx:show-config $TEST_APP" echo "output: $output" @@ -92,7 +92,7 @@ teardown() { } @test "(nginx-vhosts) proxy:build-config (failed validate_nginx)" { - run deploy_app nodejs-express dokku@dokku.me:$TEST_APP bad_custom_nginx_template + run deploy_app nodejs-express dokku@$DOKKU_DOMAIN:$TEST_APP bad_custom_nginx_template echo "output: $output" echo "status: $status" assert_failure diff --git a/tests/unit/nginx-vhosts_9.bats b/tests/unit/nginx-vhosts_9.bats index 44420ca47..143238aee 100644 --- a/tests/unit/nginx-vhosts_9.bats +++ b/tests/unit/nginx-vhosts_9.bats @@ -163,12 +163,12 @@ teardown() { @test "(nginx-vhosts) proxy:build-config (with SSL and unrelated domain)" { setup_test_tls - run /bin/bash -c "dokku domains:add $TEST_APP node-js-app.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP node-js-app.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "dokku domains:add $TEST_APP test.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP test.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success @@ -183,18 +183,18 @@ teardown() { echo "status: $status" assert_success - assert_ssl_domain "node-js-app.dokku.me" - assert_http_redirect "http://test.dokku.me" "https://test.dokku.me:443/" + assert_ssl_domain "node-js-app.${DOKKU_DOMAIN}" + assert_http_redirect "http://test.${DOKKU_DOMAIN}" "https://test.${DOKKU_DOMAIN}:443/" } @test "(nginx-vhosts) proxy:build-config (wildcard SSL)" { setup_test_tls wildcard - run /bin/bash -c "dokku domains:add $TEST_APP wildcard1.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP wildcard1.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "dokku domains:add $TEST_APP wildcard2.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP wildcard2.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success @@ -209,6 +209,6 @@ teardown() { echo "status: $status" assert_success - assert_ssl_domain "wildcard1.dokku.me" - assert_ssl_domain "wildcard2.dokku.me" + assert_ssl_domain "wildcard1.${DOKKU_DOMAIN}" + assert_ssl_domain "wildcard2.${DOKKU_DOMAIN}" } diff --git a/tests/unit/ports.bats b/tests/unit/ports.bats index 2dbb30772..62674ea07 100644 --- a/tests/unit/ports.bats +++ b/tests/unit/ports.bats @@ -110,8 +110,8 @@ teardown() { for URL in $URLS; do assert_http_success $URL done - assert_http_success "http://$TEST_APP.dokku.me:8080" - assert_http_success "http://$TEST_APP.dokku.me:8081" + assert_http_success "http://$TEST_APP.${DOKKU_DOMAIN}:8080" + assert_http_success "http://$TEST_APP.${DOKKU_DOMAIN}:8081" } @test "(ports:report) herokuish tls" { @@ -152,7 +152,7 @@ teardown() { } @test "(ports:report) dockerfile tls" { - run deploy_app python dokku@dokku.me:$TEST_APP move_expose_dockerfile_into_place + run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP move_expose_dockerfile_into_place echo "output: $output" echo "status: $status" assert_success diff --git a/tests/unit/proxied-app.bats b/tests/unit/proxied-app.bats index 3d4ed4548..92181b2b1 100644 --- a/tests/unit/proxied-app.bats +++ b/tests/unit/proxied-app.bats @@ -34,7 +34,7 @@ teardown() { echo "status: $status" assert_success - run /bin/bash -c "dokku domains:set $TEST_APP $TEST_APP.dokku.me" + run /bin/bash -c "dokku domains:set $TEST_APP $TEST_APP.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success diff --git a/tests/unit/proxy.bats b/tests/unit/proxy.bats index 6cc149efd..45ae78c48 100644 --- a/tests/unit/proxy.bats +++ b/tests/unit/proxy.bats @@ -96,7 +96,7 @@ teardown() { echo "status: $status" assert_success - assert_nonssl_domain "${TEST_APP}.dokku.me" + assert_nonssl_domain "${TEST_APP}.${DOKKU_DOMAIN}" run /bin/bash -c "dokku proxy:disable $TEST_APP" echo "output: $output" @@ -111,7 +111,7 @@ teardown() { echo "output: $output" echo "status: $status" assert_success - assert_http_success "${TEST_APP}.dokku.me" + assert_http_success "${TEST_APP}.${DOKKU_DOMAIN}" for CID_FILE in $DOKKU_ROOT/$TEST_APP/CONTAINER.web.*; do assert_not_external_port $(<$CID_FILE) diff --git a/tests/unit/ps-cnb.bats b/tests/unit/ps-cnb.bats index 5abbbb1bd..61dc10ba8 100644 --- a/tests/unit/ps-cnb.bats +++ b/tests/unit/ps-cnb.bats @@ -22,7 +22,7 @@ teardown() { echo "status: $status" assert_success - run deploy_app python dokku@dokku.me:$TEST_APP add_requirements_txt + run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP add_requirements_txt echo "output: $output" echo "status: $status" assert_success diff --git a/tests/unit/ps-general-1.bats b/tests/unit/ps-general-1.bats index 9cab2caef..94de64bc6 100644 --- a/tests/unit/ps-general-1.bats +++ b/tests/unit/ps-general-1.bats @@ -63,7 +63,7 @@ EOF } @test "(ps:scale) update formations from Procfile" { - local TMP=$(mktemp -d "/tmp/dokku.me.XXXXX") + local TMP=$(mktemp -d "/tmp/${DOKKU_DOMAIN}.XXXXX") trap 'popd &>/dev/null || true; rm -rf "$TMP"' INT TERM CUSTOM_TMP="$TMP" deploy_app nodejs-express diff --git a/tests/unit/run_1.bats b/tests/unit/run_1.bats index a2a9aeea8..1ab4ee1dd 100644 --- a/tests/unit/run_1.bats +++ b/tests/unit/run_1.bats @@ -46,7 +46,7 @@ teardown() { echo "status: $status" assert_success - run deploy_app python dokku@dokku.me:$TEST_APP add_requirements_txt + run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP add_requirements_txt echo "output: $output" echo "status: $status" assert_success @@ -73,7 +73,7 @@ teardown() { echo "status: $status" assert_success - run deploy_app python dokku@dokku.me:$TEST_APP add_requirements_txt + run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP add_requirements_txt echo "output: $output" echo "status: $status" assert_success diff --git a/tests/unit/scheduler-docker-local.bats b/tests/unit/scheduler-docker-local.bats index 8bce5e89c..ba3199a3b 100644 --- a/tests/unit/scheduler-docker-local.bats +++ b/tests/unit/scheduler-docker-local.bats @@ -38,8 +38,8 @@ teardown() { assert_success # the run command is equivalent to the following line, except with backslashes due to the enclosing doublequotes - # dokku docker-options:add test deploy '--label "some.key=Host(\`$TEST_APP.dokku.me\`)"' - run /bin/bash -c "dokku docker-options:add $TEST_APP deploy '--label \"some.key=Host(\\\`$TEST_APP.dokku.me\\\`)\"'" + # dokku docker-options:add test deploy '--label "some.key=Host(\`$TEST_APP.$DOKKU_DOMAIN\`)"' + run /bin/bash -c "dokku docker-options:add $TEST_APP deploy '--label \"some.key=Host(\\\`$TEST_APP.$DOKKU_DOMAIN\\\`)\"'" echo "output: $output" echo "status: $status" assert_success @@ -52,7 +52,7 @@ teardown() { run /bin/bash -c "docker inspect --format '{{ index .Config.Labels \"some.key\"}}' $TEST_APP.web.1" echo "output: $output" echo "status: $status" - assert_output "Host(\`$TEST_APP.dokku.me\`)" + assert_output "Host(\`$TEST_APP.$DOKKU_DOMAIN\`)" assert_success } diff --git a/tests/unit/test_helper.bash b/tests/unit/test_helper.bash index 9aea3bc32..c37496fe8 100644 --- a/tests/unit/test_helper.bash +++ b/tests/unit/test_helper.bash @@ -4,12 +4,13 @@ DOKKU_ROOT=${DOKKU_ROOT:=~dokku} DOCKER_BIN=${DOCKER_BIN:="docker"} DOKKU_LIB_ROOT=${DOKKU_LIB_PATH:="/var/lib/dokku"} +DOKKU_DOMAIN=${DOKKU_DOMAIN:="dokku.me"} PLUGIN_PATH=${PLUGIN_PATH:="$DOKKU_LIB_ROOT/plugins"} PLUGIN_AVAILABLE_PATH=${PLUGIN_AVAILABLE_PATH:="$PLUGIN_PATH/available"} PLUGIN_ENABLED_PATH=${PLUGIN_ENABLED_PATH:="$PLUGIN_PATH/enabled"} PLUGIN_CORE_PATH=${PLUGIN_CORE_PATH:="$DOKKU_LIB_ROOT/core-plugins"} PLUGIN_CORE_AVAILABLE_PATH=${PLUGIN_CORE_AVAILABLE_PATH:="$PLUGIN_CORE_PATH/available"} -CUSTOM_TEMPLATE_SSL_DOMAIN=customssltemplate.dokku.me +CUSTOM_TEMPLATE_SSL_DOMAIN="customssltemplate.${DOKKU_DOMAIN}" UUID=$(uuidgen) TEST_APP="rdmtestapp-${UUID}" TEST_NETWORK="test-network-${UUID}" @@ -309,9 +310,9 @@ assert_urls() { deploy_app() { declare APP_TYPE="$1" GIT_REMOTE="$2" CUSTOM_TEMPLATE="$3" CUSTOM_PATH="$4" local APP_TYPE=${APP_TYPE:="python"} - local GIT_REMOTE=${GIT_REMOTE:="dokku@dokku.me:$TEST_APP"} + local GIT_REMOTE=${GIT_REMOTE:="dokku@${DOKKU_DOMAIN}:$TEST_APP"} local GIT_REMOTE_BRANCH=${GIT_REMOTE_BRANCH:="master"} - local TMP=${CUSTOM_TMP:=$(mktemp -d "/tmp/dokku.me.XXXXX")} + local TMP=${CUSTOM_TMP:=$(mktemp -d "/tmp/${DOKKU_DOMAIN}.XXXXX")} rmdir "$TMP" && cp -r "${BATS_TEST_DIRNAME}/../../tests/apps/$APP_TYPE" "$TMP" @@ -335,7 +336,7 @@ deploy_app() { setup_client_repo() { local TMP - TMP=$(mktemp -d "/tmp/dokku.me.XXXXX") + TMP=$(mktemp -d "/tmp/${DOKKU_DOMAIN}.XXXXX") rmdir "$TMP" && cp -r "${BATS_TEST_DIRNAME}/../../tests/apps/nodejs-express" "$TMP" cd "$TMP" || exit 1 git init @@ -451,7 +452,7 @@ custom_nginx_template() { server { listen [::]:{{ \$listen_port }}; listen {{ \$listen_port }}; - server_name {{ $.NOSSL_SERVER_NAME }} customtemplate.dokku.me; + server_name {{ $.NOSSL_SERVER_NAME }} customtemplate.${DOKKU_DOMAIN}; location / { proxy_pass http://{{ $.APP }}-{{ \$upstream_port }}; diff --git a/tests/unit/traefik.bats b/tests/unit/traefik.bats index 3f7de41b5..0ed0ec274 100644 --- a/tests/unit/traefik.bats +++ b/tests/unit/traefik.bats @@ -39,7 +39,7 @@ teardown() { echo "status: $status" assert_success - run deploy_app python dokku@dokku.me:$TEST_APP convert_to_dockerfile + run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP convert_to_dockerfile echo "output: $output" echo "status: $status" assert_success @@ -57,28 +57,28 @@ teardown() { echo "status: $status" assert_success - run /bin/bash -c "dokku domains:add $TEST_APP $TEST_APP.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP $TEST_APP.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "dokku domains:add $TEST_APP $TEST_APP-2.dokku.me" + run /bin/bash -c "dokku domains:add $TEST_APP $TEST_APP-2.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success - run deploy_app python dokku@dokku.me:$TEST_APP convert_to_dockerfile + run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP convert_to_dockerfile echo "output: $output" echo "status: $status" assert_success - run /bin/bash -c "curl --silent $TEST_APP.dokku.me" + run /bin/bash -c "curl --silent $TEST_APP.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success assert_output "python/http.server" - run /bin/bash -c "curl --silent $TEST_APP-2.dokku.me" + run /bin/bash -c "curl --silent $TEST_APP-2.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" assert_success @@ -136,7 +136,7 @@ teardown() { run /bin/bash -c "docker inspect traefik-traefik-1 --format '{{ index .Config.Labels \"traefik.http.routers.api.rule\" }}'" echo "output: $output" echo "status: $status" - assert_output "Host(\`traefik.dokku.me\`)" + assert_output "Host(\`traefik.$DOKKU_DOMAIN\`)" assert_success } From 625ea14c8ff5c6f72aa82d06a7fedfa15e8c1164 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Tue, 1 Aug 2023 02:16:11 -0400 Subject: [PATCH 53/61] feat: add openresty proxy implementation This plugin is mostly compatible with the nginx plugin, but runs the proxy within a docker container. Users do not have direct access to add custom openresty configuration at this time, but instead receive the ability to setup automatic ssl on first request via letsencrypt integration. --- docs/development/plugin-triggers.md | 15 ++ docs/networking/proxies/openresty.md | 164 +++++++++++++ docs/template.html | 1 + dokku | 3 + plugins/20_events/certs-force | 1 + plugins/caddy-vhosts/certs-force | 21 ++ plugins/common/functions | 57 +++++ plugins/common/subprocess.go | 6 + plugins/haproxy-vhosts/certs-force | 21 ++ .../nginx-vhosts/docker-args-process-deploy | 79 ------ plugins/openresty-vhosts/certs-force | 21 ++ plugins/openresty-vhosts/command-functions | 172 +++++++++++++ plugins/openresty-vhosts/commands | 15 ++ plugins/openresty-vhosts/core-post-deploy | 24 ++ plugins/openresty-vhosts/core-post-extract | 55 +++++ .../docker-args-process-deploy | 153 ++++++++++++ plugins/openresty-vhosts/help-functions | 36 +++ plugins/openresty-vhosts/install | 17 ++ plugins/openresty-vhosts/internal-functions | 230 ++++++++++++++++++ plugins/openresty-vhosts/plugin.toml | 4 + plugins/openresty-vhosts/pre-restore | 20 ++ plugins/openresty-vhosts/report | 6 + plugins/openresty-vhosts/subcommands/default | 6 + plugins/openresty-vhosts/subcommands/logs | 6 + plugins/openresty-vhosts/subcommands/report | 6 + plugins/openresty-vhosts/subcommands/set | 37 +++ .../openresty-vhosts/subcommands/show-config | 7 + plugins/openresty-vhosts/subcommands/start | 6 + plugins/openresty-vhosts/subcommands/stop | 6 + .../templates/compose.yml.sigil | 27 ++ plugins/ports/functions.go | 10 +- plugins/traefik-vhosts/certs-force | 21 ++ tests/unit/caddy.bats | 2 +- tests/unit/haproxy.bats | 2 +- tests/unit/openresty.bats | 189 ++++++++++++++ tests/unit/test_helper.bash | 9 +- tests/unit/traefik.bats | 2 +- 37 files changed, 1371 insertions(+), 86 deletions(-) create mode 100644 docs/networking/proxies/openresty.md create mode 120000 plugins/20_events/certs-force create mode 100755 plugins/caddy-vhosts/certs-force create mode 100755 plugins/haproxy-vhosts/certs-force delete mode 100755 plugins/nginx-vhosts/docker-args-process-deploy create mode 100755 plugins/openresty-vhosts/certs-force create mode 100755 plugins/openresty-vhosts/command-functions create mode 100755 plugins/openresty-vhosts/commands create mode 100755 plugins/openresty-vhosts/core-post-deploy create mode 100755 plugins/openresty-vhosts/core-post-extract create mode 100755 plugins/openresty-vhosts/docker-args-process-deploy create mode 100755 plugins/openresty-vhosts/help-functions create mode 100755 plugins/openresty-vhosts/install create mode 100755 plugins/openresty-vhosts/internal-functions create mode 100644 plugins/openresty-vhosts/plugin.toml create mode 100755 plugins/openresty-vhosts/pre-restore create mode 100755 plugins/openresty-vhosts/report create mode 100755 plugins/openresty-vhosts/subcommands/default create mode 100755 plugins/openresty-vhosts/subcommands/logs create mode 100755 plugins/openresty-vhosts/subcommands/report create mode 100755 plugins/openresty-vhosts/subcommands/set create mode 100755 plugins/openresty-vhosts/subcommands/show-config create mode 100755 plugins/openresty-vhosts/subcommands/start create mode 100755 plugins/openresty-vhosts/subcommands/stop create mode 100644 plugins/openresty-vhosts/templates/compose.yml.sigil create mode 100755 plugins/traefik-vhosts/certs-force create mode 100644 tests/unit/openresty.bats diff --git a/docs/development/plugin-triggers.md b/docs/development/plugin-triggers.md index f326282ca..8c4ad2461 100644 --- a/docs/development/plugin-triggers.md +++ b/docs/development/plugin-triggers.md @@ -297,6 +297,21 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x # TODO ``` +### `certs-force` + +- Description: Echos `true` if a cert should be simulated for the app, no output otherwise +- Invoked by: +- Arguments: `$APP` +- Example: + +```shell +#!/usr/bin/env bash + +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x + +# TODO +``` + ### `check-deploy` - Description: Allows you to run checks on a deploy before Dokku allows the container to handle requests. diff --git a/docs/networking/proxies/openresty.md b/docs/networking/proxies/openresty.md new file mode 100644 index 000000000..65299f462 --- /dev/null +++ b/docs/networking/proxies/openresty.md @@ -0,0 +1,164 @@ +# OpenResty Proxy + +> New as of 0.31.0 + +Dokku can provide integration with the [OpenResty](https://openresty.org/) proxy service by utilizing the Docker label-based integration implemented by [openresty-docker-proxy](https://github.com/dokku/openresty-docker-proxy). + +``` +openresty:report [] [] # Displays a openresty report for one or more apps +openresty:logs [--num num] [--tail] # Display openresty log output +openresty:set () # Set or clear an openresty property for an app +openresty:show-config # Display openresty compose config +openresty:start # Starts the openresty server +openresty:stop # Stops the openresty server +``` + +## Requirements + +Using the `openresty` plugin integration requires the `docker-compose-plugin` for Docker. See [this document](https://docs.docker.com/compose/install/) from the Docker documentation for more information on the installation process for the `docker-compose-plugin`. + +## Usage + +> Warning: As using multiple proxy plugins on a single Dokku installation can lead to issues routing requests to apps, doing so should be avoided. As the default proxy implementation is nginx, users are encouraged to stop the nginx service before switching to OpenResty. + +The OpenResty plugin has specific rules for routing requests: + +- OpenResty integration is exposed via docker labels attached to containers. Changes in labels require either app deploys or rebuilds. +- While OpenResty will respect labels associated with other containers, only `web` containers have OpenResty labels injected by the plugin. +- Only `http:80` and `https:443` port mappings are supported at this time. +- Requests are routed as soon as the container is running and passing healthchecks. + +### Switching to OpenResty + +To use the OpenResty plugin, use the `proxy:set` command for the app in question: + +```shell +dokku proxy:set node-js-app openresty +``` + +This will enable the docker label-based OpenResty integration. All future deploys will inject the correct labels for OpenResty to read and route requests to containers. Due to the docker label-based integration used by OpenResty, a single deploy or rebuild will be required before requests will route successfully. + +```shell +dokku ps:rebuild node-js-app +``` + +Any changes to domains or port mappings will also require either a deploy or rebuild. + +### Starting OpenResty container + +OpenResty can be started via the `openresty:start` command. This will start a OpenResty container via the `docker compose up` command. + +```shell +dokku openresty:start +``` + +### Stopping the OpenResty container + +OpenResty may be stopped via the `openresty:stop` command. + +```shell +dokku openresty:stop +``` + +The OpenResty container will be stopped and removed from the system. If the container is not running, this command will do nothing. + +### Showing the OpenResty compose config + +For debugging purposes, it may be useful to show the OpenResty compose config. This can be achieved via the `openresty:show-config` command. + +```shell +dokku openresty:show-config +``` + +### Customizing the OpenResty container image + +While the default OpenResty image is hardcoded, users may specify an alternative by setting the `image` property with the `--global` flag: + +```shell +dokku openresty:set --global image dokku/openresty-docker-proxy:0.5.6 +``` + +#### Checking the OpenResty container's logs + +It may be necessary to check the OpenResty container's logs to ensure that OpenResty is operating as expected. This can be performed with the `openresty:logs` command. + +```shell +dokku openresty:logs +``` + +This command also supports the following modifiers: + +```shell +--num NUM # the number of lines to display +--tail # continually stream logs +``` + +You can use these modifiers as follows: + +```shell +dokku openresty:logs --tail --num 10 +``` + +The above command will show logs continually from the openresty container, with an initial history of 10 log lines + +### SSL Configuration + +The OpenResty plugin only supports automatic ssl certificates from it's letsencrypt integration. Managed certificates provided by the `certs` plugin are ignored. + +#### Enabling letsencrypt integration + +By default, letsencrypt is disabled and https port mappings are ignored. To enable, set the `letsencrypt-email` property with the `--global` flag: + +```shell +dokku openresty:set --global letsencrypt-email automated@dokku.sh +``` + +After enabling, the OpenResty container will need to be restarted and apps will need to be rebuilt. All http requests will then be redirected to https. + +#### Customizing the letsencrypt server + +The letsencrypt integration is set to the production letsencrypt server by default. To change this, set the `letsencrypt-server` property with the `--global` flag: + +```shell +dokku openresty:set --global letsencrypt-server https://acme-staging-v02.api.letsencrypt.org/directory +``` + +After enabling, the OpenResty container will need to be restarted and apps will need to be rebuilt to retrieve certificates from the new server. + +## Displaying OpenResty reports for an app + +You can get a report about the app's OpenResty config using the `openresty:report` command: + +```shell +dokku openresty:report +``` + +``` +=====> node-js-app openresty information + Openresty image: dokku/openresty-docker-proxy:0.5.6 + Openresty letsencrypt email: automated@dokku.sh +=====> python-app openresty information + Openresty image: dokku/openresty-docker-proxy:0.5.6 + Openresty letsencrypt email: automated@dokku.sh +=====> ruby-app openresty information + Openresty image: dokku/openresty-docker-proxy:0.5.6 + Openresty letsencrypt email: automated@dokku.sh +``` + +You can run the command for a specific app also. + +```shell +dokku openresty:report node-js-app +``` + +``` +=====> node-js-app openresty information + Openresty image: dokku/openresty-docker-proxy:0.5.6 + Openresty letsencrypt email: automated@dokku.sh +``` + +You can pass flags which will output only the value of the specific information you want. For example: + +```shell +dokku openresty:report node-js-app --openresty-letsencrypt-email +``` diff --git a/docs/template.html b/docs/template.html index ce49b3a88..1832e64ae 100644 --- a/docs/template.html +++ b/docs/template.html @@ -178,6 +178,7 @@ Caddy Proxy Haproxy Proxy Nginx Proxy + Openresty Proxy Traefik Proxy Advanced Usage diff --git a/dokku b/dokku index 0a2086471..ce657b8a1 100755 --- a/dokku +++ b/dokku @@ -143,6 +143,9 @@ execute_dokku_cmd() { nginx | nginx:*) local PLUGIN_NAME=${PLUGIN_NAME/nginx/nginx-vhosts} ;; + openresty | openresty:*) + local PLUGIN_NAME=${PLUGIN_NAME/openresty/openresty-vhosts} + ;; traefik | traefik:*) local PLUGIN_NAME=${PLUGIN_NAME/traefik/traefik-vhosts} ;; diff --git a/plugins/20_events/certs-force b/plugins/20_events/certs-force new file mode 120000 index 000000000..5178a749f --- /dev/null +++ b/plugins/20_events/certs-force @@ -0,0 +1 @@ +hook \ No newline at end of file diff --git a/plugins/caddy-vhosts/certs-force b/plugins/caddy-vhosts/certs-force new file mode 100755 index 000000000..a01fd5d5a --- /dev/null +++ b/plugins/caddy-vhosts/certs-force @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +source "$PLUGIN_AVAILABLE_PATH/caddy-vhosts/internal-functions" + +trigger-caddy-vhosts-certs-force() { + declare desc="caddy-vhosts certs-force plugin trigger" + declare trigger="certs-force" + declare APP="$1" + + if [[ "$(plugn trigger proxy-type "$APP")" != "caddy" ]]; then + return + fi + + if [[ -n "$(fn-caddy-letsencrypt-email)" ]]; then + echo true + fi +} + +trigger-caddy-vhosts-certs-force "$@" diff --git a/plugins/common/functions b/plugins/common/functions index adf5066ae..f9a07f502 100755 --- a/plugins/common/functions +++ b/plugins/common/functions @@ -506,6 +506,63 @@ copy_from_image() { fi } +copy_dir_from_image() { + declare desc="copy a directory from named image to destination" + declare IMAGE="$1" SRC_DIR="$2" DST_DIR="$3" + local WORK_DIR="" + + local DOCKER_CREATE_LABEL_ARGS="--label=com.dokku.app-name=$APP" + + if verify_image "$IMAGE"; then + if ! is_abs_path "$SRC_DIR"; then + if is_image_cnb_based "$IMAGE"; then + WORKDIR="/workspace" + elif is_image_herokuish_based "$IMAGE" "$APP"; then + WORKDIR="/app" + else + WORKDIR="$("$DOCKER_BIN" image inspect --format '{{.Config.WorkingDir}}' "$IMAGE")" + fi + + if [[ -n "$WORKDIR" ]]; then + SRC_DIR="${WORKDIR}/${SRC_DIR}" + fi + fi + + TMP_DIR_COMMAND_OUTPUT=$(mktemp -d "/tmp/dokku-${DOKKU_PID}-${FUNCNAME[0]}.XXXXXX") + trap "rm -rf '$TMP_DIR_COMMAND_OUTPUT' &>/dev/null || true" RETURN + + local CID=$("$DOCKER_BIN" container create "${DOCKER_CREATE_LABEL_ARGS[@]}" $DOKKU_GLOBAL_RUN_ARGS "$IMAGE") + "$DOCKER_BIN" container cp "$CID:$SRC_DIR" "$TMP_DIR_COMMAND_OUTPUT" 2>/dev/null || true + "$DOCKER_BIN" container rm --force "$CID" &>/dev/null + plugn trigger scheduler-register-retired "$APP" "$CID" + + # docker cp exits with status 1 when run as non-root user when it tries to chown the file + # after successfully copying the file. Thus, we suppress stderr. + # ref: https://github.com/dotcloud/docker/issues/3986 + if [[ ! -d "$TMP_DIR_COMMAND_OUTPUT" ]]; then + return 1 + fi + + pushd "$TMP_DIR_COMMAND_OUTPUT" >/dev/null + for filename in *; do + if [[ ! -f "$TMP_DIR_COMMAND_OUTPUT/$filename" ]]; then + continue + fi + + # workaround for CHECKS file when owner is root. seems to only happen when running inside docker + dos2unix -l <"$TMP_DIR_COMMAND_OUTPUT/$filename" >"$DST_DIR/$filename" + + # add trailing newline for certain places where file parsing depends on it + if [[ "$(tail -c1 "$DST_DIR/$filename")" != "" ]]; then + echo "" >>"$DST_DIR/$filename" + fi + done + popd &>/dev/null || pushd "/tmp" >/dev/null + else + return 1 + fi +} + get_app_container_ids() { declare desc="returns list of docker container ids for given app and optional container_type" local APP="$1" diff --git a/plugins/common/subprocess.go b/plugins/common/subprocess.go index 19b02e81e..b428b9da8 100644 --- a/plugins/common/subprocess.go +++ b/plugins/common/subprocess.go @@ -124,6 +124,12 @@ func PlugnTriggerOutput(triggerName string, args ...string) ([]byte, error) { return readStdout, err } +// PlugnTriggerOutputAsString fires the given plugn trigger with the given args and returns the string contents instead of bytes +func PlugnTriggerOutputAsString(triggerName string, args ...string) (string, error) { + b, err := PlugnTriggerOutput(triggerName, args...) + return strings.TrimSpace(string(b[:])), err +} + // PlugnTriggerSetup sets up a plugn trigger call func PlugnTriggerSetup(triggerName string, args ...string) *sh.Session { shellArgs := make([]interface{}, len(args)+2) diff --git a/plugins/haproxy-vhosts/certs-force b/plugins/haproxy-vhosts/certs-force new file mode 100755 index 000000000..69bab2c83 --- /dev/null +++ b/plugins/haproxy-vhosts/certs-force @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +source "$PLUGIN_AVAILABLE_PATH/haproxy-vhosts/internal-functions" + +trigger-haproxy-vhosts-certs-force() { + declare desc="haproxy-vhosts certs-force plugin trigger" + declare trigger="certs-force" + declare APP="$1" + + if [[ "$(plugn trigger proxy-type "$APP")" != "haproxy" ]]; then + return + fi + + if [[ -n "$(fn-haproxy-letsencrypt-email)" ]]; then + echo true + fi +} + +trigger-haproxy-vhosts-certs-force "$@" diff --git a/plugins/nginx-vhosts/docker-args-process-deploy b/plugins/nginx-vhosts/docker-args-process-deploy deleted file mode 100755 index 78b06cb98..000000000 --- a/plugins/nginx-vhosts/docker-args-process-deploy +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env bash -set -eo pipefail -[[ $DOKKU_TRACE ]] && set -x -source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" -source "$PLUGIN_AVAILABLE_PATH/nginx-vhosts/functions" - -trigger-nginx-vhosts-docker-args-process-deploy() { - declare desc="nginx-vhosts docker-args-process-deploy plugin trigger" - declare trigger="docker-args-process-deploy" - declare APP="$1" IMAGE_SOURCE_TYPE="$2" IMAGE_TAG="$3" PROC_TYPE="$4" CONTAINER_INDEX="$5" - local output - local STDIN=$(cat) - - if [[ "$PROC_TYPE" != "web" ]]; then - return - fi - - if [[ "$(plugn trigger proxy-type "$APP")" != "nginx" ]]; then - return - fi - - if [[ "$(plugn trigger proxy-is-enabled "$APP")" != "true" ]]; then - return - fi - - if ! plugn trigger domains-vhost-enabled "$APP" 2>/dev/null; then - return - fi - - # ensure we have a port mapping - plugn trigger ports-configure "$APP" - - value="$(fn-nginx-access-log-format "$APP")" - [[ -n "$value" ]] && output="$output '--label=nginx.access-log-format=$value'" - value="$(fn-nginx-bind-address-ipv4 "$APP")" - [[ -n "$value" ]] && output="$output '--label=nginx.bind-address-ipv4=$value'" - value="$(fn-nginx-bind-address-ipv6 "$APP")" - [[ -n "$value" ]] && output="$output '--label=nginx.bind-address-ipv6=$value'" - value="$(fn-nginx-client-max-body-size "$APP")" - [[ -n "$value" ]] && output="$output '--label=nginx.client-max-body-size=$value'" - value="$(fn-nginx-hsts-include-subdomains "$APP")" - [[ -n "$value" ]] && output="$output '--label=nginx.hsts-include-subdomains=$value'" - value="$(fn-nginx-hsts-max-age "$APP")" - [[ -n "$value" ]] && output="$output '--label=nginx.hsts-max-age=$value'" - value="$(fn-nginx-hsts-preload "$APP")" - [[ -n "$value" ]] && output="$output '--label=nginx.hsts-preload=$value'" - value="$(fn-nginx-hsts-is-enabled "$APP")" - [[ -n "$value" ]] && output="$output '--label=nginx.hsts=$value'" - value="443" - [[ -n "$value" ]] && output="$output '--label=nginx.https-port=$value'" - value="$(plugn trigger domains-list "$APP" | xargs)" - [[ -n "$value" ]] && output="$output '--label=nginx.domains=$value'" - value="$(plugn trigger network-get-property "$APP" initial-network)" - [[ -n "$value" ]] && output="$output '--label=nginx.initial-network=$value'" - value="$(plugn trigger ports-get "$APP" | xargs)" - [[ -n "$value" ]] && output="$output '--label=nginx.port-mapping=$value'" - value="$(fn-nginx-proxy-buffer-size "$APP")" - [[ -n "$value" ]] && output="$output '--label=nginx.proxy-buffer-size=$value'" - value="$(fn-nginx-proxy-buffering "$APP")" - [[ -n "$value" ]] && output="$output '--label=nginx.proxy-buffering=$value'" - value="$(fn-nginx-proxy-buffers "$APP")" - [[ -n "$value" ]] && output="$output '--label=nginx.proxy-buffers=$value'" - value="$(fn-nginx-proxy-busy-buffers-size "$APP")" - [[ -n "$value" ]] && output="$output '--label=nginx.proxy-busy-buffer-size=$value'" - value="$(fn-nginx-proxy-read-timeout "$APP")" - [[ -n "$value" ]] && output="$output '--label=nginx.proxy-read-timeout=$value'" - value="$(fn-nginx-x-forwarded-for-value "$APP")" - [[ -n "$value" ]] && output="$output '--label=nginx.x-forwarded-for-value=$value'" - value="$(fn-nginx-x-forwarded-port-value "$APP")" - [[ -n "$value" ]] && output="$output '--label=nginx.x-forwarded-port-value=$value'" - value="$(fn-nginx-x-forwarded-proto-value "$APP")" - [[ -n "$value" ]] && output="$output '--label=nginx.x-forwarded-proto-value=$value'" - value="$(fn-nginx-x-forwarded-ssl "$APP")" - [[ -n "$value" ]] && output="$output '--label=nginx.x-forwarded-ssl=$value'" - - echo -n "$STDIN$output" -} - -trigger-nginx-vhosts-docker-args-process-deploy "$@" diff --git a/plugins/openresty-vhosts/certs-force b/plugins/openresty-vhosts/certs-force new file mode 100755 index 000000000..fdaaf7285 --- /dev/null +++ b/plugins/openresty-vhosts/certs-force @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +source "$PLUGIN_AVAILABLE_PATH/openresty-vhosts/internal-functions" + +trigger-openresty-vhosts-certs-force() { + declare desc="openresty-vhosts certs-force plugin trigger" + declare trigger="certs-force" + declare APP="$1" + + if [[ "$(plugn trigger proxy-type "$APP")" != "openresty" ]]; then + return + fi + + if [[ -n "$(fn-openresty-letsencrypt-email)" ]]; then + echo true + fi +} + +trigger-openresty-vhosts-certs-force "$@" diff --git a/plugins/openresty-vhosts/command-functions b/plugins/openresty-vhosts/command-functions new file mode 100755 index 000000000..d630d87d5 --- /dev/null +++ b/plugins/openresty-vhosts/command-functions @@ -0,0 +1,172 @@ +#!/usr/bin/env bash +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +source "$PLUGIN_AVAILABLE_PATH/openresty-vhosts/internal-functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +cmd-openresty-report() { + declare desc="displays a openresty report for one or more apps" + declare cmd="openresty:report" + [[ "$1" == "$cmd" ]] && shift 1 + declare APP="$1" INFO_FLAG="$2" + + if [[ -n "$APP" ]] && [[ "$APP" == --* ]]; then + INFO_FLAG="$APP" + APP="" + fi + + if [[ -z "$APP" ]] && [[ -z "$INFO_FLAG" ]]; then + INFO_FLAG="true" + fi + + if [[ -z "$APP" ]]; then + for app in $(dokku_apps); do + cmd-openresty-report-single "$app" "$INFO_FLAG" | tee || true + done + else + cmd-openresty-report-single "$APP" "$INFO_FLAG" + fi +} + +cmd-openresty-report-single() { + declare APP="$1" INFO_FLAG="$2" + if [[ "$INFO_FLAG" == "true" ]]; then + INFO_FLAG="" + fi + verify_app_name "$APP" + local flag_map=( + "--openresty-access-log-format: $(fn-openresty-access-log-format "$APP")" + "--openresty-access-log-path: $(fn-openresty-access-log-path "$APP")" + "--openresty-bind-address-ipv4: $(fn-openresty-bind-address-ipv4 "$APP")" + "--openresty-bind-address-ipv6: $(fn-openresty-bind-address-ipv6 "$APP")" + "--openresty-client-max-body-size: $(fn-openresty-client-max-body-size "$APP")" + "--openresty-error-log-path: $(fn-openresty-error-log-path "$APP")" + "--openresty-global-hsts: $(fn-plugin-property-get-default "openresty" "--global" "hsts" "true")" + "--openresty-computed-hsts: $(fn-openresty-hsts-is-enabled "$APP")" + "--openresty-hsts: $(fn-plugin-property-get-default "openresty" "$APP" "hsts" "")" + "--openresty-hsts-include-subdomains: $(fn-openresty-hsts-include-subdomains "$APP")" + "--openresty-hsts-max-age: $(fn-openresty-hsts-max-age "$APP")" + "--openresty-hsts-preload: $(fn-openresty-hsts-preload "$APP")" + "--openresty-image: $(fn-openresty-image)" + "--openresty-letsencrypt-email: $(fn-openresty-letsencrypt-email)" + "--openresty-letsencrypt-server: $(fn-openresty-letsencrypt-server)" + "--openresty-proxy-buffer-size: $(fn-openresty-proxy-buffer-size "$APP")" + "--openresty-proxy-buffering: $(fn-openresty-proxy-buffering "$APP")" + "--openresty-proxy-buffers: $(fn-openresty-proxy-buffers "$APP")" + "--openresty-proxy-busy-buffers-size: $(fn-openresty-proxy-busy-buffers-size "$APP")" + "--openresty-proxy-read-timeout: $(fn-openresty-proxy-read-timeout "$APP")" + "--openresty-x-forwarded-for-value: $(fn-openresty-x-forwarded-for-value "$APP")" + "--openresty-x-forwarded-port-value: $(fn-openresty-x-forwarded-port-value "$APP")" + "--openresty-x-forwarded-proto-value: $(fn-openresty-x-forwarded-proto-value "$APP")" + "--openresty-x-forwarded-ssl: $(fn-openresty-x-forwarded-ssl "$APP")" + ) + + if [[ -z "$INFO_FLAG" ]]; then + dokku_log_info2_quiet "${APP} openresty information" + for flag in "${flag_map[@]}"; do + key="$(echo "${flag#--}" | cut -f1 -d' ' | tr - ' ')" + dokku_log_verbose "$(printf "%-30s %-25s" "${key^}" "${flag#*: }")" + done + else + local match=false + local value_exists=false + for flag in "${flag_map[@]}"; do + valid_flags="${valid_flags} $(echo "$flag" | cut -d':' -f1)" + if [[ "$flag" == "${INFO_FLAG}:"* ]]; then + value=${flag#*: } + size="${#value}" + if [[ "$size" -ne 0 ]]; then + echo "$value" && match=true && value_exists=true + else + match=true + fi + fi + done + [[ "$match" == "true" ]] || dokku_log_fail "Invalid flag passed, valid flags:${valid_flags}" + fi +} + +cmd-openresty-logs() { + declare desc="display openresty logs from command line" + declare cmd="openresty:logs" + [[ "$1" == "$cmd" ]] && shift 1 + local NUM="100" TAIL=false + + local TEMP=$(getopt -o htn: --long help,tail,num: -n 'dokku openresty:logs' -- "$@") + local EXIT_CODE="$?" + if [[ "$EXIT_CODE" != 0 ]]; then + fn-openresty-logs-usage >&2 + exit 1 + fi + eval set -- "$TEMP" + + while true; do + case "$1" in + -t | --tail) + local TAIL=true + shift + ;; + -n | --num) + local NUM="$2" + shift 2 + ;; + --) + shift + break + ;; + *) dokku_log_fail "Internal error" ;; + esac + done + + fn-openresty-logs "$TAIL" "$NUM" +} + +cmd-openresty-show-config() { + declare desc="display openresty config" + declare cmd="openresty:show-config" + [[ "$1" == "$cmd" ]] && shift 1 + + if ! fn-is-compose-installed; then + dokku_log_fail "Required docker compose plugin is not installed" + fi + + local TMP_COMPOSE_FILE=$(mktemp "/tmp/dokku-${DOKKU_PID}-${FUNCNAME[0]}.XXXXXX") + trap "rm -rf '$TMP_COMPOSE_FILE' >/dev/null" RETURN INT TERM EXIT + + fn-openresty-template-compose-file "$TMP_COMPOSE_FILE" + cat "$TMP_COMPOSE_FILE" +} + +cmd-openresty-start() { + declare desc="Starts the openresty server" + declare cmd="openresty:start" + [[ "$1" == "$cmd" ]] && shift 1 + + if ! fn-is-compose-installed; then + dokku_log_fail "Required docker compose plugin is not installed" + fi + + local TMP_COMPOSE_FILE=$(mktemp "/tmp/dokku-${DOKKU_PID}-${FUNCNAME[0]}.XXXXXX") + trap "rm -rf '$TMP_COMPOSE_FILE' >/dev/null" RETURN INT TERM EXIT + + fn-plugin-property-write "openresty" "--global" "proxy-status" "started" + fn-openresty-template-compose-file "$TMP_COMPOSE_FILE" + "$DOCKER_BIN" compose -f "$TMP_COMPOSE_FILE" -p openresty up -d --quiet-pull +} + +cmd-openresty-stop() { + declare desc="Starts the openresty server" + declare cmd="openresty:stop" + [[ "$1" == "$cmd" ]] && shift 1 + + if ! fn-is-compose-installed; then + dokku_log_fail "Required docker compose plugin is not installed" + fi + + local TMP_COMPOSE_FILE=$(mktemp "/tmp/dokku-${DOKKU_PID}-${FUNCNAME[0]}.XXXXXX") + trap "rm -rf '$TMP_COMPOSE_FILE' >/dev/null" RETURN INT TERM EXIT + + fn-plugin-property-write "openresty" "--global" "proxy-status" "stopped" + fn-openresty-template-compose-file "$TMP_COMPOSE_FILE" + "$DOCKER_BIN" compose -f "$TMP_COMPOSE_FILE" -p openresty down --remove-orphans +} diff --git a/plugins/openresty-vhosts/commands b/plugins/openresty-vhosts/commands new file mode 100755 index 000000000..b87c8aa14 --- /dev/null +++ b/plugins/openresty-vhosts/commands @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +[[ " openresty:help help " == *" $1 "* ]] || exit "$DOKKU_NOT_IMPLEMENTED_EXIT" +source "$PLUGIN_AVAILABLE_PATH/openresty-vhosts/help-functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +case "$1" in + help | openresty:help) + cmd-openresty-help "$@" + ;; + + *) + exit "$DOKKU_NOT_IMPLEMENTED_EXIT" + ;; +esac diff --git a/plugins/openresty-vhosts/core-post-deploy b/plugins/openresty-vhosts/core-post-deploy new file mode 100755 index 000000000..c8ad3ea0c --- /dev/null +++ b/plugins/openresty-vhosts/core-post-deploy @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +source "$PLUGIN_AVAILABLE_PATH/openresty-vhosts/internal-functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +trigger-openresty-vhosts-core-post-deploy() { + declare desc="openresty-vhosts core-post-deploy plugin trigger" + declare trigger="core-post-deploy" + declare APP="$1" + local tls_internal + + if [[ "$(plugn trigger proxy-type "$APP")" != "openresty" ]]; then + return + fi + + dokku_log_info1 "Routing app via openresty" + if [[ -f "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/openresty-http-includes.$DOKKU_PID.missing" ]]; then + rm -f "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/openresty-http-includes.$DOKKU_PID.missing" + rm -f "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/openresty-http-includes" + fi +} + +trigger-openresty-vhosts-core-post-deploy "$@" diff --git a/plugins/openresty-vhosts/core-post-extract b/plugins/openresty-vhosts/core-post-extract new file mode 100755 index 000000000..e30bba6c6 --- /dev/null +++ b/plugins/openresty-vhosts/core-post-extract @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +source "$PLUGIN_AVAILABLE_PATH/openresty-vhosts/internal-functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +fn-openresty-vhosts-copy-from-image() { + declare APP="$1" IMAGE_NAME="$2" CONF_PATH="$3" + + mkdir -p "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP" + rm -f "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/openresty-http-includes."* + copy_dir_from_image "$IMAGE_NAME" "$CONF_PATH" "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/openresty-http-includes.$DOKKU_PID" || true + if [[ ! -f "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/openresty-http-includes.$DOKKU_PID" ]]; then + touch "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/openresty-http-includes.$DOKKU_PID.missing" + fi +} + +fn-openresty-vhosts-copy-from-directory() { + declare APP="$1" SOURCECODE_WORK_DIR="$2" CONF_PATH="$3" + + pushd "$SOURCECODE_WORK_DIR" >/dev/null + mkdir -p "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP" + + if [[ -z "$CONF_PATH" ]]; then + touch "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/openresty-http-includes.$DOKKU_PID.missing" + return + fi + + if [[ ! -d "$CONF_PATH" ]]; then + touch "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/openresty-http-includes.$DOKKU_PID.missing" + return + fi + + rm -f "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/openresty-http-includes."* + mkdir p "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/openresty-http-includes.$DOKKU_PID/" + cp -f "$CONF_PATH"/* "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/openresty-http-includes.$DOKKU_PID/" + popd &>/dev/null || pushd "/tmp" >/dev/null +} + +trigger-openresty-vhosts-core-post-extract() { + declare desc="openresty-vhosts post-extract plugin trigger" + declare trigger="post-extract" + declare APP="$1" SOURCECODE_WORK_DIR="$2" + local CONF_PATH="openresty-http-includes" + local app_source_image + + app_source_image="$(plugn trigger git-get-property "$APP" "source-image")" + if [[ -n "$app_source_image" ]]; then + fn-openresty-vhosts-copy-from-image "$APP" "$app_source_image" "$CONF_PATH" + else + fn-openresty-vhosts-copy-from-directory "$APP" "$SOURCECODE_WORK_DIR" "$CONF_PATH" + fi +} + +trigger-openresty-vhosts-core-post-extract "$@" diff --git a/plugins/openresty-vhosts/docker-args-process-deploy b/plugins/openresty-vhosts/docker-args-process-deploy new file mode 100755 index 000000000..1f78bdb33 --- /dev/null +++ b/plugins/openresty-vhosts/docker-args-process-deploy @@ -0,0 +1,153 @@ +#!/usr/bin/env bash +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +source "$PLUGIN_AVAILABLE_PATH/openresty-vhosts/internal-functions" + +trigger-openresty-vhosts-docker-args-process-deploy() { + declare desc="openresty-vhosts docker-args-process-deploy plugin trigger" + declare trigger="docker-args-process-deploy" + declare APP="$1" IMAGE_SOURCE_TYPE="$2" IMAGE_TAG="$3" PROC_TYPE="$4" CONTAINER_INDEX="$5" + local include_dir output value DATA + local STDIN=$(cat) + + if [[ "$PROC_TYPE" != "web" ]]; then + return + fi + + if [[ "$(plugn trigger proxy-type "$APP")" != "openresty" ]] && [[ "$(plugn trigger proxy-type "$APP")" != "nginx" ]]; then + return + fi + + if [[ "$(plugn trigger proxy-is-enabled "$APP")" != "true" ]]; then + return + fi + + if ! plugn trigger domains-vhost-enabled "$APP" 2>/dev/null; then + return + fi + + # ensure we have a port mapping + plugn trigger ports-configure "$APP" + + include_dir="$(fn-openresty-get-http-includes-dir "$APP")" + if [[ -d "$include_dir" ]]; then + pushd "$include_dir" >/dev/null + for filename in *; do + if [[ ! -f "$include_dir/$filename" ]]; then + continue + fi + + if [[ $filename != *.conf ]]; then + continue + fi + + DATA="$(base64 -w 0 <"$include_dir/$filename")" + output="$output '--label=openresty.include-http-$filename=$DATA'" + done + popd &>/dev/null || pushd "/tmp" >/dev/null + fi + + is_app_listening="false" + local APP_PORT_MAP="$(plugn trigger ports-get "$APP")" + while IFS= read -r port_map; do + proxy_scheme="$(awk -F ':' '{ print $1 }' <<<"$port_map")" + proxy_host_port="$(awk -F ':' '{ print $2 }' <<<"$port_map")" + proxy_container_port="$(awk -F ':' '{ print $3 }' <<<"$port_map")" + + if [[ "$proxy_scheme" == "http" ]]; then + is_app_listening="true" + if [[ -z "$proxy_container_http_port_candidate" ]]; then + proxy_container_http_port_candidate="$proxy_container_port" + proxy_host_http_port_candidate="$proxy_host_port" + fi + + if [[ "$proxy_host_port" == "80" ]] && [[ -z "$proxy_container_http_port" ]]; then + proxy_container_http_port="$proxy_container_port" + fi + fi + + if [[ "$proxy_scheme" == "https" ]]; then + is_app_listening="true" + if [[ -z "$proxy_container_https_port_candidate" ]]; then + proxy_container_https_port_candidate="$proxy_container_port" + proxy_host_https_port_candidate="$proxy_host_port" + fi + + if [[ "$proxy_host_port" == "443" ]] && [[ -z "$proxy_container_https_port" ]]; then + proxy_container_https_port="$proxy_container_port" + fi + fi + done <<<"$APP_PORT_MAP" + + if [[ -n "$letsencrypt_email" ]] && [[ -z "$proxy_container_https_port" ]]; then + proxy_container_https_port_candidate="$proxy_container_http_port_candidate" + proxy_host_https_port_candidate="$proxy_host_http_port_candidate" + if [[ -n "$proxy_container_http_port" ]]; then + proxy_container_https_port_candidate="$proxy_container_http_port" + proxy_host_http_port_candidate=443 + fi + fi + + letsencrypt_value="false" + if [[ -n "$(fn-openresty-letsencrypt-email)" ]]; then + if [[ -n "$proxy_container_https_port" ]] || [[ -n "$proxy_container_https_port_candidate" ]]; then + letsencrypt_value="true" + fi + fi + + output="$output '--label=openresty.letsencrypt=$letsencrypt_value'" + + value="$(fn-openresty-access-log-format "$APP")" + [[ -n "$value" ]] && output="$output '--label=openresty.access-log-format=$value'" + value="$(fn-openresty-bind-address-ipv4 "$APP")" + [[ -n "$value" ]] && output="$output '--label=openresty.bind-address-ipv4=$value'" + value="$(fn-openresty-bind-address-ipv6 "$APP")" + [[ -n "$value" ]] && output="$output '--label=openresty.bind-address-ipv6=$value'" + value="$(fn-openresty-client-max-body-size "$APP")" + [[ -n "$value" ]] && output="$output '--label=openresty.client-max-body-size=$value'" + value="$(fn-openresty-hsts-include-subdomains "$APP")" + [[ -n "$value" ]] && output="$output '--label=openresty.hsts-include-subdomains=$value'" + value="$(fn-openresty-hsts-max-age "$APP")" + [[ -n "$value" ]] && output="$output '--label=openresty.hsts-max-age=$value'" + value="$(fn-openresty-hsts-preload "$APP")" + [[ -n "$value" ]] && output="$output '--label=openresty.hsts-preload=$value'" + value="$(fn-openresty-hsts-is-enabled "$APP")" + [[ -n "$value" ]] && output="$output '--label=openresty.hsts=$value'" + value="443" + [[ -n "$value" ]] && output="$output '--label=openresty.https-port=$value'" + value="$(plugn trigger domains-list "$APP" | xargs)" + [[ -n "$value" ]] && output="$output '--label=openresty.domains=$value'" + value="$(plugn trigger network-get-property "$APP" initial-network)" + [[ -n "$value" ]] && output="$output '--label=openresty.initial-network=$value'" + value="$(echo "$APP_PORT_MAP" | xargs)" + [[ -n "$value" ]] && output="$output '--label=openresty.port-mapping=$value'" + value="$(fn-openresty-proxy-buffer-size "$APP")" + [[ -n "$value" ]] && output="$output '--label=openresty.proxy-buffer-size=$value'" + value="$(fn-openresty-proxy-buffering "$APP")" + [[ -n "$value" ]] && output="$output '--label=openresty.proxy-buffering=$value'" + value="$(fn-openresty-proxy-buffers "$APP")" + [[ -n "$value" ]] && output="$output '--label=openresty.proxy-buffers=$value'" + value="$(fn-openresty-proxy-busy-buffers-size "$APP")" + [[ -n "$value" ]] && output="$output '--label=openresty.proxy-busy-buffer-size=$value'" + value="$(fn-openresty-proxy-connect-timeout "$APP")" + [[ -n "$value" ]] && output="$output '--label=openresty.proxy-connect-timeout=$value'" + value="$(fn-openresty-proxy-read-timeout "$APP")" + [[ -n "$value" ]] && output="$output '--label=openresty.proxy-read-timeout=$value'" + value="$(fn-openresty-proxy-send-timeout "$APP")" + [[ -n "$value" ]] && output="$output '--label=openresty.proxy-send-timeout=$value'" + value="$(fn-openresty-send-timeout "$APP")" + [[ -n "$value" ]] && output="$output '--label=openresty.send-timeout=$value'" + value="$(fn-openresty-x-forwarded-for-value "$APP")" + [[ -n "$value" ]] && output="$output '--label=openresty.x-forwarded-for-value=$value'" + value="$(fn-openresty-x-forwarded-port-value "$APP")" + [[ -n "$value" ]] && output="$output '--label=openresty.x-forwarded-port-value=$value'" + value="$(fn-openresty-x-forwarded-proto-value "$APP")" + [[ -n "$value" ]] && output="$output '--label=openresty.x-forwarded-proto-value=$value'" + value="$(fn-openresty-x-forwarded-ssl "$APP")" + [[ -n "$value" ]] && output="$output '--label=openresty.x-forwarded-ssl=$value'" + + echo -n "$STDIN$output" +} + +trigger-openresty-vhosts-docker-args-process-deploy "$@" diff --git a/plugins/openresty-vhosts/help-functions b/plugins/openresty-vhosts/help-functions new file mode 100755 index 000000000..d8fc19e7a --- /dev/null +++ b/plugins/openresty-vhosts/help-functions @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +cmd-openresty-help() { + declare desc="help command" + declare CMD="$1" + local plugin_name="openresty" + local plugin_description="Manage the openresty proxy integration" + + if [[ "$CMD" == "${plugin_name}:help" ]]; then + echo -e "Usage: dokku ${plugin_name}[:COMMAND]" + echo '' + echo "$plugin_description" + echo '' + echo 'Additional commands:' + fn-help-content | sort | column -c2 -t -s, + elif [[ $(ps -o command= $PPID) == *"--all"* ]]; then + fn-help-content + else + cat <] [], Displays an openresty report for one or more apps + openresty:set (), Set or clear an openresty property for an app + openresty:show-config , Display openresty compose config + openresty:start, Starts the openresty server + openresty:stop, Stops the openresty server +help_content +} diff --git a/plugins/openresty-vhosts/install b/plugins/openresty-vhosts/install new file mode 100755 index 000000000..643146b17 --- /dev/null +++ b/plugins/openresty-vhosts/install @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +trigger-openresty-install() { + declare desc="installs the openresty plugin" + declare trigger="install" + + mkdir -p "${DOKKU_LIB_ROOT}/data/openresty" "${DOKKU_LIB_ROOT}/data/openresty/.docker-letsencrypt" + chown -R "${DOKKU_SYSTEM_USER}:${DOKKU_SYSTEM_GROUP}" "${DOKKU_LIB_ROOT}/data/openresty" + + fn-plugin-property-setup "openresty" +} + +trigger-openresty-install "$@" diff --git a/plugins/openresty-vhosts/internal-functions b/plugins/openresty-vhosts/internal-functions new file mode 100755 index 000000000..c22799263 --- /dev/null +++ b/plugins/openresty-vhosts/internal-functions @@ -0,0 +1,230 @@ +#!/usr/bin/env bash +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +fn-get-pagesize() { + declare desc="return the underlying system's memory page size" + declare todo="port to common functions" + + "$PLUGIN_CORE_AVAILABLE_PATH/nginx-vhosts/pagesize" +} + +fn-openresty-access-log-format() { + declare desc="get the configured access log format" + declare APP="$1" + + fn-plugin-property-get-default "openresty" "$APP" "access-log-format" "" +} + +fn-openresty-access-log-path() { + declare desc="get the configured access log path" + declare APP="$1" + local OPENRESTY_LOG_ROOT="$(fn-openresty-log-root)" + + fn-plugin-property-get-default "openresty" "$APP" "access-log-path" "${OPENRESTY_LOG_ROOT}/${APP}-access.log" +} + +fn-openresty-bind-address-ipv4() { + declare desc="get the configured ipv4 bind address" + declare APP="$1" + + fn-plugin-property-get-default "openresty" "$APP" "bind-address-ipv4" "" +} + +fn-openresty-bind-address-ipv6() { + declare desc="get the configured ipv6 bind address" + declare APP="$1" + + fn-plugin-property-get-default "openresty" "$APP" "bind-address-ipv6" "::" +} + +fn-openresty-client-max-body-size() { + declare desc="get the configured client max body size" + declare APP="$1" + + fn-plugin-property-get-default "openresty" "$APP" "client-max-body-size" "" +} + +fn-openresty-get-http-includes-dir() { + declare desc="get any include dir if available" + declare APP="$1" + + if [[ -d "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/openresty-http-includes.$DOKKU_PID.missing" ]]; then + return + fi + + if [[ -d "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/openresty-http-includes.$DOKKU_PID" ]]; then + echo "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/openresty-http-includes.$DOKKU_PID" + elif [[ -d "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/openresty-http-includes" ]]; then + echo "${DOKKU_LIB_ROOT}/data/openresty-vhosts/app-$APP/openresty-http-includes" + fi +} + +fn-openresty-hsts-include-subdomains() { + declare APP="$1" + fn-plugin-property-get-default "openresty" "$APP" "hsts-include-subdomains" "true" +} + +fn-openresty-hsts-max-age() { + declare APP="$1" + fn-plugin-property-get-default "openresty" "$APP" "hsts-max-age" "15724800" +} + +fn-openresty-hsts-preload() { + declare APP="$1" + fn-plugin-property-get-default "openresty" "$APP" "hsts-preload" "false" +} + +fn-openresty-hsts-is-enabled() { + declare APP="$1" + local hsts_is_enabled="$(fn-plugin-property-get-default "openresty" "$APP" "hsts" "")" + + if [[ "$hsts_is_enabled" == "" ]]; then + hsts_is_enabled="$(fn-plugin-property-get-default "openresty" "--global" "hsts" "true")" + fi + + echo "$hsts_is_enabled" +} + +fn-openresty-image() { + fn-plugin-property-get-default "openresty" "--global" "image" "dokku/openresty-docker-proxy:0.5.6" +} + +fn-openresty-letsencrypt-email() { + fn-plugin-property-get-default "openresty" "--global" "letsencrypt-email" "" +} + +fn-openresty-letsencrypt-server() { + fn-plugin-property-get-default "openresty" "--global" "letsencrypt-server" "https://acme-v02.api.letsencrypt.org/directory" +} + +fn-openresty-log-root() { + declare desc="get the openresty log root" + local OPENRESTY_LOG_ROOT="/var/log/openresty" + echo "$OPENRESTY_LOG_ROOT" +} + +fn-openresty-logs() { + declare desc="shows the logs for the openresty container" + declare TAIL="$1" NUM="$2" + local dokku_logs_args=("--tail" "$NUM") + + if [[ "$TAIL" == "true" ]]; then + dokku_logs_args+=("--follow") + fi + + "$DOCKER_BIN" logs openresty-openresty-1 "${dokku_logs_args[@]}" +} + +fn-openresty-logs-usage() { + declare desc="logs specific usage" + echo "Usage: dokku openresty:logs" + echo " display recent openresty log output" + echo "" + echo " -n, --num NUM # the number of lines to display" + echo " -t, --tail # continually stream logs" +} + +fn-openresty-proxy-buffer-size() { + declare desc="get the configured proxy buffer size" + declare APP="$1" + + fn-plugin-property-get-default "openresty" "$APP" "proxy-buffer-size" "$(fn-get-pagesize)" +} + +fn-openresty-proxy-buffering() { + declare desc="get the configured proxy buffering" + declare APP="$1" + + fn-plugin-property-get-default "openresty" "$APP" "proxy-buffering" "on" +} + +fn-openresty-proxy-buffers() { + declare desc="get the configured proxy buffers" + declare APP="$1" + + fn-plugin-property-get-default "openresty" "$APP" "proxy-buffers" "8 $(fn-get-pagesize)" +} + +fn-openresty-proxy-busy-buffers-size() { + declare desc="get the configured proxy busy buffers size" + declare APP="$1" + + fn-plugin-property-get-default "openresty" "$APP" "proxy-busy-buffers-size" "$(($(fn-get-pagesize) * 2))" +} + +fn-openresty-proxy-connect-timeout() { + declare desc="get the configured proxy connect timeout" + declare APP="$1" + + fn-plugin-property-get-default "openresty" "$APP" "proxy-connect-timeout" "60s" +} + +fn-openresty-proxy-read-timeout() { + declare desc="get the configured proxy read timeout" + declare APP="$1" + + fn-plugin-property-get-default "openresty" "$APP" "proxy-read-timeout" "60s" +} + +fn-openresty-proxy-send-timeout() { + declare desc="get the configured proxy send timeout" + declare APP="$1" + + fn-plugin-property-get-default "openresty" "$APP" "proxy-send-timeout" "60s" +} + +fn-openresty-send-timeout() { + declare desc="get the configured proxy send timeout" + declare APP="$1" + + fn-plugin-property-get-default "openresty" "$APP" "send-timeout" "60s" +} + +fn-openresty-template-compose-file() { + declare desc="templates out the compose file" + declare OUTPUT_PATH="$1" + local COMPOSE_TEMPLATE="$PLUGIN_AVAILABLE_PATH/openresty-vhosts/templates/compose.yml.sigil" + + CUSTOM_COMPOSE_TEMPLATE="$(plugn trigger openresty-template-source "$APP")" + if [[ -n "$CUSTOM_COMPOSE_TEMPLATE" ]]; then + COMPOSE_TEMPLATE="$CUSTOM_COMPOSE_TEMPLATE" + fi + + local SIGIL_PARAMS=(OPENRESTY_DATA_DIR="${DOKKU_LIB_ROOT}/data/openresty/.docker-letsencrypt" + OPENRESTY_IMAGE="$(fn-openresty-image)" + OPENRESTY_LETSENCRYPT_EMAIL="$(fn-openresty-letsencrypt-email)" + OPENRESTY_LETSENCRYPT_SERVER="$(fn-openresty-letsencrypt-server)") + + sigil -f "$COMPOSE_TEMPLATE" "${SIGIL_PARAMS[@]}" | cat -s >"$OUTPUT_PATH" +} + +fn-openresty-x-forwarded-for-value() { + declare desc="get the configured x-forwarded-for value" + declare APP="$1" + + fn-plugin-property-get-default "openresty" "$APP" "x-forwarded-for-value" "\$remote_addr" +} + +fn-openresty-x-forwarded-port-value() { + declare desc="get the configured x-forwarded-port value" + declare APP="$1" + + fn-plugin-property-get-default "openresty" "$APP" "x-forwarded-port-value" "\$server_port" +} + +fn-openresty-x-forwarded-proto-value() { + declare desc="get the configured x-forwarded-proto value" + declare APP="$1" + + fn-plugin-property-get-default "openresty" "$APP" "x-forwarded-proto-value" "\$scheme" +} + +fn-openresty-x-forwarded-ssl() { + declare desc="get the configured x-forwarded-ssl value" + declare APP="$1" + + fn-plugin-property-get-default "openresty" "$APP" "x-forwarded-ssl" "" +} diff --git a/plugins/openresty-vhosts/plugin.toml b/plugins/openresty-vhosts/plugin.toml new file mode 100644 index 000000000..d02688cf1 --- /dev/null +++ b/plugins/openresty-vhosts/plugin.toml @@ -0,0 +1,4 @@ +[plugin] +description = "dokku core openresty-vhosts plugin" +version = "0.30.10" +[plugin.config] diff --git a/plugins/openresty-vhosts/pre-restore b/plugins/openresty-vhosts/pre-restore new file mode 100755 index 000000000..8903a23ae --- /dev/null +++ b/plugins/openresty-vhosts/pre-restore @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +source "$PLUGIN_AVAILABLE_PATH/openresty-vhosts/command-functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +trigger-openresty-pre-restore() { + declare desc="pre-restore the openresty proxy" + declare trigger="install" + + if [[ "$(fn-plugin-property-get "openresty" "--global" "proxy-status")" != "started" ]]; then + return + fi + + if ! cmd-openresty-start; then + dokku_log_warn "Failed to restore openresty proxy, requests may not route as expected" + fi +} + +trigger-openresty-pre-restore "$@" diff --git a/plugins/openresty-vhosts/report b/plugins/openresty-vhosts/report new file mode 100755 index 000000000..05081e23a --- /dev/null +++ b/plugins/openresty-vhosts/report @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +source "$PLUGIN_AVAILABLE_PATH/openresty-vhosts/command-functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +cmd-openresty-report-single "$@" diff --git a/plugins/openresty-vhosts/subcommands/default b/plugins/openresty-vhosts/subcommands/default new file mode 100755 index 000000000..0d187aa0c --- /dev/null +++ b/plugins/openresty-vhosts/subcommands/default @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_AVAILABLE_PATH/openresty-vhosts/help-functions" + +cmd-openresty-help "openresty:help" diff --git a/plugins/openresty-vhosts/subcommands/logs b/plugins/openresty-vhosts/subcommands/logs new file mode 100755 index 000000000..ff8a9fe66 --- /dev/null +++ b/plugins/openresty-vhosts/subcommands/logs @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +source "$PLUGIN_AVAILABLE_PATH/openresty-vhosts/command-functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +cmd-openresty-logs "$@" diff --git a/plugins/openresty-vhosts/subcommands/report b/plugins/openresty-vhosts/subcommands/report new file mode 100755 index 000000000..5bf0b5380 --- /dev/null +++ b/plugins/openresty-vhosts/subcommands/report @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +source "$PLUGIN_AVAILABLE_PATH/openresty-vhosts/command-functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +cmd-openresty-report "$@" diff --git a/plugins/openresty-vhosts/subcommands/set b/plugins/openresty-vhosts/subcommands/set new file mode 100755 index 000000000..079e0019e --- /dev/null +++ b/plugins/openresty-vhosts/subcommands/set @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +cmd-openresty-set() { + declare desc="set or clear an openresty property for an app" + declare cmd="openresty:set" + [[ "$1" == "$cmd" ]] && shift 1 + declare APP="$1" KEY="$2" VALUE="$3" + local VALID_KEYS=("access-log-format" "access-log-path" "bind-address-ipv4" "bind-address-ipv6" "client-max-body-size" "error-log-path" "hsts" "hsts-include-subdomains" "hsts-preload" "hsts-max-age" "image" "log-level" "letsencrypt-email" "letsencrypt-server" "proxy-read-timeout" "proxy-buffer-size" "proxy-buffering" "proxy-buffers" "proxy-busy-buffers-size" "x-forwarded-for-value" "x-forwarded-port-value" "x-forwarded-proto-value" "x-forwarded-ssl") + local GLOBAL_KEYS=("image" "log-level" "letsencrypt-email" "letsencrypt-server") + + [[ -z "$KEY" ]] && dokku_log_fail "No key specified" + + if ! fn-in-array "$KEY" "${VALID_KEYS[@]}"; then + dokku_log_fail "Invalid key specified, valid keys include: access-log-format, access-log-path, bind-address-ipv4, bind-address-ipv6, client-max-body-size, error-log-path, hsts, hsts-include-subdomains, hsts-preload, hsts-max-age, image, log-level, letsencrypt-email, letsencrypt-server, proxy-read-timeout, proxy-buffer-size, proxy-buffering, proxy-buffers, proxy-busy-buffers-size, x-forwarded-for-value, x-forwarded-port-value, x-forwarded-proto-value, x-forwarded-ssl" + fi + + if ! fn-in-array "$KEY" "${GLOBAL_KEYS[@]}"; then + if [[ "$APP" == "--global" ]]; then + dokku_log_fail "The key '$KEY' cannot be set globally" + fi + verify_app_name "$APP" + fi + + if [[ -n "$VALUE" ]]; then + dokku_log_info2_quiet "Setting ${KEY} to ${VALUE}" + fn-plugin-property-write "openresty" "$APP" "$KEY" "$VALUE" + else + dokku_log_info2_quiet "Unsetting ${KEY}" + fn-plugin-property-delete "openresty" "$APP" "$KEY" + fi +} + +cmd-openresty-set "$@" diff --git a/plugins/openresty-vhosts/subcommands/show-config b/plugins/openresty-vhosts/subcommands/show-config new file mode 100755 index 000000000..98b6e4b83 --- /dev/null +++ b/plugins/openresty-vhosts/subcommands/show-config @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +source "$PLUGIN_AVAILABLE_PATH/openresty-vhosts/command-functions" + +cmd-openresty-show-config "$@" diff --git a/plugins/openresty-vhosts/subcommands/start b/plugins/openresty-vhosts/subcommands/start new file mode 100755 index 000000000..7f42b1984 --- /dev/null +++ b/plugins/openresty-vhosts/subcommands/start @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_AVAILABLE_PATH/openresty-vhosts/command-functions" + +cmd-openresty-start "$@" diff --git a/plugins/openresty-vhosts/subcommands/stop b/plugins/openresty-vhosts/subcommands/stop new file mode 100755 index 000000000..308149693 --- /dev/null +++ b/plugins/openresty-vhosts/subcommands/stop @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_AVAILABLE_PATH/openresty-vhosts/command-functions" + +cmd-openresty-stop "$@" diff --git a/plugins/openresty-vhosts/templates/compose.yml.sigil b/plugins/openresty-vhosts/templates/compose.yml.sigil new file mode 100644 index 000000000..36e2fd5e2 --- /dev/null +++ b/plugins/openresty-vhosts/templates/compose.yml.sigil @@ -0,0 +1,27 @@ +--- +version: "3.7" + +services: + openresty: + image: "{{ $.OPENRESTY_IMAGE }}" + + environment: + - OPENRESTY_LABEL_PREFIX=openresty. + {{ if $.OPENRESTY_LETSENCRYPT_EMAIL }} + - OPENRESTY_LETSENCRYPT_EMAIL={{ $.OPENRESTY_LETSENCRYPT_EMAIL }}" + - OPENRESTY_LETSENCRYPT_CA={{ $.OPENRESTY_LETSENCRYPT_SERVER }} + {{ end }} + + network_mode: bridge + + ports: + - "80:80" + {{ if $.OPENRESTY_LETSENCRYPT_EMAIL }} + - "443:443" + {{ end }} + + restart: unless-stopped + + volumes: + - "/var/run/docker.sock:/var/run/docker.sock:ro" + - "{{ $.OPENRESTY_DATA_DIR }}:/etc/resty-auto-ssl" diff --git a/plugins/ports/functions.go b/plugins/ports/functions.go index 842166136..7bea56ec3 100644 --- a/plugins/ports/functions.go +++ b/plugins/ports/functions.go @@ -30,9 +30,13 @@ func clearPorts(appName string) error { } func doesCertExist(appName string) bool { - b, _ := common.PlugnTriggerOutput("certs-exists", []string{appName}...) - certsExists := strings.TrimSpace(string(b[:])) - return certsExists == "true" + certsExists, _ := common.PlugnTriggerOutputAsString("certs-exists", []string{appName}...) + if certsExists == "true" { + return true + } + + certsForce, _ := common.PlugnTriggerOutputAsString("certs-force", []string{appName}...) + return certsForce == "true" } func filterAppPortMaps(appName string, scheme string, hostPort int) []PortMap { diff --git a/plugins/traefik-vhosts/certs-force b/plugins/traefik-vhosts/certs-force new file mode 100755 index 000000000..665717bad --- /dev/null +++ b/plugins/traefik-vhosts/certs-force @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +source "$PLUGIN_AVAILABLE_PATH/traefik-vhosts/internal-functions" + +trigger-traefik-vhosts-certs-force() { + declare desc="traefik-vhosts certs-force plugin trigger" + declare trigger="certs-force" + declare APP="$1" + + if [[ "$(plugn trigger proxy-type "$APP")" != "traefik" ]]; then + return + fi + + if [[ -n "$(fn-traefik-letsencrypt-email)" ]]; then + echo true + fi +} + +trigger-traefik-vhosts-certs-force "$@" diff --git a/tests/unit/caddy.bats b/tests/unit/caddy.bats index 76a7c370e..d48d41196 100644 --- a/tests/unit/caddy.bats +++ b/tests/unit/caddy.bats @@ -145,5 +145,5 @@ teardown() { run /bin/bash -c "dokku --quiet ports:report $TEST_APP --ports-map-detected" echo "output: $output" echo "status: $status" - assert_output "http:80:5000" + assert_output "http:80:5000 https:443:5000" } diff --git a/tests/unit/haproxy.bats b/tests/unit/haproxy.bats index 31c781730..dc4f09218 100644 --- a/tests/unit/haproxy.bats +++ b/tests/unit/haproxy.bats @@ -162,5 +162,5 @@ teardown() { run /bin/bash -c "dokku --quiet ports:report $TEST_APP --ports-map-detected" echo "output: $output" echo "status: $status" - assert_output "http:80:5000" + assert_output "http:80:5000 https:443:5000" } diff --git a/tests/unit/openresty.bats b/tests/unit/openresty.bats new file mode 100644 index 000000000..f188bbedc --- /dev/null +++ b/tests/unit/openresty.bats @@ -0,0 +1,189 @@ +#!/usr/bin/env bats + +load test_helper + +setup() { + global_setup + dokku nginx:stop + dokku openresty:set --global letsencrypt-server https://acme-staging-v02.api.letsencrypt.org/directory + dokku openresty:set --global letsencrypt-email + dokku openresty:start + create_app +} + +teardown() { + global_teardown + destroy_app + dokku openresty:stop + dokku nginx:start +} + +@test "(openresty) openresty:help" { + run /bin/bash -c "dokku openresty" + echo "output: $output" + echo "status: $status" + assert_output_contains "Manage the openresty proxy integration" + help_output="$output" + + run /bin/bash -c "dokku openresty:help" + echo "output: $output" + echo "status: $status" + assert_output_contains "Manage the openresty proxy integration" + assert_output "$help_output" +} + +@test "(openresty) single domain" { + run /bin/bash -c "dokku proxy:set $TEST_APP openresty" + echo "output: $output" + echo "status: $status" + assert_success + + run deploy_app python dokku@dokku.me:$TEST_APP convert_to_dockerfile + echo "output: $output" + echo "status: $status" + assert_success + + assert_http_localhost_success "http" "$TEST_APP.dokku.me" "80" "" "python/http.server" +} + +@test "(openresty) multiple domains" { + run /bin/bash -c "dokku proxy:set $TEST_APP openresty" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku domains:add $TEST_APP $TEST_APP.dokku.me" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku domains:add $TEST_APP $TEST_APP-2.dokku.me" + echo "output: $output" + echo "status: $status" + assert_success + + run deploy_app python dokku@dokku.me:$TEST_APP convert_to_dockerfile + echo "output: $output" + echo "status: $status" + assert_success + + assert_http_localhost_success "http" "$TEST_APP.dokku.me" "80" "" "python/http.server" + assert_http_localhost_success "http" "$TEST_APP-2.dokku.me" "80" "" "python/http.server" +} + +@test "(openresty) ssl" { + run /bin/bash -c "dokku builder-herokuish:set $TEST_APP allowed true" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku proxy:set $TEST_APP openresty" + echo "output: $output" + echo "status: $status" + assert_success + + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success + assert_http_localhost_success "http" "$TEST_APP.dokku.me" "80" "" "python/http.server" + + run /bin/bash -c "dokku ports:report $TEST_APP --ports-map-detected" + echo "output: $output" + echo "status: $status" + assert_success + assert_output "http:80:5000" + + run /bin/bash -c "docker inspect $TEST_APP.web.1 --format '{{ index .Config.Labels \"openresty.letsencrypt\" }}'" + echo "output: $output" + echo "status: $status" + assert_success + assert_output "false" + + run /bin/bash -c "dokku openresty:set --global letsencrypt-email test@example.com" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku openresty:stop" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku openresty:start" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku ps:rebuild $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku ps:inspect $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "docker inspect $TEST_APP.web.1 --format '{{ index .Config.Labels \"openresty.letsencrypt\" }}'" + echo "output: $output" + echo "status: $status" + assert_success + assert_output "true" + + run /bin/bash -c "dokku ports:report $TEST_APP --ports-map-detected" + echo "output: $output" + echo "status: $status" + assert_success + assert_output "http:80:5000 https:443:5000" +} + +@test "(openresty) includes" { + run /bin/bash -c "dokku proxy:set $TEST_APP openresty" + echo "output: $output" + echo "status: $status" + assert_success + + run deploy_app python dokku@dokku.me:$TEST_APP add_openresty_include + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku ps:inspect $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "docker inspect $TEST_APP.web.1 --format '{{ index .Config.Labels \"openresty.include-http-example.conf\" }}'" + echo "output: $output" + echo "status: $status" + assert_success + assert_output "IyBmb3JjZSB0aGUgY2hhcmFjdGVyIHNldCB0byB1dGYtOApjaGFyc2V0IFVURi04Owo=" + + run /bin/bash -c "docker logs openresty-openresty-1" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "docker exec openresty-openresty-1 /usr/local/openresty/nginx/sbin/nginx -t" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "docker exec openresty-openresty-1 cat /etc/nginx/sites-enabled/sites.conf" + echo "output: $output" + echo "status: $status" + assert_success + assert_output_contains "force the character set to utf-8" + assert_output_contains "charset UTF-8;" +} + +add_openresty_include() { + local APP="$1" + local APP_REPO_DIR="$2" + [[ -z "$APP" ]] && local APP="$TEST_APP" + mkdir -p "$APP_REPO_DIR/openresty-http-includes" + touch "$APP_REPO_DIR/openresty-http-includes/example.conf" + echo "# force the character set to utf-8" >>"$APP_REPO_DIR/openresty-http-includes/example.conf" + echo "charset UTF-8;" >>"$APP_REPO_DIR/openresty-http-includes/example.conf" +} diff --git a/tests/unit/test_helper.bash b/tests/unit/test_helper.bash index c37496fe8..6cdca2e2a 100644 --- a/tests/unit/test_helper.bash +++ b/tests/unit/test_helper.bash @@ -223,12 +223,19 @@ assert_http_success() { } assert_http_localhost_success() { - local scheme="$1" domain="$2" port="${3:-80}" path="${4:-}" + local scheme="$1" domain="$2" port="${3:-80}" path="${4:-}" content="${5:-}" run curl --connect-to "$domain:$port:localhost:$port" -kSso /dev/null -w "%{http_code}" "$scheme://$domain:$port$path" echo "curl: curl --connect-to $domain:$port:localhost:$port -kSso /dev/null -w %{http_code} $scheme://$domain:$port$path" echo "output: $output" echo "status: $status" assert_output "200" + + if [[ -n "$content" ]]; then + run curl --connect-to "$domain:$port:localhost:$port" -kSs "$scheme://$domain:$port$path" + echo "output: $output" + echo "status: $status" + assert_output "$content" + fi } assert_ssl_domain() { diff --git a/tests/unit/traefik.bats b/tests/unit/traefik.bats index 0ed0ec274..835b974cd 100644 --- a/tests/unit/traefik.bats +++ b/tests/unit/traefik.bats @@ -241,7 +241,7 @@ teardown() { run /bin/bash -c "dokku --quiet ports:report $TEST_APP --ports-map-detected" echo "output: $output" echo "status: $status" - assert_output "http:80:5000" + assert_output "http:80:5000 https:443:5000" } @test "(traefik) show-config without auth set" { From 26e9cc9b8c18b52ad3d4de743984cbd8526ff59b Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sat, 5 Aug 2023 12:28:11 -0400 Subject: [PATCH 54/61] feat: add support for non-web healthchecks via app.json This change converts existing CHECKS files into a healthchecks key that is understood by 'docker-container-healthchecker'. This tool supports a number of different types of container healthchecks - command, http, uptime - and can perform healthchecks against non-web processes. The use of the old CHECKS file is now deprecated, and will be removed in the next minor version. Users can use the 'docker-container-healthchecker' to convert existing CHECKS files to the new format automatically. Closes #2760 --- Makefile | 9 +- .../digitalocean/in_parts/012-dokku-packages | 3 +- debian/control | 2 +- docker/etc/sudoers.d/dokku-docker | 2 +- .../local/bin/docker-container-healthchecker | 20 ++ docs/appendices/0.31.0-migration-guide.md | 2 + docs/deployment/zero-downtime-deploys.md | 293 ++---------------- docs/development/plugin-triggers.md | 15 + docs/processes/process-management.md | 1 - plugins/20_events/app-json-get-content | 1 + plugins/app-json/Makefile | 2 +- plugins/app-json/src/triggers/triggers.go | 3 + plugins/app-json/triggers.go | 22 ++ plugins/scheduler-docker-local/check-deploy | 171 ++++------ tests/ci/setup.sh | 7 + tests/unit/checks.bats | 2 +- 16 files changed, 171 insertions(+), 384 deletions(-) create mode 100755 docker/usr/local/bin/docker-container-healthchecker create mode 120000 plugins/20_events/app-json-get-content diff --git a/Makefile b/Makefile index 7e91b9560..19455ec34 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,7 @@ DOKKU_VERSION ?= master TARGETARCH ?= amd64 DOCKER_IMAGE_LABELER_VERSION ?= 0.5.0 +DOCKER_CONTAINER_HEALTHCHECKER_VERSION ?= 0.6.4 HEROKUISH_VERSION ?= 0.6.0 LAMBDA_BUILDER_VERSION ?= 0.4.0 NETRC_VERSION ?= 0.6.0 @@ -11,6 +12,7 @@ PROCFILE_VERSION ?= 0.15.0 SIGIL_VERSION ?= 0.9.0 SSHCOMMAND_VERSION ?= 0.16.0 DOCKER_IMAGE_LABELER_URL ?= https://github.com/dokku/docker-image-labeler/releases/download/v${DOCKER_IMAGE_LABELER_VERSION}/docker-image-labeler_${DOCKER_IMAGE_LABELER_VERSION}_linux_${TARGETARCH}.tgz +DOCKER_CONTAINER_HEALTHCHECKER_URL ?= https://github.com/dokku/docker-container-healthchecker/releases/download/v${DOCKER_CONTAINER_HEALTHCHECKER_VERSION}/docker-container-healthchecker_${DOCKER_CONTAINER_HEALTHCHECKER_VERSION}_linux_${TARGETARCH}.tgz LAMBDA_BUILDER_URL ?= https://github.com/dokku/lambda-builder/releases/download/v${LAMBDA_BUILDER_VERSION}/lambda-builder_${LAMBDA_BUILDER_VERSION}_linux_${TARGETARCH}.tgz NETRC_URL ?= https://github.com/dokku/netrc/releases/download/v${NETRC_VERSION}/netrc_${NETRC_VERSION}_linux_${TARGETARCH}.tgz PLUGN_URL ?= https://github.com/dokku/plugn/releases/download/v${PLUGN_VERSION}/plugn_${PLUGN_VERSION}_linux_${TARGETARCH}.tgz @@ -135,7 +137,7 @@ plugin-dependencies: plugn procfile-util plugins: plugn procfile-util docker sudo -E dokku plugin:install --core -dependencies: apt-update docker-image-labeler lambda-builder netrc sshcommand plugn procfile-util docker help2man man-db sigil dos2unix jq parallel +dependencies: apt-update docker-image-labeler docker-container-healthchecker lambda-builder netrc sshcommand plugn procfile-util docker help2man man-db sigil dos2unix jq parallel $(MAKE) -e stack apt-update: @@ -161,6 +163,11 @@ docker-image-labeler: tar xzf /tmp/docker-image-labeler_latest.tgz -C /usr/local/bin mv /usr/local/bin/docker-image-labeler-${TARGETARCH} /usr/local/bin/docker-image-labeler +docker-container-healthchecker: + wget -qO /tmp/docker-container-healthchecker_latest.tgz ${DOCKER_CONTAINER_HEALTHCHECKER_URL} + tar xzf /tmp/docker-container-healthchecker_latest.tgz -C /usr/local/bin + mv /usr/local/bin/docker-container-healthchecker-${TARGETARCH} /usr/local/bin/docker-container-healthchecker + lambda-builder: wget -qO /tmp/lambda-builder_latest.tgz ${LAMBDA_BUILDER_URL} tar xzf /tmp/lambda-builder_latest.tgz -C /usr/local/bin diff --git a/contrib/images/digitalocean/in_parts/012-dokku-packages b/contrib/images/digitalocean/in_parts/012-dokku-packages index 768c44924..193327852 100755 --- a/contrib/images/digitalocean/in_parts/012-dokku-packages +++ b/contrib/images/digitalocean/in_parts/012-dokku-packages @@ -33,7 +33,8 @@ pkgs=(apache2-utils debconf ) -dokku_pkgs=(docker-image-labeler +dokku_pkgs=(docker-docker-container-healthchecker + docker-image-labeler gliderlabs-sigil lambda-builder netrc diff --git a/debian/control b/debian/control index 146b6c40d..df54edcc9 100644 --- a/debian/control +++ b/debian/control @@ -3,7 +3,7 @@ Version: 0.30.11 Section: web Priority: optional Architecture: amd64 -Depends: apache2-utils, locales, git, cpio, curl, man-db, netcat, sshcommand (>= 0.12.0), docker-engine-cs (>= 17.05.0) | docker-engine (>= 17.05.0) | docker-io (>= 17.05.0) | docker.io (>= 17.05.0) | docker-ce (>= 17.05.0) | docker-ee (>= 17.05.0) | moby-engine, docker-compose-plugin | moby-compose, docker-image-labeler (>= 0.2.2), lambda-builder, net-tools, netrc, software-properties-common, parallel, procfile-util (>= 0.11.0), python-software-properties | python3-software-properties, rsync, rsyslog, dos2unix, jq, unzip +Depends: apache2-utils, locales, git, cpio, curl, man-db, netcat, sshcommand (>= 0.12.0), docker-engine-cs (>= 17.05.0) | docker-engine (>= 17.05.0) | docker-io (>= 17.05.0) | docker.io (>= 17.05.0) | docker-ce (>= 17.05.0) | docker-ee (>= 17.05.0) | moby-engine, docker-compose-plugin | moby-compose, docker-container-healthchecker (>= 0.6.4), docker-image-labeler (>= 0.2.2), lambda-builder, net-tools, netrc, software-properties-common, parallel, procfile-util (>= 0.11.0), python-software-properties | python3-software-properties, rsync, rsyslog, dos2unix, jq, unzip Recommends: herokuish (>= 0.3.4), bash-completion, dokku-update, dokku-event-listener Pre-Depends: gliderlabs-sigil, nginx (>= 1.8.0) | openresty, dnsutils, cgroupfs-mount | cgroup-lite, plugn (>= 0.3.0), sudo, python3, debconf Maintainer: Jose Diaz-Gonzalez diff --git a/docker/etc/sudoers.d/dokku-docker b/docker/etc/sudoers.d/dokku-docker index 8150da4a5..6dfc5d175 100644 --- a/docker/etc/sudoers.d/dokku-docker +++ b/docker/etc/sudoers.d/dokku-docker @@ -1 +1 @@ -dokku ALL=NOPASSWD:SETENV:/usr/bin/docker,/usr/bin/docker-image-labeler,/usr/bin/pack,/usr/bin/crontab +dokku ALL=NOPASSWD:SETENV:/usr/bin/docker,/usr/bin/docker-container-healthchecker,/usr/bin/docker-image-labeler,/usr/bin/pack,/usr/bin/crontab diff --git a/docker/usr/local/bin/docker-container-healthchecker b/docker/usr/local/bin/docker-container-healthchecker new file mode 100755 index 000000000..8453942f2 --- /dev/null +++ b/docker/usr/local/bin/docker-container-healthchecker @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +set -eo pipefail +[[ $TRACE ]] && set -x + +main() { + declare desc="re-runs docker-container-healthchecker commands as sudo" + local DOCKER_CONTAINER_HEALTHCHECKER_BIN="" + if [[ -x "/usr/bin/docker-container-healthchecker" ]]; then + DOCKER_CONTAINER_HEALTHCHECKER_BIN="/usr/bin/docker-container-healthchecker" + fi + + if [[ -z "$DOCKER_CONTAINER_HEALTHCHECKER_BIN" ]]; then + echo "! No docker-container-healthchecker binary found" 1>&2 + exit 1 + fi + + sudo -E "$DOCKER_CONTAINER_HEALTHCHECKER_BIN" "$@" +} + +main "$@" diff --git a/docs/appendices/0.31.0-migration-guide.md b/docs/appendices/0.31.0-migration-guide.md index 7ed392dc8..95a76181d 100644 --- a/docs/appendices/0.31.0-migration-guide.md +++ b/docs/appendices/0.31.0-migration-guide.md @@ -8,6 +8,7 @@ - Users no longer need to clear the `source-image` git property when transitioning from image-based deploys (`git:from-image` and `git:load-image`) to other deployment methods (git push, `git:from-archive`, `git:sync`). - For deploys via the `git:from-image` and `git:load-image` commands, the `CHECKS` file is now extracted from the configured `WORKDIR` property of the image. For all other deploys - git push, `git:from-archive`, `git:sync` - will have the `CHECKS` extracted directly from the source code. The filename in both cases is `CHECKS` and cannot be modified. - Port mappings are now auto-detected during the build process. Users may override detected port mappings via the `ports` plugin. A default port mapping of `http:80:5000` will be used if none is otherwise specified. +- Users building docker images that run Dokku will need to use a new sudoer wrapper for the `docker-container-healthchecker` binary to work correctly. A reference version has been placed in the `docker` skeleton directory. This should only impact platform developers, and users of our Docker image will already have the file available. ## Deprecations @@ -17,6 +18,7 @@ - The `common#get_dockerfile_exposed_ports()` function is deprecated and will be removed in the next release. There is no replacement for this as it's only use in Dokku core was in the `builder-dockerfile` plugin. - The `common#get_exposed_ports_from_image()` function is deprecated and will be removed in the next release. There is no replacement for this as it's only use in Dokku core was during the build process. - The environment variable `DOKKU_PROXY_PORT_MAP` has been migrated to the properties system. Direct changes to the value will be ignored, and users should interact with port maps via the `ports` plugin. +- The `CHECKS` file is deprecated in favor of defining healthchecks in the `app.json` file. The [docker-container-healthchecker](https://github.com/dokku/docker-container-healthchecker) tool can be used to generate healthcheck entries in `app.json` format from existing `CHECKS` files. See the [zero-downtime deploy documentation](/docs/deployment/zero-downtime-deploys.md) for more information on how the new zero downtime check format works. ## Un-Deprecations diff --git a/docs/deployment/zero-downtime-deploys.md b/docs/deployment/zero-downtime-deploys.md index 1d6bc3f88..57738dbaa 100644 --- a/docs/deployment/zero-downtime-deploys.md +++ b/docs/deployment/zero-downtime-deploys.md @@ -13,7 +13,7 @@ checks:skip [process-type(s)] Skip zero-downtime checks for all proc By default, Dokku will wait `10` seconds after starting each container before assuming it is up and proceeding with the deploy. Once this has occurred for all containers started by an application, traffic will be switched to point to your new containers. Dokku will also wait a further `60` seconds *after* the deploy is complete before terminating old containers in order to give time for long running connections to terminate. In either case, you may have more than one container running for a given application. -You may both create user-defined checks for web processes using a `CHECKS` file, as well as customize any and all parts of this experience using the checks plugin. +You may both create user-defined checks for web processes using the `healthchecks` key in the `app.json` file, as well as customize any and all parts of this experience using the checks plugin. > Web checks are performed via `curl` on Dokku host. Some application code - such > as the Django framework - checks for specific hostnames or header values, these @@ -42,7 +42,7 @@ There are certain settings that can be configured via environment variables: - `DOKKU_DEFAULT_CHECKS_WAIT`: (default: `10`) If no user-defined checks are specified - or if the process being checked is not a `web` process - this is the period of time Dokku will wait before checking that a container is still running. - `DOKKU_DOCKER_STOP_TIMEOUT`: (default: `10`) Configurable grace period given to the `docker stop` command. If a container has not stopped by this time, a `kill -9` signal or equivalent is sent in order to force-terminate the container. Both the `ps:stop` and `apps:destroy` commands *also* respect this value. If not specified, the Docker defaults for the [`docker stop` command](https://docs.docker.com/engine/reference/commandline/stop/) will be used. -The following settings may also be specified in the `CHECKS` file, though are available as environment variables in order to ease application reuse. +The following settings may also be specified in the `app.json` file, though are available as environment variables in order to ease application reuse. - `DOKKU_CHECKS_WAIT`: (default: `5`) Wait this many seconds for the container to start before running checks. - `DOKKU_CHECKS_TIMEOUT`: (default: `30`) Wait this many seconds for each response before marking it as a failure. @@ -135,95 +135,45 @@ dokku checks:report node-js-app --checks-disabled-list ## Customizing checks +> New as of 0.31.0 + If your application needs a longer period to boot up - perhaps to load data into memory, or because of slow boot time - you may also use Dokku's `checks` functionality to more precisely check whether an application can serve traffic or not. -Checks are run against the detected `web` process from your application's `Procfile`. For non-web processes, Dokku will fallback to the aforementioned process uptime check. +Healthchecks are run against all process from your application's `Procfile`. When no healthcheck is defined, Dokku will fallback to a process uptime check. -For deploys via the `git:from-image` and `git:load-image` commands, the `CHECKS` file is extracted from the configured `WORKDIR` property of the image. For all other deploys - git push, `git:from-archive`, `git:sync` - will have the `CHECKS` extracted directly from the source code. The filename in both cases is `CHECKS` and cannot be modified. The `CHECKS` file should be plain text and may contain: +One or more healthchecks can be defined in the `app.json` file - see the [deployment task documentation](/docs/advanced-usage/deployment-tasks.md) for more information on how this is extracted - under the `healthchecks.web` path: -- check instructions -- settings (NAME=VALUE) -- comments (lines starting with #) -- empty lines - -### Check instructions - -The format of a check instruction is a path or relative URL, optionally followed by the expected content: - -``` -/about Our Amazing Team +```json +{ + "healthchecks": { + "web": [ + { + "type": "startup", + "name": "web check", + "description": "Checking if the app responds to the /health/ready endpoint", + "path": "/health/ready", + "attempts": 3 + } + ] +} ``` -The `CHECKS` file can contain multiple checks: +A healthcheck entry takes the following properties: -``` -/ My Amazing App -/stylesheets/index.css .body -/scripts/index.js $(function() -/images/logo.png -``` +- `attempts`: (default: `3` seconds) Number of retry attempts to perform on failure. +- `command`: (default `''` - empty string) Command to execute within container. +- `content`: (default: `''` - empty string) Content to search in http path check output. +initialDelay default: 0, unit: seconds) Number of seconds to wait after a container has started before triggering the healthcheck. +- `name`: (default: autogenerated) The name of the healthcheck. If unspecified, it will be autogenerated from the rest of the healthcheck information. +- `path`: (default: `/` - for http checks): An http path to check. +- `timeout`: (default: `5` seconds): Number of seconds to wait before timing out a healthcheck. +- `type`: (default: `""` - none): Type of the healthcheck. Options: liveness, readiness, startup. +- `uptime`: (default: `""` - none): Amount of time the container must be alive before the container is considered healthy. Any restarts will cause this to check to fail, and this check does not respect retries. +- `wait`: (default: `5` seconds): Number of seconds to wait between healthcheck attempts. -To check an application that supports multiple hostnames, use relative URLs that include the hostname: +> Warning: Healthchecks are implemented by specific scheduler plugins, and not all plugins support all options. Please consult the scheduler documentation for further details on what is supported. -``` -//admin.dokku.me Admin Dashboard -//static.dokku.me/logo.png -``` - -You can also specify the protocol to explicitly check HTTPS requests: - -``` -https://admin.dokku.me Admin Dashboard -https://static.dokku.me/logo.png -``` - -While a full URL may be used in order to invoke checks, if you are using relative URLs, the port *must* be omitted. - -> Changed as of 0.22.5 - -Please note that dollar sign bracket characters (`{` and `}`) must be escaped when used within a `CHECKS` file. Escaping follows golang template rules. The proper way to do this is via one of the following methods: - -``` -# escaping the `{` character -# using double-quotes -{{"{"}} - -# using raw string constants -{{`{`}} - -# escaping the `}` character -# using double-quotes -{{"}"}} - -# using raw string constants -{{`}`}} -``` - -### Templating Checks Files - -> New as of 0.22.5 - -An app's `CHECKS` file is sent through a single pass of the [`sigil`](https://github.com/gliderlabs/sigil/) templating tool. This enables usage of Golang templating within application `CHECKS` files. In addition to general templating access, access to app environment variables is also allowed via the `var` function: - -``` -{{ var "SOME_ENV_VAR" }} -``` - -This may be useful if certain zero-downtime checks require access to an app-specific value, such as a domain name. - -### Check settings - -The default behavior is to wait for `5` seconds before running the checks, to timeout the checks after `30` seconds, and to attempt the checks `5` times. If the checks fail `5` times, the deployment is considered failed and the old container will continue serving traffic. - -You can change the default behavior by setting `WAIT`, `TIMEOUT`, and `ATTEMPTS` to different values in the `CHECKS` file: - -``` -WAIT=30 # Wait 1/2 minute -TIMEOUT=60 # Timeout after a minute -ATTEMPTS=10 # Attempt checks 10 times - -/ My Amazing App -``` +See the [docker-container-healthchecker](https://github.com/dokku/docker-container-healthchecker) documentation for more details on how healthchecks are interpreted. ## Manually invoking checks @@ -310,182 +260,3 @@ dokku checks:run node-js-app web.3 -----> Running pre-flight checks Invalid container id specified (APP.web.3) ``` - -## Example: Successful Rails deployment - -In this example, a Rails application is successfully deployed to Dokku. The initial round of checks fails while the server is starting, but once it starts they succeed and the deployment is successful. `WAIT` is set to `10` because our application takes a while to boot up. `ATTEMPTS` is set to `6`, but the third attempt succeeds. - -### Successful `CHECKS` file - -``` -WAIT=10 -ATTEMPTS=6 -/check.txt simple_check -``` - -For this check to work, we've added a line to `config/routes.rb` that simply returns a string: - -``` -get '/check.txt', to: proc {[200, {}, ['simple_check']]} -``` - -### Successful deploy output - -> Note: The output has been trimmed for brevity. - -```shell -git push dokku master -``` - -``` ------> Cleaning up... ------> Building node-js-app from herokuish... ------> Adding BUILD_ENV to build environment... ------> Ruby app detected ------> Compiling Ruby/Rails ------> Using Ruby version: ruby-2.0.0 - -..... - ------> Discovering process types - Procfile declares types -> web ------> Releasing node-js-app... ------> Deploying node-js-app... ------> Running pre-flight checks ------> Attempt 1/6 Waiting for 10 seconds ... - CHECKS expected result: - http://localhost/check.txt => "simple_check" - ! -curl: (7) Failed to connect to 172.17.0.155 port 5000: Connection refused - ! Check attempt 1/6 failed. ------> Attempt 2/6 Waiting for 10 seconds ... - CHECKS expected result: - http://localhost/check.txt => "simple_check" - ! -curl: (7) Failed to connect to 172.17.0.155 port 5000: Connection refused - ! Check attempt 2/6 failed. ------> Attempt 3/6 Waiting for 10 seconds ... - CHECKS expected result: - http://localhost/check.txt => "simple_check" ------> All checks successful! -=====> node-js-app container output: - => Booting Thin - => Rails 4.2.0 application starting in production on http://0.0.0.0:5000 - => Run `rails server -h` for more startup options - => Ctrl-C to shutdown server - Thin web server (v1.6.3 codename Protein Powder) - Maximum connections set to 1024 - Listening on 0.0.0.0:5000, CTRL+C to stop -=====> end node-js-app container output ------> Running post-deploy ------> Configuring myapp.dokku.me... ------> Creating http nginx.conf - Reloading nginx ------> Shutting down old container in 60 seconds -=====> Application deployed: - http://myapp.dokku.me -``` - -## Example: Failing Rails deployment - -In this example, a Rails application fails to deploy. The reason for the failure is that the PostgreSQL database connection fails. The initial checks will fail while we wait for the server to start up, just like in the above example. However, once the server does start accepting connections, we will see an error 500 due to the PostgreSQL database connection failure. - -Once the attempts have been exceeded, the deployment fails and we see the container output, which shows the PostgreSQL connection errors. - -### Failing `CHECKS` file - -``` -WAIT=10 -ATTEMPTS=6 -/ -``` - -> The check to the root url `/` would normally access the database. - -### Failing deploy output - -> Note: The output has been trimmed for brevity. - -```shell -git push dokku master -``` - -``` ------> Cleaning up... ------> Building node-js-app from herokuish... ------> Adding BUILD_ENV to build environment... ------> Ruby app detected ------> Compiling Ruby/Rails ------> Using Ruby version: ruby-2.0.0 - -..... - -Discovering process types -Procfile declares types -> web -Releasing node-js-app... -Deploying node-js-app... -Running pre-flight checks ------> Attempt 1/6 Waiting for 10 seconds ... - CHECKS expected result: - http://localhost/ => "" - ! -curl: (7) Failed to connect to 172.17.0.188 port 5000: Connection refused - ! Check attempt 1/6 failed. ------> Attempt 2/6 Waiting for 10 seconds ... - CHECKS expected result: - http://localhost/ => "" - ! -curl: (7) Failed to connect to 172.17.0.188 port 5000: Connection refused - ! Check attempt 2/6 failed. ------> Attempt 3/6 Waiting for 10 seconds ... - CHECKS expected result: - http://localhost/ => "" - ! -curl: (22) The requested URL returned error: 500 Internal Server Error - ! Check attempt 3/6 failed. ------> Attempt 4/6 Waiting for 10 seconds ... - CHECKS expected result: - http://localhost/ => "" - ! -curl: (22) The requested URL returned error: 500 Internal Server Error - ! Check attempt 4/6 failed. ------> Attempt 5/6 Waiting for 10 seconds ... - CHECKS expected result: - http://localhost/ => "" - ! -curl: (22) The requested URL returned error: 500 Internal Server Error - ! Check attempt 5/6 failed. ------> Attempt 6/6 Waiting for 10 seconds ... - CHECKS expected result: - http://localhost/ => "" - ! -curl: (22) The requested URL returned error: 500 Internal Server Error -Could not start due to 1 failed checks. - ! Check attempt 6/6 failed. -=====> node-js-app container output: - => Booting Thin - => Rails 4.2.0 application starting in production on http://0.0.0.0:5000 - => Run `rails server -h` for more startup options - => Ctrl-C to shutdown server - Thin web server (v1.6.3 codename Protein Powder) - Maximum connections set to 1024 - Listening on 0.0.0.0:5000, CTRL+C to stop - Started GET "/" for 172.17.42.1 at 2015-03-26 21:36:47 +0000 - Is the server running on host "172.17.42.1" and accepting - TCP/IP connections on port 5431? - PG::ConnectionBad (could not connect to server: Connection refused - Is the server running on host "172.17.42.1" and accepting - TCP/IP connections on port 5431? - ): - vendor/bundle/ruby/2.0.0/gems/activerecord-4.2.0/lib/active_record/connection_adapters/postgresql_adapter.rb:651:in `initialize' - vendor/bundle/ruby/2.0.0/gems/activerecord-4.2.0/lib/active_record/connection_adapters/postgresql_adapter.rb:651:in `new' - vendor/bundle/ruby/2.0.0/gems/activerecord-4.2.0/lib/active_record/connection_adapters/postgresql_adapter.rb:651:in `connect' - vendor/bundle/ruby/2.0.0/gems/activerecord-4.2.0/lib/active_record/connection_adapters/postgresql_adapter.rb:242:in `initialize' - vendor/bundle/ruby/2.0.0/gems/activerecord-4.2.0/lib/active_record/connection_adapters/postgresql_adapter.rb:44:in `new' - vendor/bundle/ruby/2.0.0/gems/activerecord-4.2.0/lib/active_record/connection_adapters/postgresql_adapter.rb:44:in `postgresql_connection -=====> end node-js-app container output -/usr/bin/dokku: line 49: 23409 Killed dokku deploy "$APP" -To dokku@dokku.me:myapp - ! [remote rejected] dokku -> master (pre-receive hook declined) -error: failed to push some refs to 'dokku@dokku.me:myapp' -``` diff --git a/docs/development/plugin-triggers.md b/docs/development/plugin-triggers.md index 8c4ad2461..1f75aef43 100644 --- a/docs/development/plugin-triggers.md +++ b/docs/development/plugin-triggers.md @@ -104,6 +104,21 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x # TODO ``` +### `app-json-get-content` + +- Description: Outputs the contents of the app-json file, if any +- Invoked by: Deployment checks +- Arguments: `$APP` +- Example: + +```shell +#!/usr/bin/env bash + +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x + +# TODO +``` + ### `app-maybe-create` - Description: Creates an app (gated by whether this is globally enabled or not) diff --git a/docs/processes/process-management.md b/docs/processes/process-management.md index 1527ccb29..9d3e5fbae 100644 --- a/docs/processes/process-management.md +++ b/docs/processes/process-management.md @@ -140,7 +140,6 @@ For initial app deploys, Dokku will default to starting a single `web` process f There are also a few other exceptions for the `web` process. -- Custom checks defined by a `CHECKS` file only apply to the `web` process type. - By default, the built-in nginx proxy implementation only proxies the `web` process (others may be handled via a custom `nginx.conf.sigil`). - See the [nginx request proxying documentation](/docs/networking/proxies/nginx.md#request-proxying) for more information on how nginx handles proxied requests. - Only the `web` process may be bound to an external port. diff --git a/plugins/20_events/app-json-get-content b/plugins/20_events/app-json-get-content new file mode 120000 index 000000000..5178a749f --- /dev/null +++ b/plugins/20_events/app-json-get-content @@ -0,0 +1 @@ +hook \ No newline at end of file diff --git a/plugins/app-json/Makefile b/plugins/app-json/Makefile index db0133b5d..bff87b1a8 100644 --- a/plugins/app-json/Makefile +++ b/plugins/app-json/Makefile @@ -1,5 +1,5 @@ SUBCOMMANDS = subcommands/report subcommands/set -TRIGGERS = triggers/app-json-process-deploy-parallelism triggers/core-post-deploy triggers/core-post-extract triggers/install triggers/post-app-clone-setup triggers/post-app-rename triggers/post-app-rename-setup triggers/post-create triggers/post-delete triggers/post-deploy triggers/pre-deploy triggers/report +TRIGGERS = triggers/app-json-process-deploy-parallelism triggers/app-json-get-content triggers/core-post-deploy triggers/core-post-extract triggers/install triggers/post-app-clone-setup triggers/post-app-rename triggers/post-app-rename-setup triggers/post-create triggers/post-delete triggers/post-deploy triggers/pre-deploy triggers/report BUILD = commands subcommands triggers PLUGIN_NAME = app-json diff --git a/plugins/app-json/src/triggers/triggers.go b/plugins/app-json/src/triggers/triggers.go index 1b49fdc4d..04580d2e2 100644 --- a/plugins/app-json/src/triggers/triggers.go +++ b/plugins/app-json/src/triggers/triggers.go @@ -22,6 +22,9 @@ func main() { appName := flag.Arg(0) processType := flag.Arg(1) err = appjson.TriggerAppJSONProcessDeployParallelism(appName, processType) + case "app-json-get-content": + appName := flag.Arg(0) + err = appjson.TriggerAppJSONGetContent(appName) case "core-post-deploy": appName := flag.Arg(0) err = appjson.TriggerCorePostDeploy(appName) diff --git a/plugins/app-json/triggers.go b/plugins/app-json/triggers.go index b6e117162..4f33d12c8 100644 --- a/plugins/app-json/triggers.go +++ b/plugins/app-json/triggers.go @@ -37,6 +37,28 @@ func TriggerAppJSONProcessDeployParallelism(appName string, processType string) return nil } +// TriggerAppJSONGetContent outputs the contents of the app-json file, if any +func TriggerAppJSONGetContent(appName string) error { + if !hasAppJSON(appName) { + fmt.Print("{}") + return nil + } + + b, err := os.ReadFile(getProcessSpecificAppJSONPath(appName)) + if err != nil { + return fmt.Errorf("Cannot read app.json file: %v", err) + } + + content := strings.TrimSpace(string(b)) + if content == "" { + fmt.Print("{}") + return nil + } + + fmt.Print(content) + return nil +} + // TriggerCorePostDeploy sets a property to // allow the app to be restored on boot func TriggerCorePostDeploy(appName string) error { diff --git a/plugins/scheduler-docker-local/check-deploy b/plugins/scheduler-docker-local/check-deploy index 678ae2677..cdf2c7610 100755 --- a/plugins/scheduler-docker-local/check-deploy +++ b/plugins/scheduler-docker-local/check-deploy @@ -44,6 +44,7 @@ trigger-scheduler-docker-local-check-deploy() { declare desc="scheduler-docker-local check-deploy plugin trigger" declare trigger="check-deploy" declare APP="$1" DOKKU_APP_CONTAINER_ID="$2" DOKKU_APP_CONTAINER_TYPE="$3" DOKKU_APP_LISTEN_PORT="$4" DOKKU_APP_LISTEN_IP="$5" CONTAINER_INDEX="$6" + local content local DOKKU_SCHEDULER=$(get_app_scheduler "$APP") if [[ "$DOKKU_SCHEDULER" != "docker-local" ]]; then @@ -53,9 +54,6 @@ trigger-scheduler-docker-local-check-deploy() { if [[ -z "$DOKKU_APP_LISTEN_PORT" ]] && [[ -f "$DOKKU_ROOT/$APP/PORT" ]]; then local DOKKU_APP_LISTEN_PORT=$(<"$DOKKU_ROOT/$APP/PORT") fi - if [[ -z "$DOKKU_APP_LISTEN_IP" ]] && [[ -f "$DOKKU_ROOT/$APP/IP" ]]; then - local DOKKU_APP_LISTEN_IP=$(<"$DOKKU_ROOT/$APP/IP") - fi if [[ -z "$DOKKU_APP_CONTAINER_ID" ]]; then local DOKKU_APP_CIDS=($(get_app_container_ids "$APP")) local DOKKU_APP_CONTAINER_ID=${DOKKU_APP_CIDS[0]} @@ -81,6 +79,34 @@ trigger-scheduler-docker-local-check-deploy() { local IMAGE_TAG="$(get_running_image_tag "$APP")" local IMAGE=$(get_deploying_app_image_name "$APP" "$IMAGE_TAG") + local TMP_APP_JSON_OUTPUT=$(mktemp "/tmp/dokku-${DOKKU_PID}-${FUNCNAME[0]}.XXXXXX") + trap "rm -rf '$TMP_APP_JSON_OUTPUT' >/dev/null" RETURN INT TERM EXIT + + plugn trigger app-json-get-content "$APP" >"$TMP_APP_JSON_OUTPUT" + if [[ -s "${CHECKS_FILENAME}" ]]; then + # Reads name/value pairs, sets the WAIT and TIMEOUT variables + exec <"$CHECKS_FILENAME" + local line + local NAME + local VALUE + while read -r line; do + line=$(strip_inline_comments "$line") + # Name/value pair + if [[ "$line" =~ ^.+= ]]; then + NAME=${line%=*} + VALUE=${line#*=} + [[ "$NAME" == "WAIT" ]] && local WAIT=$VALUE + [[ "$NAME" == "TIMEOUT" ]] && local TIMEOUT=$VALUE + [[ "$NAME" == "ATTEMPTS" ]] && local ATTEMPTS=$VALUE + fi + done + + dokku_log_warn "Deprecated: Usage of the CHECKS file is deprecated in favor of healthchecks in app.json" + dokku_log_warn "Please move your healthchecks to app.json." + content="$(docker-container-healthchecker convert "$CHECKS_FILENAME" --app-json "$TMP_APP_JSON_OUTPUT" --pretty)" + echo "$content" >"$TMP_APP_JSON_OUTPUT" + fi + checks_check_deploy_cleanup() { declare desc="print container output" local id="$1" @@ -93,127 +119,40 @@ trigger-scheduler-docker-local-check-deploy() { } trap "checks_check_deploy_cleanup $DOKKU_APP_CONTAINER_ID" RETURN INT TERM EXIT - # We allow custom check for web instances only - if [[ ! -s "${CHECKS_FILENAME}" ]] || [[ "$DOKKU_APP_CONTAINER_TYPE" != "web" ]]; then - # simple default check to see if the container stuck around - local DOKKU_DEFAULT_CHECKS_WAIT="${DOKKU_DEFAULT_CHECKS_WAIT:-10}" - dokku_log_verbose "Waiting for $DOKKU_DEFAULT_CHECKS_WAIT seconds ($DOKKU_APP_CONTAINER_TYPE.$CONTAINER_INDEX)" - sleep "$DOKKU_DEFAULT_CHECKS_WAIT" + local DOKKU_DEFAULT_CHECKS_WAIT="${DOKKU_DEFAULT_CHECKS_WAIT:-10}" + content="$(docker-container-healthchecker add "$DOKKU_APP_CONTAINER_TYPE" --app-json "$TMP_APP_JSON_OUTPUT" --if-empty --pretty --uptime "$DOKKU_DEFAULT_CHECKS_WAIT")" + echo "$content" >"$TMP_APP_JSON_OUTPUT" - ! (is_container_status "$DOKKU_APP_CONTAINER_ID" "Running") && dokku_log_fail "App container failed to start ($DOKKU_APP_CONTAINER_TYPE.$CONTAINER_INDEX)" - local container_restarts="$("$DOCKER_BIN" container inspect --format "{{ .RestartCount }}" "$DOKKU_APP_CONTAINER_ID")" - if [[ $container_restarts -ne 0 ]]; then - "$DOCKER_BIN" container update --restart=no "$DOKKU_APP_CONTAINER_ID" &>/dev/null || true - "$DOCKER_BIN" container stop "$DOKKU_APP_CONTAINER_ID" || true - dokku_log_fail "App container failed to start ($DOKKU_APP_CONTAINER_TYPE.$CONTAINER_INDEX)" - fi + local FAILEDCHECKS=0 + local SSL="$DOKKU_ROOT/$APP/tls" + declare -a ARG_ARRAY + ARG_ARRAY+=("--app-json") + ARG_ARRAY+=("$TMP_APP_JSON_OUTPUT") + ARG_ARRAY+=("--process-type") + ARG_ARRAY+=("$DOKKU_APP_CONTAINER_TYPE") - trap - EXIT - dokku_log_verbose "Default container check successful ($DOKKU_APP_CONTAINER_TYPE.$CONTAINER_INDEX)" && exit 0 + if [[ -e "$SSL/server.crt" && -e "$SSL/server.key" ]]; then + ARG_ARRAY+=("--header") + ARG_ARRAY+=("X-Forwarded-Proto: https") fi - # Reads name/value pairs, sets the WAIT and TIMEOUT variables - exec <"$CHECKS_FILENAME" - local line - local NAME - local VALUE - while read -r line; do - line=$(strip_inline_comments "$line") - # Name/value pair - if [[ "$line" =~ ^.+= ]]; then - NAME=${line%=*} - VALUE=${line#*=} - [[ "$NAME" == "WAIT" ]] && local WAIT=$VALUE - [[ "$NAME" == "TIMEOUT" ]] && local TIMEOUT=$VALUE - [[ "$NAME" == "ATTEMPTS" ]] && local ATTEMPTS=$VALUE - fi - done + if [[ -n "$DOKKU_APP_LISTEN_IP" ]]; then + ARG_ARRAY+=("--ip-address") + ARG_ARRAY+=("$DOKKU_APP_LISTEN_IP") + fi - exec <"$CHECKS_FILENAME" - local CHECK_URL - local EXPECTED - local FAILEDCHECKS=0 - while read -r CHECK_URL EXPECTED; do - # Ignore empty lines and lines starting with # - [[ -z "$CHECK_URL" || "$CHECK_URL" =~ ^\# ]] && continue - # Ignore if it's not a URL in a supported format - ! [[ "$CHECK_URL" =~ ^(http(s)?:)?\/.* ]] && continue + if [[ -n "$DOKKU_APP_LISTEN_PORT" ]]; then + ARG_ARRAY+=("--port") + ARG_ARRAY+=("$DOKKU_APP_LISTEN_PORT") + fi - if [[ "$CHECK_URL" =~ ^https?: ]]; then - local URL_PROTOCOL=${CHECK_URL%:*} - local CHECK_URL=${CHECK_URL#*:} - else - local URL_PROTOCOL="http" - fi - - if [[ "$CHECK_URL" =~ ^//.+ ]]; then - # To test a URL with specific host name, we still make request to localhost, - # but we set Host header to $SEND_HOST. - # - # The pattern is - # //SEND_HOST/PATHNAME - local UNPREFIXED=${CHECK_URL#//} - local URL_HOSTNAME=${UNPREFIXED%%/*} - local URL_PATHNAME=${UNPREFIXED#$URL_HOSTNAME} - - local HEADERS="-H Host:$URL_HOSTNAME" - else - local URL_HOSTNAME=localhost - local URL_PATHNAME=$CHECK_URL - fi - - # -q Do not use .curlrc (must come first) - # --compressed Test compression handled correctly - # --fail Fail on server errors (4xx, 5xx) - # --location Follow redirects - # --noproxy Do not use http_proxy env variable - local CURL_OPTIONS="-q --compressed --fail --location --noproxy $DOKKU_APP_LISTEN_IP --max-time $TIMEOUT" - - # Set X-Forwarded-Proto header if TLS is enabled. - local SSL="$DOKKU_ROOT/$APP/tls" - if [[ -e "$SSL/server.crt" && -e "$SSL/server.key" ]]; then - local CURL_OPTIONS+=" -H X-Forwarded-Proto:https" - fi - - # LOG_URL is used in verbose output below - local LOG_URL="$URL_PROTOCOL://$URL_HOSTNAME$URL_PATHNAME" - # CURL_ARGS includes the behinds the scenes options for hitting the container service directly - local CURL_ARGS="$CURL_OPTIONS $URL_PROTOCOL://$DOKKU_APP_LISTEN_IP:$DOKKU_APP_LISTEN_PORT$URL_PATHNAME $HEADERS" - - dokku_log_verbose "CHECKS expected result: $LOG_URL => \"$EXPECTED\" ($DOKKU_APP_CONTAINER_TYPE.$CONTAINER_INDEX)" - - local ATTEMPT=0 - local SUCCESS=0 - until [[ $SUCCESS == 1 || $ATTEMPT -ge $ATTEMPTS ]]; do - local ATTEMPT=$((ATTEMPT + 1)) - dokku_log_verbose "Attempt $ATTEMPT/$ATTEMPTS. Waiting for $WAIT seconds ($DOKKU_APP_CONTAINER_TYPE.$CONTAINER_INDEX)" - sleep "$WAIT" - - # Capture HTTP response or CURL error message - if OUTPUT=$(curl -# $CURL_ARGS 2>&1); then - # OUTPUT contains the HTTP response - # shellcheck disable=SC2076 - if [[ "$OUTPUT" =~ "$EXPECTED" ]]; then - SUCCESS=1 - break - fi - dokku_log_warn "$LOG_URL: expected to but did not find: \"$EXPECTED\" ($DOKKU_APP_CONTAINER_TYPE.$CONTAINER_INDEX)" - else - # Failed to connect/no response, OUTPUT contains error message - dokku_log_warn "$OUTPUT" - fi - - dokku_log_warn "Check attempt $ATTEMPT/$ATTEMPTS failed ($DOKKU_APP_CONTAINER_TYPE.$CONTAINER_INDEX)" - done - - if [[ $SUCCESS -ne 1 ]]; then - FAILEDCHECKS=$((FAILEDCHECKS + 1)) - fi - done + docker-container-healthchecker check "$DOKKU_APP_CONTAINER_ID" "${ARG_ARRAY[@]}" || FAILEDCHECKS="$?" if [[ $FAILEDCHECKS -gt 0 ]]; then - dokku_log_fail "Could not start due to $FAILEDCHECKS failed checks ($DOKKU_APP_CONTAINER_TYPE.$CONTAINER_INDEX)" - exit 1 + "$DOCKER_BIN" container update --restart=no "$DOKKU_APP_CONTAINER_ID" &>/dev/null || true + "$DOCKER_BIN" container stop "$DOKKU_APP_CONTAINER_ID" || true + dokku_log_warn "Could not start due to $FAILEDCHECKS failed checks ($DOKKU_APP_CONTAINER_TYPE.$CONTAINER_INDEX)" + return 1 fi trap - EXIT diff --git a/tests/ci/setup.sh b/tests/ci/setup.sh index 31bdfbaff..7e2699fcc 100755 --- a/tests/ci/setup.sh +++ b/tests/ci/setup.sh @@ -14,6 +14,12 @@ install_dependencies() { curl -L "https://packagecloud.io/dokku/dokku/packages/ubuntu/focal/docker-image-labeler_${DOCKER_IMAGE_LABELER_VERSION}_amd64.deb/download.deb" -o "$ROOT_DIR/build/${DOCKER_IMAGE_LABELER_PACKAGE_NAME}" fi + DOCKER_CONTAINER_HEALTHCHECKER_VERSION=$(grep DOCKER_CONTAINER_HEALTHCHECKER_VERSION "${ROOT_DIR}/Makefile" | head -n1 | cut -d' ' -f3) + DOCKER_CONTAINER_HEALTHCHECKER_PACKAGE_NAME="docker-container-healthchecker_${DOCKER_CONTAINER_HEALTHCHECKER_VERSION}_amd64.deb" + if [[ ! -f "$ROOT_DIR/build/${DOCKER_CONTAINER_HEALTHCHECKER_PACKAGE_NAME}" ]]; then + curl -L "https://packagecloud.io/dokku/dokku/packages/ubuntu/focal/docker-container-healthchecker_${DOCKER_CONTAINER_HEALTHCHECKER_VERSION}_amd64.deb/download.deb" -o "$ROOT_DIR/build/${DOCKER_CONTAINER_HEALTHCHECKER_PACKAGE_NAME}" + fi + HEROKUISH_VERSION=$(grep HEROKUISH_VERSION "${ROOT_DIR}/Makefile" | head -n1 | cut -d' ' -f3) HEROKUISH_PACKAGE_NAME="herokuish_${HEROKUISH_VERSION}_amd64.deb" if [[ ! -f "$ROOT_DIR/build/${HEROKUISH_PACKAGE_NAME}" ]]; then @@ -62,6 +68,7 @@ install_dependencies() { ls -lah "${ROOT_DIR}/build/" sudo dpkg -i \ + "${ROOT_DIR}/build/$DOCKER_CONTAINER_HEALTHCHECKER_PACKAGE_NAME" \ "${ROOT_DIR}/build/$DOCKER_IMAGE_LABELER_PACKAGE_NAME" \ "${ROOT_DIR}/build/$HEROKUISH_PACKAGE_NAME" \ "${ROOT_DIR}/build/$LAMBDA_BUILDER_PACKAGE_NAME" \ diff --git a/tests/unit/checks.bats b/tests/unit/checks.bats index d91bb21a9..5a3886c1c 100644 --- a/tests/unit/checks.bats +++ b/tests/unit/checks.bats @@ -265,6 +265,6 @@ teardown() { run deploy_app nodejs-express dokku@$DOKKU_DOMAIN:$TEST_APP template_checks_file echo "output: $output" echo "status: $status" - assert_output_contains "/healthcheck" 2 + assert_output_contains "/healthcheck" assert_success } From 5b738f50c3897bfa62b3c1cb8fac5a37468685c4 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 7 Aug 2023 01:37:18 -0400 Subject: [PATCH 55/61] docs: deprecated ARM as a build target AMD64 and ARM64 continue to be supported, but ARM in CI is not currently supported and doesn't get local testing either, so deprecating it now and removing it in the future makes most sense for the project. --- docs/appendices/0.31.0-migration-guide.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/appendices/0.31.0-migration-guide.md b/docs/appendices/0.31.0-migration-guide.md index 95a76181d..ecb871bb8 100644 --- a/docs/appendices/0.31.0-migration-guide.md +++ b/docs/appendices/0.31.0-migration-guide.md @@ -19,6 +19,7 @@ - The `common#get_exposed_ports_from_image()` function is deprecated and will be removed in the next release. There is no replacement for this as it's only use in Dokku core was during the build process. - The environment variable `DOKKU_PROXY_PORT_MAP` has been migrated to the properties system. Direct changes to the value will be ignored, and users should interact with port maps via the `ports` plugin. - The `CHECKS` file is deprecated in favor of defining healthchecks in the `app.json` file. The [docker-container-healthchecker](https://github.com/dokku/docker-container-healthchecker) tool can be used to generate healthcheck entries in `app.json` format from existing `CHECKS` files. See the [zero-downtime deploy documentation](/docs/deployment/zero-downtime-deploys.md) for more information on how the new zero downtime check format works. +- ARM support is now deprecated, and will be removed in the next minor release. ARM releases are not tested in CI and do not get testing locally. Users of ARM installations on platforms such as the Raspberry PI should consider migrating to ARM64. ARM64 will continue to be supported by Dokku, and there are no plans to remove support for ARM64. ## Un-Deprecations From e09b0eb1b93eb4a5860a5bd0d0b10bed6fe5abbb Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 7 Aug 2023 03:05:47 -0400 Subject: [PATCH 56/61] feat: timeout docker image builds in CI ARM usually takes quite a while to build. If it takes more than ~15 minutes, then it is usually going to fail anyways, so timeout early. AMD64 takes ~3 minutes, and ARM64 appears to take 15 minutes. --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index afd700308..6bfa69123 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -106,6 +106,7 @@ jobs: uses: docker/setup-buildx-action@v2 - name: build docker image + timeout-minutes: 20 run: | docker buildx build \ --platform ${{ matrix.architecture }} \ From 8d25b931ddadaaa649e41fe5680901bf730e90d1 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sat, 1 Jul 2023 08:11:51 -0400 Subject: [PATCH 57/61] fix: correctly handle rebase when generating release notes for minor/major releases With the integration branch where pull requests are merged prior to a release, we require a rebase every so often to ensure there aren't any issues upon merging to master. Unfortunately, this breaks the link between the original PR and the integration PR, thus causing changelog generation to fail as it depended upon those - now removed - merge request commits. With this change, we introduce usage of milestones in order to track what is getting merged into a release. Milestones are only used for major/minor releases, and all others will fall back to the default git log method. --- contrib/release-dokku | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/contrib/release-dokku b/contrib/release-dokku index ea5935bc6..1c740445d 100755 --- a/contrib/release-dokku +++ b/contrib/release-dokku @@ -183,8 +183,12 @@ fn-replace-version() { fn-repo-merged-requests() { declare desc="Retrieves all pull request ids since a given release of dokku" - declare TAG="$1" - git log "v${TAG}..HEAD" --oneline | grep "Merge pull request" | cut -d' ' -f 5 | cut -d '#' -f2 + declare TAG="$1" IS_PATCH="$2" + if [[ "$IS_PATCH" == "true" ]]; then + git log "v${TAG}..HEAD" --oneline | grep "Merge pull request" | cut -d' ' -f 5 | cut -d '#' -f2 + else + curl -u "$RELEASE_GITHUB_USERNAME:$RELEASE_GITHUB_API_TOKEN" --fail -sS "https://api.github.com/search/issues?q=repo:dokku/dokku+milestone:v${NEXT_VERSION}+is:pr" | jq '.items[].number' + fi } fn-repo-push-tags() { @@ -201,7 +205,7 @@ fn-repo-push-tags() { fn-repo-update() { declare desc="Updates files within the repository for a given release" - declare IS_RELEASE="$1" CURRENT_VERSION="$2" NEXT_VERSION="$3" + declare IS_RELEASE="$1" CURRENT_VERSION="$2" NEXT_VERSION="$3" IS_PATCH="$4" log-info "(release-dokku) Replacing ${CURRENT_VERSION} with ${NEXT_VERSION}" for f in plugins/*/plugin.toml; do @@ -238,13 +242,13 @@ fn-repo-update() { fi log-info "(release-dokku) Adding HISTORY.md, committing and tagging" - fn-repo-update-history-and-commit "$CURRENT_VERSION" "$NEXT_VERSION" + fn-repo-update-history-and-commit "$CURRENT_VERSION" "$NEXT_VERSION" "$IS_PATCH" git tag "v${NEXT_VERSION}" } fn-repo-update-history-and-commit() { declare desc="Updates the history file and commits the changes" - declare CURRENT_VERSION="$1" NEXT_VERSION="$2" + declare CURRENT_VERSION="$1" NEXT_VERSION="$2" IS_PATCH="$3" local PULL_REQUEST_ID TITLE AUTHOR TYPE CHANGELOG_TEXT local COMMIT_MESSAGE HISTORY HISTORY_CONTENTS HISTORY_BUG HISTORY_DEPENDENCY HISTORY_DEPRECATION HISTORY_DOCUMENTATION HISTORY_ENHANCEMENT HISTORY_BC_BREAK HISTORY_REFACTOR HISTORY_REMOVAL HISTORY_OTHER HISTORY_TESTS ISSUE_FILE @@ -252,7 +256,7 @@ fn-repo-update-history-and-commit() { mkdir -p /tmp/github-data - for PULL_REQUEST_ID in $(fn-repo-merged-requests "$CURRENT_VERSION"); do + for PULL_REQUEST_ID in $(fn-repo-merged-requests "$CURRENT_VERSION" "$IS_PATCH"); do ISSUE_FILE="/tmp/github-data/${PULL_REQUEST_ID}.json" if [[ ! -f "$ISSUE_FILE" ]]; then while true; do @@ -399,7 +403,7 @@ fn-build-docker-image() { main() { declare RELEASE="$1" - local CURRENT_VERSION NEXT_VERSION IS_RELEASE + local CURRENT_VERSION NEXT_VERSION IS_PATCH IS_RELEASE local VALID_RELEASE_LEVELS=("major" "minor" "patch" "betafish" "build") if [[ "$RELEASE" == '--trace' ]]; then @@ -430,9 +434,14 @@ main() { NEXT_VERSION="$(fn-version-next "$CURRENT_VERSION" "$RELEASE")" IS_RELEASE="$(fn-is-release "$RELEASE")" + IS_PATCH="false" + if [[ "$RELEASE" == "patch" ]]; then + IS_PATCH="true" + fi + echo "v${NEXT_VERSION}" >build/next-version - fn-repo-update "$IS_RELEASE" "$CURRENT_VERSION" "$NEXT_VERSION" + fn-repo-update "$IS_RELEASE" "$CURRENT_VERSION" "$NEXT_VERSION" "$IS_PATCH" fn-build-dokku "$IS_RELEASE" "$NEXT_VERSION" || log-fail "Error building package" fn-extract-package "$IS_RELEASE" "dokku_${NEXT_VERSION}_armhf.deb" || log-fail "Error extracting deb package" fn-extract-package "$IS_RELEASE" "dokku_${NEXT_VERSION}_arm64.deb" || log-fail "Error extracting deb package" From 24d94576a94b069eb4cd2788c7bd6dcf5efb8257 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Tue, 8 Aug 2023 23:02:50 -0400 Subject: [PATCH 58/61] feat: make heroku-22/jammy the default stack for cnb/herokuish builds --- Makefile | 2 +- debian/control | 2 +- docs/appendices/0.31.0-migration-guide.md | 3 +++ .../builders/cloud-native-buildpacks.md | 4 ++-- docs/deployment/builders/herokuish-buildpacks.md | 16 ++++++++-------- dokku | 4 ++-- tests/apps/dockerfile/app.json | 2 +- tests/apps/multi/Gemfile | 2 +- tests/apps/multi/Gemfile.lock | 6 +++--- tests/apps/nodejs-express/app.json | 2 +- tests/apps/python/cnb/1/buildpack.toml | 2 +- tests/apps/python/cnb/2/buildpack.toml | 2 +- tests/unit/builder-pack.bats | 11 +++++------ 13 files changed, 30 insertions(+), 28 deletions(-) diff --git a/Makefile b/Makefile index 19455ec34..7c3e16e66 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ PROCFILE_UTIL_URL ?= https://github.com/josegonzalez/go-procfile-util/releases/d SIGIL_URL ?= https://github.com/gliderlabs/sigil/releases/download/v${SIGIL_VERSION}/gliderlabs-sigil_${SIGIL_VERSION}_linux_${TARGETARCH}.tgz SSHCOMMAND_URL ?= https://github.com/dokku/sshcommand/releases/download/v${SSHCOMMAND_VERSION}/sshcommand_${SSHCOMMAND_VERSION}_linux_x86_64.tgz STACK_URL ?= https://github.com/gliderlabs/herokuish.git -PREBUILT_STACK_URL ?= gliderlabs/herokuish:latest-20 +PREBUILT_STACK_URL ?= gliderlabs/herokuish:latest-22 DOKKU_LIB_ROOT ?= /var/lib/dokku PLUGINS_PATH ?= ${DOKKU_LIB_ROOT}/plugins CORE_PLUGINS_PATH ?= ${DOKKU_LIB_ROOT}/core-plugins diff --git a/debian/control b/debian/control index df54edcc9..2d9dba1e8 100644 --- a/debian/control +++ b/debian/control @@ -4,7 +4,7 @@ Section: web Priority: optional Architecture: amd64 Depends: apache2-utils, locales, git, cpio, curl, man-db, netcat, sshcommand (>= 0.12.0), docker-engine-cs (>= 17.05.0) | docker-engine (>= 17.05.0) | docker-io (>= 17.05.0) | docker.io (>= 17.05.0) | docker-ce (>= 17.05.0) | docker-ee (>= 17.05.0) | moby-engine, docker-compose-plugin | moby-compose, docker-container-healthchecker (>= 0.6.4), docker-image-labeler (>= 0.2.2), lambda-builder, net-tools, netrc, software-properties-common, parallel, procfile-util (>= 0.11.0), python-software-properties | python3-software-properties, rsync, rsyslog, dos2unix, jq, unzip -Recommends: herokuish (>= 0.3.4), bash-completion, dokku-update, dokku-event-listener +Recommends: herokuish (>= 0.7.0), bash-completion, dokku-update, dokku-event-listener Pre-Depends: gliderlabs-sigil, nginx (>= 1.8.0) | openresty, dnsutils, cgroupfs-mount | cgroup-lite, plugn (>= 0.3.0), sudo, python3, debconf Maintainer: Jose Diaz-Gonzalez Description: Docker-powered PaaS that helps build and manage the lifecycle of applications diff --git a/docs/appendices/0.31.0-migration-guide.md b/docs/appendices/0.31.0-migration-guide.md index ecb871bb8..ae2455b86 100644 --- a/docs/appendices/0.31.0-migration-guide.md +++ b/docs/appendices/0.31.0-migration-guide.md @@ -9,6 +9,9 @@ - For deploys via the `git:from-image` and `git:load-image` commands, the `CHECKS` file is now extracted from the configured `WORKDIR` property of the image. For all other deploys - git push, `git:from-archive`, `git:sync` - will have the `CHECKS` extracted directly from the source code. The filename in both cases is `CHECKS` and cannot be modified. - Port mappings are now auto-detected during the build process. Users may override detected port mappings via the `ports` plugin. A default port mapping of `http:80:5000` will be used if none is otherwise specified. - Users building docker images that run Dokku will need to use a new sudoer wrapper for the `docker-container-healthchecker` binary to work correctly. A reference version has been placed in the `docker` skeleton directory. This should only impact platform developers, and users of our Docker image will already have the file available. +- The default image used for Herokuish Buildpack builds is now `gliderlabs/herokuish:latest-22`, and is based on both the `heroku-22` stack as well as Ubuntu 22.04. Users that wish to stick with the old, `heroku-20`/Ubuntu 20.04 builder may specify `gliderlabs/herokuish:latest-20` as their builder image. Please see the [herokuish buildpack deployment documentation](/docs/deployment/builders/herokuish-buildpacks.md#customizing-the-buildpack-stack-builder) for more information on how to specify a custom buildpack stack builder. +- The default image used for Cloud Native Buildpack builds is now `heroku/builder:22`, and is based on both the `heroku-22` stack as well as Ubuntu 22.04. Users that wish to stick with the old, `heroku-20`/Ubuntu 20.04 builder may specify `heroku/buildpacks:20` as their builder image. Please see the [herokuish buildpack deployment documentation](/docs/deployment/builders/herokuish-buildpacks.md#customizing-the-buildpack-stack-builder) for more information on how to specify a custom buildpack stack builder. + ## Deprecations diff --git a/docs/deployment/builders/cloud-native-buildpacks.md b/docs/deployment/builders/cloud-native-buildpacks.md index 23d5c6ab9..0e300ede4 100644 --- a/docs/deployment/builders/cloud-native-buildpacks.md +++ b/docs/deployment/builders/cloud-native-buildpacks.md @@ -136,7 +136,7 @@ project2.toml > New as of 0.23.0 -The default stack builder in use by CNB buildpacks in Dokku is based on `heroku/buildpacks`. Users may desire to switch the stack builder to a custom version, either to update the operating system or to customize packages included with the stack builder. This can be performed via the `buildpacks:set-property` command. +The default stack builder in use by CNB buildpacks in Dokku is based on `heroku/builder:22`. Users may desire to switch the stack builder to a custom version, either to update the operating system or to customize packages included with the stack builder. This can be performed via the `buildpacks:set-property` command. ```shell dokku buildpacks:set-property node-js-app stack paketobuildpacks/build:base-cnb @@ -150,7 +150,7 @@ dokku buildpacks:set-property node-js-app stack A change in the stack builder value will execute the `post-stack-set` trigger. -Finally, stack builders can be set or unset globally as a fallback. This will take precedence over a globally set `DOKKU_CNB_BUILDER` environment variable (`heroku/buildpacks` by default). +Finally, stack builders can be set or unset globally as a fallback. This will take precedence over a globally set `DOKKU_CNB_BUILDER` environment variable (`heroku/builder:22` by default). ```shell # set globally diff --git a/docs/deployment/builders/herokuish-buildpacks.md b/docs/deployment/builders/herokuish-buildpacks.md index 66ca72ec3..60b295f65 100644 --- a/docs/deployment/builders/herokuish-buildpacks.md +++ b/docs/deployment/builders/herokuish-buildpacks.md @@ -163,7 +163,7 @@ dokku buildpacks:set-property node-js-app stack A change in the stack builder value will execute the `post-stack-set` trigger. -Finally, stack builders can be set or unset globally as a fallback. This will take precedence over a globally set `DOKKU_IMAGE` environment variable (`gliderlabs/herokuish:latest-20` by default). +Finally, stack builders can be set or unset globally as a fallback. This will take precedence over a globally set `DOKKU_IMAGE` environment variable (`gliderlabs/herokuish:latest-22` by default). ```shell # set globally @@ -213,18 +213,18 @@ dokku buildpacks:report ``` =====> node-js-app buildpacks information - Buildpacks computed stack: gliderlabs/herokuish:v0.5.23-20 - Buildpacks global stack: gliderlabs/herokuish:latest-20 + Buildpacks computed stack: gliderlabs/herokuish:v0.7.0-22 + Buildpacks global stack: gliderlabs/herokuish:latest-22 Buildpacks list: https://github.com/heroku/heroku-buildpack-nodejs.git - Buildpacks stack: gliderlabs/herokuish:v0.5.23-20 + Buildpacks stack: gliderlabs/herokuish:v0.7.0-20 =====> python-sample buildpacks information - Buildpacks computed stack: gliderlabs/herokuish:latest-20 - Buildpacks global stack: gliderlabs/herokuish:latest-20 + Buildpacks computed stack: gliderlabs/herokuish:latest-22 + Buildpacks global stack: gliderlabs/herokuish:latest-22 Buildpacks list: https://github.com/heroku/heroku-buildpack-nodejs.git,https://github.com/heroku/heroku-buildpack-python.git Buildpacks stack: =====> ruby-sample buildpacks information - Buildpacks computed stack: gliderlabs/herokuish:latest-20 - Buildpacks global stack: gliderlabs/herokuish:latest-20 + Buildpacks computed stack: gliderlabs/herokuish:latest-22 + Buildpacks global stack: gliderlabs/herokuish:latest-22 Buildpacks list: Buildpacks stack: ``` diff --git a/dokku b/dokku index ce657b8a1..fdef028b2 100755 --- a/dokku +++ b/dokku @@ -39,8 +39,8 @@ DOKKU_DISTRO=$( # configuration can ever override the DOCKER_BIN value export DOCKER_BIN=${DOCKER_BIN:="docker"} -export DOKKU_IMAGE=${DOKKU_IMAGE:="gliderlabs/herokuish:latest-20"} -export DOKKU_CNB_BUILDER=${DOKKU_CNB_BUILDER:="heroku/buildpacks:20"} +export DOKKU_IMAGE=${DOKKU_IMAGE:="gliderlabs/herokuish:latest-22"} +export DOKKU_CNB_BUILDER=${DOKKU_CNB_BUILDER:="heroku/builder:22"} export DOKKU_LIB_ROOT=${DOKKU_LIB_PATH:="/var/lib/dokku"} export PLUGIN_PATH=${PLUGIN_PATH:="$DOKKU_LIB_ROOT/plugins"} diff --git a/tests/apps/dockerfile/app.json b/tests/apps/dockerfile/app.json index ea96d901c..6c39f8703 100644 --- a/tests/apps/dockerfile/app.json +++ b/tests/apps/dockerfile/app.json @@ -25,7 +25,7 @@ "generator": "echo 5" } }, - "image": "gliderlabs/herokuish:latest-20", + "image": "gliderlabs/herokuish:latest-22", "addons": [ "dokku-postgres", "dokku-redis" diff --git a/tests/apps/multi/Gemfile b/tests/apps/multi/Gemfile index df0465167..8c6c3a47c 100644 --- a/tests/apps/multi/Gemfile +++ b/tests/apps/multi/Gemfile @@ -1,6 +1,6 @@ source "https://rubygems.org" -ruby '2.7.5' +ruby '3.1.4' gem 'compass', ">= 1.0.1" gem 'sass-globbing' diff --git a/tests/apps/multi/Gemfile.lock b/tests/apps/multi/Gemfile.lock index 20c4333b1..c4f33b77e 100644 --- a/tests/apps/multi/Gemfile.lock +++ b/tests/apps/multi/Gemfile.lock @@ -16,7 +16,7 @@ GEM sass (>= 3.2, < 3.5) ffi (1.15.5) multi_json (1.15.0) - rb-fsevent (0.11.1) + rb-fsevent (0.11.2) rb-inotify (0.10.1) ffi (~> 1.0) sass (3.4.25) @@ -31,7 +31,7 @@ DEPENDENCIES sass-globbing RUBY VERSION - ruby 2.7.5p203 + ruby 3.1.4p223 BUNDLED WITH - 2.1.4 + 2.3.26 diff --git a/tests/apps/nodejs-express/app.json b/tests/apps/nodejs-express/app.json index aad3fd590..918760d5f 100644 --- a/tests/apps/nodejs-express/app.json +++ b/tests/apps/nodejs-express/app.json @@ -25,7 +25,7 @@ "generator": "echo 5" } }, - "image": "gliderlabs/herokuish:latest-20", + "image": "gliderlabs/herokuish:latest-22", "addons": [ "dokku-postgres", "dokku-redis" diff --git a/tests/apps/python/cnb/1/buildpack.toml b/tests/apps/python/cnb/1/buildpack.toml index f89645558..9c0af09cf 100644 --- a/tests/apps/python/cnb/1/buildpack.toml +++ b/tests/apps/python/cnb/1/buildpack.toml @@ -6,4 +6,4 @@ version = "0.1.0" name = "1 Packages Buildpack" [[stacks]] -id = "heroku-20" +id = "heroku-22" diff --git a/tests/apps/python/cnb/2/buildpack.toml b/tests/apps/python/cnb/2/buildpack.toml index c15695e03..93b5e2955 100644 --- a/tests/apps/python/cnb/2/buildpack.toml +++ b/tests/apps/python/cnb/2/buildpack.toml @@ -6,4 +6,4 @@ version = "0.1.0" name = "2 Packages Buildpack" [[stacks]] -id = "heroku-20" +id = "heroku-22" diff --git a/tests/unit/builder-pack.bats b/tests/unit/builder-pack.bats index 84918aba9..bbcc7589b 100644 --- a/tests/unit/builder-pack.bats +++ b/tests/unit/builder-pack.bats @@ -25,7 +25,7 @@ teardown() { echo "status: $status" assert_success - run /bin/bash -c "dokku buildpacks:set-property $TEST_APP stack heroku/buildpacks:20" + run /bin/bash -c "dokku buildpacks:set-property $TEST_APP stack heroku/builder:22" echo "output: $output" echo "status: $status" assert_success @@ -35,8 +35,7 @@ teardown() { echo "status: $status" assert_success assert_output_contains 'Building with buildpack 1' 0 - assert_output_contains 'Installing requirements with pip' - assert_output_contains "Build time env var SECRET_KEY=fjdkslafjdk" + assert_output_contains 'Installing dependencies using Pip' run /bin/bash -c "dokku builder-pack:set $TEST_APP projecttoml-path nonexistent.toml" echo "output: $output" @@ -48,7 +47,7 @@ teardown() { echo "status: $status" assert_success assert_output_contains 'Building with buildpack 1' 0 - assert_output_contains 'Installing requirements with pip' + assert_output_contains 'Installing dependencies using Pip' run /bin/bash -c "dokku builder-pack:set $TEST_APP projecttoml-path project2.toml" echo "output: $output" @@ -60,7 +59,7 @@ teardown() { echo "status: $status" assert_success assert_output_contains 'Building with buildpack 1' - assert_output_contains 'Installing requirements with pip' 0 + assert_output_contains 'Installing dependencies using Pip' 0 run /bin/bash -c "dokku builder-pack:set $TEST_APP projecttoml-path" echo "output: $output" @@ -72,7 +71,7 @@ teardown() { echo "status: $status" assert_success assert_output_contains 'Building with buildpack 1' 0 - assert_output_contains 'Installing requirements with pip' + assert_output_contains 'Installing dependencies using Pip' } @test "(builder-pack) git:from-image without a Procfile" { From 1ee0fc7dc51ead7802fdfe95372be3917ce8a8fc Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 9 Aug 2023 13:53:14 -0400 Subject: [PATCH 59/61] docs: remove extra newline --- docs/appendices/0.31.0-migration-guide.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/appendices/0.31.0-migration-guide.md b/docs/appendices/0.31.0-migration-guide.md index ae2455b86..a5636568b 100644 --- a/docs/appendices/0.31.0-migration-guide.md +++ b/docs/appendices/0.31.0-migration-guide.md @@ -12,7 +12,6 @@ - The default image used for Herokuish Buildpack builds is now `gliderlabs/herokuish:latest-22`, and is based on both the `heroku-22` stack as well as Ubuntu 22.04. Users that wish to stick with the old, `heroku-20`/Ubuntu 20.04 builder may specify `gliderlabs/herokuish:latest-20` as their builder image. Please see the [herokuish buildpack deployment documentation](/docs/deployment/builders/herokuish-buildpacks.md#customizing-the-buildpack-stack-builder) for more information on how to specify a custom buildpack stack builder. - The default image used for Cloud Native Buildpack builds is now `heroku/builder:22`, and is based on both the `heroku-22` stack as well as Ubuntu 22.04. Users that wish to stick with the old, `heroku-20`/Ubuntu 20.04 builder may specify `heroku/buildpacks:20` as their builder image. Please see the [herokuish buildpack deployment documentation](/docs/deployment/builders/herokuish-buildpacks.md#customizing-the-buildpack-stack-builder) for more information on how to specify a custom buildpack stack builder. - ## Deprecations - The `proxy:ports*` commands have been replaced with the new `ports` plugin. Users will be able to use the old `proxy:ports*` commands for a single minor release, and they will be removed in the next minor release. From 038b76858538d41eedeab7bb30aa198d36e65af4 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 13 Aug 2023 21:54:25 -0400 Subject: [PATCH 60/61] fix: correct version --- plugins/openresty-vhosts/plugin.toml | 2 +- plugins/ports/plugin.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/openresty-vhosts/plugin.toml b/plugins/openresty-vhosts/plugin.toml index d02688cf1..f5898437d 100644 --- a/plugins/openresty-vhosts/plugin.toml +++ b/plugins/openresty-vhosts/plugin.toml @@ -1,4 +1,4 @@ [plugin] description = "dokku core openresty-vhosts plugin" -version = "0.30.10" +version = "0.30.11" [plugin.config] diff --git a/plugins/ports/plugin.toml b/plugins/ports/plugin.toml index a43ba8fb5..a596e24c9 100644 --- a/plugins/ports/plugin.toml +++ b/plugins/ports/plugin.toml @@ -1,4 +1,4 @@ [plugin] description = "dokku core ports plugin" -version = "0.30.8" +version = "0.30.11" [plugin.config] From 00d02121d99b0a7b51dfde5190e6f72b9ec81a9b Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 13 Aug 2023 22:00:39 -0400 Subject: [PATCH 61/61] fix: pull 100 results when fetching PRs per milestone --- contrib/release-dokku | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/release-dokku b/contrib/release-dokku index 1c740445d..c63fdc8eb 100755 --- a/contrib/release-dokku +++ b/contrib/release-dokku @@ -187,7 +187,7 @@ fn-repo-merged-requests() { if [[ "$IS_PATCH" == "true" ]]; then git log "v${TAG}..HEAD" --oneline | grep "Merge pull request" | cut -d' ' -f 5 | cut -d '#' -f2 else - curl -u "$RELEASE_GITHUB_USERNAME:$RELEASE_GITHUB_API_TOKEN" --fail -sS "https://api.github.com/search/issues?q=repo:dokku/dokku+milestone:v${NEXT_VERSION}+is:pr" | jq '.items[].number' + curl -u "$RELEASE_GITHUB_USERNAME:$RELEASE_GITHUB_API_TOKEN" --fail -sS "https://api.github.com/search/issues?q=repo:dokku/dokku+milestone:v${NEXT_VERSION}+is:pr&per_page=100" | jq '.items[].number' fi }