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
This commit is contained in:
Jose Diaz-Gonzalez
2021-02-13 00:46:35 -05:00
parent 3dfb84048c
commit 1e915968b5
14 changed files with 193 additions and 26 deletions

View File

@@ -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

View File

@@ -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=

View File

@@ -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

View File

@@ -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

View File

@@ -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() {

View File

@@ -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
)

View File

@@ -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=

View File

@@ -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())
}
}

View File

@@ -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())
}
}

View File

@@ -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)
}

View File

@@ -0,0 +1 @@
console: python3 console.py

View File

@@ -0,0 +1 @@
print("Hello world!")

View File

View File

@@ -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"
}