From 1e915968b557c5daa4c96c600dea89154b32fec7 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sat, 13 Feb 2021 00:46:35 -0500 Subject: [PATCH] fix: correctly handle is-deployed check Previously, checking if an app was deployed actually checked if there were any running processes. This is not only incorrect, but also fails to take into account applications that do not have running processes and are only used for one-off commands. This fix migrates apps to the new "deployed" property which is set in core-post-deploy. The result is a slightly faster "deployed" check that is correct and allows non-scaled apps to actually work. Closes #4398 --- docs/development/plugin-triggers.md | 19 --------- plugins/app-json/go.sum | 1 + plugins/common/Makefile | 9 ++++- plugins/common/common.go | 26 +++++++++++-- plugins/common/functions | 4 +- plugins/common/go.mod | 1 + plugins/common/go.sum | 2 + plugins/common/src/common/common.go | 34 ++++++++++++++++ plugins/common/src/triggers/triggers.go | 35 +++++++++++++++++ plugins/common/triggers.go | 39 +++++++++++++++++++ tests/apps/python-console-only/Procfile | 1 + tests/apps/python-console-only/console.py | 1 + tests/apps/python-console-only/null | 0 tests/unit/ps-general.bats | 47 +++++++++++++++++++++++ 14 files changed, 193 insertions(+), 26 deletions(-) create mode 100644 plugins/common/src/common/common.go create mode 100644 plugins/common/src/triggers/triggers.go create mode 100644 plugins/common/triggers.go create mode 100644 tests/apps/python-console-only/Procfile create mode 100644 tests/apps/python-console-only/console.py create mode 100644 tests/apps/python-console-only/null diff --git a/docs/development/plugin-triggers.md b/docs/development/plugin-triggers.md index 37acb4e32..61d05c603 100644 --- a/docs/development/plugin-triggers.md +++ b/docs/development/plugin-triggers.md @@ -1947,25 +1947,6 @@ DOKKU_SCHEDULER="$1"; APP="$2"; # TODO ``` -### `scheduler-is-deployed` - -> Warning: The scheduler plugin trigger apis are under development and may change -> between minor releases until the 1.0 release. - -- Description: Allows you to check if an app has been deployed -- Invoked by: `dokku ps:rebuild` -- Arguments: `$DOKKU_SCHEDULER $APP` -- Example: - -```shell -#!/usr/bin/env bash - -set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x -DOKKU_SCHEDULER="$1"; APP="$2"; - -# TODO -``` - ### `scheduler-logs` > Warning: The scheduler plugin trigger apis are under development and may change diff --git a/plugins/app-json/go.sum b/plugins/app-json/go.sum index 3599be52d..9fdb858d0 100644 --- a/plugins/app-json/go.sum +++ b/plugins/app-json/go.sum @@ -6,5 +6,6 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNU github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/ryanuber/columnize v1.1.2-0.20190319233515-9e6335e58db3 h1:utdYOikI1XjNtTFGCwSM6OmFJblU4ld4gACoJsbadJg= github.com/ryanuber/columnize v1.1.2-0.20190319233515-9e6335e58db3/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/plugins/common/Makefile b/plugins/common/Makefile index 81bfdd798..e3823d2da 100644 --- a/plugins/common/Makefile +++ b/plugins/common/Makefile @@ -1,10 +1,17 @@ -BUILD = prop +TRIGGERS = triggers/core-post-deploy triggers/install triggers/post-delete +BUILD = prop common triggers PLUGIN_NAME = common clean-prop: rm -rf prop +clean-common: + rm -rf common + prop: clean-prop **/**/prop.go go build -ldflags="-s -w" $(GO_ARGS) -o prop src/prop/prop.go +common: clean-common **/**/common.go + go build -ldflags="-s -w" $(GO_ARGS) -o common src/common/common.go + include ../../common.mk diff --git a/plugins/common/common.go b/plugins/common/common.go index a4b1be044..59c009e42 100644 --- a/plugins/common/common.go +++ b/plugins/common/common.go @@ -20,6 +20,16 @@ import ( type errfunc func() error +var ( + // DefaultProperties is a map of all valid common properties with corresponding default property values + DefaultProperties = map[string]string{ + "deployed": "false", + } + + // GlobalProperties is a map of all valid global common properties + GlobalProperties = map[string]bool{} +) + // AppRoot returns the app root path func AppRoot(appName string) string { dokkuRoot := MustGetEnv("DOKKU_ROOT") @@ -279,9 +289,19 @@ func GetAppImageName(appName, imageTag, imageRepo string) (imageName string) { // IsDeployed returns true if given app has a running container func IsDeployed(appName string) bool { - scheduler := GetAppScheduler(appName) - _, err := PlugnTriggerOutput("scheduler-is-deployed", []string{scheduler, appName}...) - return err == nil + deployed := PropertyGetDefault("common", appName, "deployed", "") + if deployed == "" { + deployed = "false" + scheduler := GetAppScheduler(appName) + _, err := PlugnTriggerOutput("scheduler-is-deployed", []string{scheduler, appName}...) + if err == nil { + deployed = "true" + } + + CommandPropertySet("common", appName, "deployed", deployed, DefaultProperties, GlobalProperties) + } + + return deployed == "true" } // MustGetEnv returns env variable or fails if it's not set diff --git a/plugins/common/functions b/plugins/common/functions index 93002186a..b5838af23 100755 --- a/plugins/common/functions +++ b/plugins/common/functions @@ -577,10 +577,8 @@ get_app_running_container_types() { is_deployed() { declare desc="return 0 if given app has a running container" local APP="$1" - source "$PLUGIN_AVAILABLE_PATH/config/functions" - local DOKKU_SCHEDULER=$(get_app_scheduler "$APP") - plugn trigger scheduler-is-deployed "$DOKKU_SCHEDULER" "$APP" + "$PLUGIN_CORE_AVAILABLE_PATH/common/common" --quiet is-deployed "$APP" } is_container_running() { diff --git a/plugins/common/go.mod b/plugins/common/go.mod index 6fb4e0764..8fc5569f7 100644 --- a/plugins/common/go.mod +++ b/plugins/common/go.mod @@ -6,5 +6,6 @@ require ( github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 // indirect github.com/codeskyblue/go-sh v0.0.0-20190412065543-76bd3d59ff27 github.com/ryanuber/columnize v1.1.2-0.20190319233515-9e6335e58db3 + github.com/spf13/pflag v1.0.5 // indirect golang.org/x/sync v0.0.0-20201207232520-09787c993a3a ) diff --git a/plugins/common/go.sum b/plugins/common/go.sum index aa1fb3e7e..58c904707 100644 --- a/plugins/common/go.sum +++ b/plugins/common/go.sum @@ -4,5 +4,7 @@ github.com/codeskyblue/go-sh v0.0.0-20190412065543-76bd3d59ff27 h1:HHUr4P/aKh4qu github.com/codeskyblue/go-sh v0.0.0-20190412065543-76bd3d59ff27/go.mod h1:VQx0hjo2oUeQkQUET7wRwradO6f+fN5jzXgB/zROxxE= github.com/ryanuber/columnize v1.1.2-0.20190319233515-9e6335e58db3 h1:utdYOikI1XjNtTFGCwSM6OmFJblU4ld4gACoJsbadJg= github.com/ryanuber/columnize v1.1.2-0.20190319233515-9e6335e58db3/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/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/plugins/common/src/common/common.go b/plugins/common/src/common/common.go new file mode 100644 index 000000000..1562b1b2e --- /dev/null +++ b/plugins/common/src/common/common.go @@ -0,0 +1,34 @@ +package main + +import ( + "fmt" + "os" + + "github.com/dokku/dokku/plugins/common" + flag "github.com/spf13/pflag" +) + +func main() { + quiet := flag.Bool("quiet", false, "--quiet: set DOKKU_QUIET_OUTPUT=1") + flag.Parse() + cmd := flag.Arg(0) + + if *quiet { + os.Setenv("DOKKU_QUIET_OUTPUT", "1") + } + + var err error + switch cmd { + case "is-deployed": + appName := flag.Arg(1) + if !common.IsDeployed(appName) { + err = fmt.Errorf("App %v not deployed", appName) + } + default: + err = fmt.Errorf("Invalid common command call: %v", cmd) + } + + if err != nil { + common.LogFailQuiet(err.Error()) + } +} diff --git a/plugins/common/src/triggers/triggers.go b/plugins/common/src/triggers/triggers.go new file mode 100644 index 000000000..e5b342297 --- /dev/null +++ b/plugins/common/src/triggers/triggers.go @@ -0,0 +1,35 @@ +package main + +import ( + "flag" + "fmt" + "os" + "strings" + + "github.com/dokku/dokku/plugins/common" +) + +// 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 "core-post-deploy": + appName := flag.Arg(0) + err = common.TriggerCorePostDeploy(appName) + case "install": + err = common.TriggerInstall() + case "post-delete": + appName := flag.Arg(0) + err = common.TriggerPostDelete(appName) + default: + common.LogFail(fmt.Sprintf("Invalid plugin trigger call: %s", trigger)) + } + + if err != nil { + common.LogFail(err.Error()) + } +} diff --git a/plugins/common/triggers.go b/plugins/common/triggers.go new file mode 100644 index 000000000..b20c655b3 --- /dev/null +++ b/plugins/common/triggers.go @@ -0,0 +1,39 @@ +package common + +import ( + "fmt" + "os" +) + +// TriggerCorePostDeploy associates the container with a specified network +func TriggerCorePostDeploy(appName string) error { + quiet := os.Getenv("DOKKU_QUIET_OUTPUT") + os.Setenv("DOKKU_QUIET_OUTPUT", "1") + CommandPropertySet("common", appName, "deployed", "true", DefaultProperties, GlobalProperties) + os.Setenv("DOKKU_QUIET_OUTPUT", quiet) + return nil +} + +// TriggerInstall runs the install step for the common plugin +func TriggerInstall() error { + if err := PropertySetup("common"); err != nil { + return fmt.Errorf("Unable to install the common plugin: %s", err.Error()) + } + + apps, err := DokkuApps() + if err != nil { + return nil + } + + // migrate all is-deployed values from trigger to property + for _, appName := range apps { + IsDeployed(appName) + } + + return nil +} + +// TriggerPostDelete destroys the common property for a given app container +func TriggerPostDelete(appName string) error { + return PropertyDestroy("common", appName) +} diff --git a/tests/apps/python-console-only/Procfile b/tests/apps/python-console-only/Procfile new file mode 100644 index 000000000..81a711088 --- /dev/null +++ b/tests/apps/python-console-only/Procfile @@ -0,0 +1 @@ +console: python3 console.py diff --git a/tests/apps/python-console-only/console.py b/tests/apps/python-console-only/console.py new file mode 100644 index 000000000..f1a18139c --- /dev/null +++ b/tests/apps/python-console-only/console.py @@ -0,0 +1 @@ +print("Hello world!") diff --git a/tests/apps/python-console-only/null b/tests/apps/python-console-only/null new file mode 100644 index 000000000..e69de29bb diff --git a/tests/unit/ps-general.bats b/tests/unit/ps-general.bats index 6c570e0ac..ebe719428 100644 --- a/tests/unit/ps-general.bats +++ b/tests/unit/ps-general.bats @@ -151,3 +151,50 @@ EOF echo "status: $status" assert_success } + +@test "(ps:scale) console-only app" { + run /bin/bash -c "dokku ps:scale $TEST_APP web=0 console=0" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku ps:report $TEST_APP --deployed" + echo "output: $output" + echo "status: $status" + assert_success + assert_output "false" + + run deploy_app python-console-only + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku ps:report $TEST_APP --deployed" + echo "output: $output" + echo "status: $status" + assert_success + assert_output "true" + + run /bin/bash -c "dokku --rm run $TEST_APP console" + echo "output: $output" + echo "status: $status" + assert_success + assert_output_contains "Hello world!" + + run /bin/bash -c "dokku --rm run $TEST_APP printenv FOO" + echo "output: $output" + echo "status: $status" + assert_failure + + run /bin/bash -c "dokku --rm config:set $TEST_APP FOO=bar" + echo "output: $output" + echo "status: $status" + assert_success + assert_output_contains "Releasing $TEST_APP" + + run /bin/bash -c "dokku --rm run $TEST_APP printenv FOO" + echo "output: $output" + echo "status: $status" + assert_success + assert_output_contains "bar" +}