diff --git a/docs/appendices/0.36.0-migration-guide.md b/docs/appendices/0.36.0-migration-guide.md new file mode 100644 index 000000000..2a79b6668 --- /dev/null +++ b/docs/appendices/0.36.0-migration-guide.md @@ -0,0 +1,6 @@ +# 0.35.0 Migration Guide + +## Changes + +- Process stop timeouts are now configured via the `ps` property `stop-timeout-seconds`. Existing `DOKKU_DOCKER_STOP_TIMEOUT` environment variables will be automatically migrated to the new value. +- Processes now default to a `30` second stop timeout. \ No newline at end of file diff --git a/docs/configuration/environment-variables.md b/docs/configuration/environment-variables.md index 3734d44c6..047571c22 100644 --- a/docs/configuration/environment-variables.md +++ b/docs/configuration/environment-variables.md @@ -112,7 +112,6 @@ The following config variables have special meanings and can be set in a variety | `DOKKU_DISABLE_PROXY` | none | `dokku proxy:disable`
`dokku proxy:enable` | Disables the proxy in front of your application, resulting in publicly routing the docker container. | | `DOKKU_DISABLE_ANSI_PREFIX_REMOVAL` | none | `dokku config:set`
`/etc/environment`
`~dokku/.dokkurc`
`~dokku/.dokkurc/*` | Disables removal of the ANSI prefix during deploys. Can be used in cases where the client deployer does not understand ansi escape codes. | | `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_START_CMD` | none | `dokku config:set` | | | `DOKKU_PARALLEL_ARGUMENTS`. | none | `dokku config:set` | Allows passing custom arguments to parallel for `ps:*all` commands | diff --git a/docs/deployment/zero-downtime-deploys.md b/docs/deployment/zero-downtime-deploys.md index b811b2d1e..e769b2193 100644 --- a/docs/deployment/zero-downtime-deploys.md +++ b/docs/deployment/zero-downtime-deploys.md @@ -36,12 +36,15 @@ dokku checks:set node-js-app wait-to-retire 30 Defaults to `60`. +## Setting stop grace period + +You can set the `stop-timeout-seconds` property on the `ps` plugin to change this value (default: `30`). See the [process management documentation](/docs/processes/process-management.md#changing-process-management-settings) for more information. + ## Configuring check settings using the `config` plugin 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 `app.json` file, though are available as environment variables in order to ease application reuse. diff --git a/docs/processes/process-management.md b/docs/processes/process-management.md index abc64ebac..86b80982e 100644 --- a/docs/processes/process-management.md +++ b/docs/processes/process-management.md @@ -107,6 +107,38 @@ proctype: qty web: 1 ``` +### Changing process management settings + +The `ps` plugin provides a number of settings that can be used to managed deployments on a per-app basis. The following table outlines ones not covered elsewhere: + +| Name | Description | Global Default | +|-----------------------|---------------------------------------------------|--------------------| +| `stop-timeout` | 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. | `30` | + +All settings can be set via the `ps:set` command. Using `stop-timeout` as an example: + +```shell +dokku ps:set node-js-app stop-timeout 60 +``` + +The default value may be set by passing an empty value for the option in question: + +```shell +dokku ps:set node-js-app stop-timeout +``` + +Properties can also be set globally. If not set for an app, the global value will apply. + +```shell +dokku ps:set --global stop-timeout 60 +``` + +The global default value may be set by passing an empty value for the option. + +```shell +dokku ps:set --global stop-timeout +``` + ### Defining Processes #### Procfile diff --git a/plugins/config/Makefile b/plugins/config/Makefile index 86ffcb36b..0cf396bd8 100644 --- a/plugins/config/Makefile +++ b/plugins/config/Makefile @@ -1,6 +1,6 @@ GOARCH ?= amd64 SUBCOMMANDS = subcommands/bundle subcommands/clear subcommands/export subcommands/get subcommands/keys subcommands/show subcommands/set subcommands/unset -TRIGGERS = triggers/config-export triggers/config-get triggers/config-get-global triggers/post-app-clone-setup triggers/post-app-rename-setup +TRIGGERS = triggers/config-export triggers/config-get triggers/config-get-global triggers/config-unset triggers/post-app-clone-setup triggers/post-app-rename-setup BUILD = commands config_sub subcommands triggers PLUGIN_NAME = config diff --git a/plugins/config/src/triggers/triggers.go b/plugins/config/src/triggers/triggers.go index 1f8f58732..96e95d455 100644 --- a/plugins/config/src/triggers/triggers.go +++ b/plugins/config/src/triggers/triggers.go @@ -24,7 +24,7 @@ func main() { global := flag.Arg(1) merged := flag.Arg(2) format := flag.Arg(3) - config.TriggerConfigExport(appName, global, merged, format) + err = config.TriggerConfigExport(appName, global, merged, format) case "config-get": appName := flag.Arg(0) key := flag.Arg(1) @@ -36,6 +36,16 @@ func main() { case "config-get-global": key := flag.Arg(0) err = config.TriggerConfigGetGlobal(key) + case "config-unset": + appName := flag.Arg(0) + key := flag.Arg(1) + restart := flag.Arg(2) + if *global { + appName = "--global" + key = flag.Arg(0) + restart = flag.Arg(1) + } + err = config.TriggerConfigUnset(appName, key, common.ToBool(restart)) case "post-app-clone-setup": oldAppName := flag.Arg(0) newAppName := flag.Arg(1) diff --git a/plugins/config/triggers.go b/plugins/config/triggers.go index af96d114c..b0c3a57f3 100644 --- a/plugins/config/triggers.go +++ b/plugins/config/triggers.go @@ -45,6 +45,12 @@ func TriggerConfigGetGlobal(key string) error { return nil } +// TriggerConfigUnset unsets an app config value by key +func TriggerConfigUnset(appName string, key string, restart bool) error { + UnsetMany(appName, []string{key}, restart) + return nil +} + // TriggerPostAppCloneSetup creates new buildpacks files func TriggerPostAppCloneSetup(oldAppName string, newAppName string) error { oldEnv, err := LoadAppEnv(oldAppName) diff --git a/plugins/ports/triggers.go b/plugins/ports/triggers.go index 4391170f1..bb4e7a83d 100644 --- a/plugins/ports/triggers.go +++ b/plugins/ports/triggers.go @@ -25,8 +25,16 @@ func TriggerInstall() error { continue } - common.LogInfo1(fmt.Sprintf("Migrating DOKKU_PROXY_PORT_MAP to ports map property for %s", appName)) - portMapString := config.GetWithDefault(appName, "DOKKU_PROXY_PORT_MAP", "") + results, _ := common.CallPlugnTrigger(common.PlugnTriggerInput{ + Trigger: "config-get", + Args: []string{appName, "DOKKU_PROXY_PORT_MAP"}, + }) + portMapString := results.StdoutContents() + if portMapString == "" { + continue + } + + common.LogVerboseQuiet(fmt.Sprintf("Setting %s ports property 'map' to %v", appName, portMapString)) portMaps, _ := parsePortMapString(portMapString) propertyValue := []string{} @@ -38,7 +46,11 @@ func TriggerInstall() error { return err } - if err := config.UnsetMany(appName, []string{"DOKKU_PROXY_PORT_MAP"}, false); err != nil { + _, err := common.CallPlugnTrigger(common.PlugnTriggerInput{ + Trigger: "config-unset", + Args: []string{appName, "DOKKU_PROXY_PORT_MAP"}, + }) + if err != nil { return err } } diff --git a/plugins/ps/Makefile b/plugins/ps/Makefile index 8e8457fc4..3f8016f33 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-release-builder triggers/post-stop triggers/procfile-get-command triggers/procfile-exists 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-release-builder triggers/post-stop triggers/procfile-get-command triggers/procfile-exists triggers/ps-can-scale triggers/ps-current-scale triggers/ps-get-property triggers/ps-set-scale triggers/report BUILD = commands subcommands triggers PLUGIN_NAME = ps diff --git a/plugins/ps/ps.go b/plugins/ps/ps.go index 7e1e2f4d9..307306f24 100644 --- a/plugins/ps/ps.go +++ b/plugins/ps/ps.go @@ -13,13 +13,15 @@ const RunInSerial = 0 var ( // DefaultProperties is a map of all valid ps properties with corresponding default property values DefaultProperties = map[string]string{ - "restart-policy": "on-failure:10", - "procfile-path": "", + "restart-policy": "on-failure:10", + "procfile-path": "", + "stop-timeout-seconds": "30", } // GlobalProperties is a map of all valid global ps properties GlobalProperties = map[string]bool{ - "procfile-path": true, + "procfile-path": true, + "stop-timeout-seconds": true, } ) diff --git a/plugins/ps/report.go b/plugins/ps/report.go index 5e0ddfe40..74c4c17a9 100644 --- a/plugins/ps/report.go +++ b/plugins/ps/report.go @@ -15,15 +15,18 @@ func ReportSingleApp(appName string, format string, infoFlag string) error { } flags := map[string]common.ReportFunc{ - "--deployed": reportDeployed, - "--processes": reportProcesses, - "--ps-can-scale": reportCanScale, - "--ps-restart-policy": reportRestartPolicy, - "--ps-computed-procfile-path": reportComputedProcfilePath, - "--ps-global-procfile-path": reportGlobalProcfilePath, - "--ps-procfile-path": reportProcfilePath, - "--restore": reportRestore, - "--running": reportRunningState, + "--deployed": reportDeployed, + "--processes": reportProcesses, + "--ps-can-scale": reportCanScale, + "--ps-restart-policy": reportRestartPolicy, + "--ps-computed-procfile-path": reportComputedProcfilePath, + "--ps-global-procfile-path": reportGlobalProcfilePath, + "--ps-procfile-path": reportProcfilePath, + "--restore": reportRestore, + "--running": reportRunningState, + "--global-stop-timeout-seconds": reportGlobalStopTimeoutSeconds, + "--computed-stop-timeout-seconds": reportComputedStopTimeoutSeconds, + "--stop-timeout-seconds": reportStopTimeoutSeconds, } extraFlags := addStatusFlags(appName, infoFlag) @@ -146,3 +149,20 @@ func reportRestore(appName string) string { func reportRunningState(appName string) string { return getRunningState(appName) } + +func reportComputedStopTimeoutSeconds(appName string) string { + value := reportStopTimeoutSeconds(appName) + if value == "" { + value = reportGlobalStopTimeoutSeconds(appName) + } + + return value +} + +func reportGlobalStopTimeoutSeconds(appName string) string { + return common.PropertyGetDefault("ps", "--global", "stop-timeout-seconds", "30") +} + +func reportStopTimeoutSeconds(appName string) string { + return common.PropertyGetDefault("ps", appName, "stop-timeout-seconds", "30") +} diff --git a/plugins/ps/src/triggers/triggers.go b/plugins/ps/src/triggers/triggers.go index 4d2821dad..3ec4beaec 100644 --- a/plugins/ps/src/triggers/triggers.go +++ b/plugins/ps/src/triggers/triggers.go @@ -75,8 +75,11 @@ func main() { case "ps-current-scale": appName := flag.Arg(0) err = ps.TriggerPsCurrentScale(appName) - case "ps-set-scale": + case "ps-get-property": appName := flag.Arg(0) + property := flag.Arg(1) + err = ps.TriggerPsGetProperty(appName, property) + case "ps-set-scale": appName, args := common.ShiftString(flag.Args()) skipDeploy, args := common.ShiftString(args) clearExisting, processTuples := common.ShiftString(args) diff --git a/plugins/ps/triggers.go b/plugins/ps/triggers.go index 674369db5..90a4e001b 100644 --- a/plugins/ps/triggers.go +++ b/plugins/ps/triggers.go @@ -164,8 +164,29 @@ func TriggerInstall() error { if common.FileExists(dokkuScaleExtracted) { os.Remove(dokkuScaleExtracted) } - } + results, _ := common.CallPlugnTrigger(common.PlugnTriggerInput{ + Trigger: "config-get", + Args: []string{appName, "DOKKU_DOCKER_STOP_TIMEOUT"}, + }) + stopTimeout := results.StdoutContents() + if stopTimeout == "" { + continue + } + + common.LogVerboseQuiet(fmt.Sprintf("Setting %s ps property 'stop-timeout-seconds' to %v", appName, stopTimeout)) + if err := common.PropertyWrite("ps", appName, "stop-timeout-seconds", stopTimeout); err != nil { + return err + } + + _, err := common.CallPlugnTrigger(common.PlugnTriggerInput{ + Trigger: "config-unset", + Args: []string{appName, "DOKKU_DOCKER_STOP_TIMEOUT"}, + }) + if err != nil { + return err + } + } return nil } @@ -320,3 +341,17 @@ func TriggerPsCurrentScale(appName string) error { func TriggerPsSetScale(appName string, skipDeploy bool, clearExisting bool, processTuples []string) error { return scaleSet(appName, skipDeploy, clearExisting, processTuples) } + +func TriggerPsGetProperty(appName string, property string) error { + computedValueMap := map[string]common.ReportFunc{ + "stop-timeout-seconds": reportComputedStopTimeoutSeconds, + } + + fn, ok := computedValueMap[property] + if !ok { + return fmt.Errorf("Invalid network property specified: %v", property) + } + + fmt.Println(fn(appName)) + return nil +} diff --git a/plugins/scheduler-docker-local/internal-functions b/plugins/scheduler-docker-local/internal-functions index ebce46b95..d0cfdb061 100755 --- a/plugins/scheduler-docker-local/internal-functions +++ b/plugins/scheduler-docker-local/internal-functions @@ -114,7 +114,7 @@ fn-scheduler-docker-local-retire-container() { return fi - DOKKU_DOCKER_STOP_TIMEOUT="$(config_get "$APP" DOKKU_DOCKER_STOP_TIMEOUT || true)" + DOKKU_DOCKER_STOP_TIMEOUT="$(plugn trigger ps-get-property "$APP" stop-timeout-seconds || true)" [[ $DOKKU_DOCKER_STOP_TIMEOUT ]] && DOCKER_STOP_TIME_ARG="--time=${DOKKU_DOCKER_STOP_TIMEOUT}" if [[ "$STATE" == "restarting" ]]; then diff --git a/plugins/scheduler-docker-local/scheduler-deploy b/plugins/scheduler-docker-local/scheduler-deploy index 3192c0e63..e7e9f4adf 100755 --- a/plugins/scheduler-docker-local/scheduler-deploy +++ b/plugins/scheduler-docker-local/scheduler-deploy @@ -32,7 +32,7 @@ trigger-scheduler-docker-local-scheduler-deploy() { local oldids=$(get_app_container_ids "$APP" "$PROCESS_TYPE") DOKKU_NETWORK_BIND_ALL="$(plugn trigger network-get-property "$APP" bind-all-interfaces)" - DOKKU_DOCKER_STOP_TIMEOUT="$(config_get "$APP" DOKKU_DOCKER_STOP_TIMEOUT || true)" + DOKKU_DOCKER_STOP_TIMEOUT="$(plugn trigger ps-get-property "$APP" stop-timeout-seconds)" [[ $DOKKU_DOCKER_STOP_TIMEOUT ]] && DOCKER_STOP_TIME_ARG="--time=${DOKKU_DOCKER_STOP_TIMEOUT}" DOKKU_START_CMD="$(config_get "$APP" DOKKU_START_CMD || true)" diff --git a/plugins/scheduler-docker-local/scheduler-run-stop b/plugins/scheduler-docker-local/scheduler-run-stop index d46f9ad72..d72015f38 100755 --- a/plugins/scheduler-docker-local/scheduler-run-stop +++ b/plugins/scheduler-docker-local/scheduler-run-stop @@ -51,7 +51,7 @@ trigger-scheduler-docker-local-scheduler-run-stop() { verify_app_name "$APP" - DOKKU_DOCKER_STOP_TIMEOUT="$(config_get "$APP" DOKKU_DOCKER_STOP_TIMEOUT || true)" + DOKKU_DOCKER_STOP_TIMEOUT="$(plugn trigger ps-get-property "$APP" stop-timeout-seconds || true)" if [[ -n "$CONTAINER_NAME" ]]; then fn-scheduler-docker-local-stop-container "$CONTAINER_NAME" "$DOKKU_DOCKER_STOP_TIMEOUT" return $? diff --git a/plugins/scheduler-docker-local/scheduler-stop b/plugins/scheduler-docker-local/scheduler-stop index 88dd43091..8e336e95b 100755 --- a/plugins/scheduler-docker-local/scheduler-stop +++ b/plugins/scheduler-docker-local/scheduler-stop @@ -14,7 +14,7 @@ trigger-scheduler-docker-local-scheduler-stop() { fi local DOKKU_APP_RUNNING_CONTAINER_IDS=$(get_app_running_container_ids "$APP" 2>/dev/null) - local DOKKU_DOCKER_STOP_TIMEOUT="$(config_get "$APP" DOKKU_DOCKER_STOP_TIMEOUT || true)" + local DOKKU_DOCKER_STOP_TIMEOUT="$(plugn trigger ps-get-property "$APP" stop-timeout-seconds || true)" [[ -n "$DOKKU_DOCKER_STOP_TIMEOUT" ]] && DOCKER_STOP_TIME_ARG="--time=${DOKKU_DOCKER_STOP_TIMEOUT}" diff --git a/plugins/scheduler/go.mod b/plugins/scheduler/go.mod index 938db56c5..c5742cd6b 100644 --- a/plugins/scheduler/go.mod +++ b/plugins/scheduler/go.mod @@ -4,7 +4,6 @@ go 1.23.0 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.6 ) @@ -13,7 +12,6 @@ require ( github.com/fatih/color v1.18.0 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/joho/godotenv v1.2.0 // indirect github.com/kr/fs v0.1.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect diff --git a/plugins/scheduler/go.sum b/plugins/scheduler/go.sum index 12803b5db..b61585913 100644 --- a/plugins/scheduler/go.sum +++ b/plugins/scheduler/go.sum @@ -10,8 +10,6 @@ github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/U github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -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/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= diff --git a/plugins/scheduler/triggers.go b/plugins/scheduler/triggers.go index 3d350e2db..a83ce8013 100644 --- a/plugins/scheduler/triggers.go +++ b/plugins/scheduler/triggers.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/dokku/dokku/plugins/common" - "github.com/dokku/dokku/plugins/config" ) // TriggerSchedulerDetect outputs a manually selected scheduler for the app @@ -36,30 +35,46 @@ func TriggerInstall() error { return nil } - globalScheduler := config.GetWithDefault("--global", "DOKKU_SCHEDULER", "") + results, _ := common.CallPlugnTrigger(common.PlugnTriggerInput{ + Trigger: "config-get-global", + Args: []string{"DOKKU_SCHEDULER"}, + }) + globalScheduler := results.StdoutContents() if globalScheduler != "" { common.LogVerboseQuiet(fmt.Sprintf("Setting scheduler property 'selected' to %v", globalScheduler)) if err := common.PropertyWrite("scheduler", "--global", "selected", globalScheduler); err != nil { return err } - if err := config.UnsetMany("--global", []string{"DOKKU_SCHEDULER"}, false); err != nil { + _, err := common.CallPlugnTrigger(common.PlugnTriggerInput{ + Trigger: "config-unset", + Args: []string{"--global", "DOKKU_SCHEDULER"}, + }) + if err != nil { common.LogWarn(err.Error()) } } for _, appName := range apps { - scheduler := config.GetWithDefault(appName, "DOKKU_SCHEDULER", "") + results, _ := common.CallPlugnTrigger(common.PlugnTriggerInput{ + Trigger: "config-get", + Args: []string{appName, "DOKKU_SCHEDULER"}, + }) + scheduler := results.StdoutContents() if scheduler == "" { continue } - common.LogVerboseQuiet(fmt.Sprintf("Setting scheduler property 'selected' to %v", scheduler)) + common.LogVerboseQuiet(fmt.Sprintf("Setting %s scheduler property 'selected' to %v", appName, scheduler)) if err := common.PropertyWrite("scheduler", appName, "selected", scheduler); err != nil { return err } - if err := config.UnsetMany(appName, []string{"DOKKU_SCHEDULER"}, false); err != nil { + _, err := common.CallPlugnTrigger(common.PlugnTriggerInput{ + Trigger: "config-unset", + Args: []string{appName, "DOKKU_SCHEDULER"}, + }) + if err != nil { common.LogWarn(err.Error()) } }