Merge pull request #5480 from dokku/5112-procfile-cleanup
Cleanup zero'd out processes when a Procfile omitting those process types is set
This commit is contained in:
@@ -8,4 +8,5 @@
|
||||
|
||||
## Removals
|
||||
|
||||
- The `DOKKU_WAIT_TO_RETIRE` environment variable has been migrated to a `checks` property named `wait-to-retire` and will be ignored if set as an environment variable.
|
||||
- The `DOKKU_WAIT_TO_RETIRE` environment variable has been migrated to a `checks` property named `wait-to-retire` and will be ignored if set as an environment variable.
|
||||
- The `Procfile` is now extracted when source code is extracted for a build and not from the built image. Users can specify alternative paths via the `procfile-path` property of the `ps` plugin. See the [process management documentation](/docs/processes/process-management.md#changing-the-procfile-location) for more information on how to configure the `Procfile` path for your application.
|
||||
|
||||
@@ -1783,21 +1783,6 @@ APP="$1";
|
||||
curl "https://dokku.me/starting/${APP}" || true
|
||||
```
|
||||
|
||||
### `procfile-extract`
|
||||
|
||||
- Description: Extracts a Procfile from a given image to a path
|
||||
- Invoked by: `internally`
|
||||
- Arguments: `$APP $IMAGE`
|
||||
- Example:
|
||||
|
||||
```shell
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
|
||||
|
||||
# TODO
|
||||
```
|
||||
|
||||
### `procfile-get-command`
|
||||
|
||||
- Description: Fetches the command for a specific process type
|
||||
@@ -1813,21 +1798,6 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
|
||||
# TODO
|
||||
```
|
||||
|
||||
### `procfile-remove`
|
||||
|
||||
- Description: Removes the extracted Procfile
|
||||
- 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
|
||||
|
||||
@@ -289,7 +289,7 @@ dokku nginx:validate-config node-js-app --clean
|
||||
|
||||
Dokku uses a templating library by the name of [sigil](https://github.com/gliderlabs/sigil) to generate nginx configuration for each app. This may be overriden by committing the [default configuration template](https://github.com/dokku/dokku/blob/master/plugins/nginx-vhosts/templates/nginx.conf.sigil) to a file named `nginx.conf.sigil` in the root of the app repository.
|
||||
|
||||
By default, the `nginx.conf.sigil` path is pulled from the root of the app source code repository. To change the path from which the `nginx.conf.sigil` is extracted, the `nginx-conf-sigil-path` property can be modified via `nginx:set`.
|
||||
When deploying a monorepo, it may be desirable to specify the specific path of the `nginx.conf.sigil` file to use for a given app. This can be done via the `nginx:set` command. If a value is specified and that file does not exist in the app's build directory, Dokku will continue the build process as if the repository has no `nginx.conf.sigil`.
|
||||
|
||||
```shell
|
||||
dokku nginx:set node-js-app nginx-conf-sigil-path dokku/nginx.conf.sigil
|
||||
|
||||
@@ -122,14 +122,6 @@ func getPhaseScript(appName string, phase string) (string, error) {
|
||||
|
||||
// getReleaseCommand extracts the release command from a given app's procfile
|
||||
func getReleaseCommand(appName string, image string) string {
|
||||
err := common.SuppressOutput(func() error {
|
||||
return common.PlugnTrigger("procfile-extract", []string{appName, image}...)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
processType := "release"
|
||||
port := "5000"
|
||||
b, _ := common.PlugnTriggerOutput("procfile-get-command", []string{appName, processType, port}...)
|
||||
|
||||
@@ -209,6 +209,20 @@ func SetPermissions(path string, fileMode os.FileMode) error {
|
||||
return os.Chown(path, uid, gid)
|
||||
}
|
||||
|
||||
// TouchFile creates an empty file at the specified path
|
||||
func TouchFile(filename string) error {
|
||||
mode := os.FileMode(0600)
|
||||
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, mode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
file.Chmod(mode)
|
||||
SetPermissions(filename, mode)
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteSliceToFile writes a slice of strings to a file
|
||||
func WriteSliceToFile(filename string, lines []string) error {
|
||||
mode := os.FileMode(0600)
|
||||
|
||||
@@ -26,6 +26,10 @@ trigger-git-git-from-image() {
|
||||
echo "FROM $DOCKER_IMAGE" >>"$TMP_WORK_DIR/Dockerfile"
|
||||
echo "LABEL com.dokku.docker-image-labeler/alternate-tags=[\\\"$DOCKER_IMAGE\\\"]" >>"$TMP_WORK_DIR/Dockerfile"
|
||||
|
||||
dokku_log_info1 "Pulling image"
|
||||
"$DOCKER_BIN" image pull "$DOCKER_IMAGE"
|
||||
|
||||
export DOKKU_BUILDING_FROM_IMAGE="$DOCKER_IMAGE"
|
||||
plugn trigger git-from-directory "$APP" "$TMP_WORK_DIR" "$USER_NAME" "$USER_EMAIL"
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@ trigger-nginx-vhosts-core-post-deploy() {
|
||||
rm -f "${DOKKU_LIB_ROOT}/data/nginx-vhosts/app-$APP/nginx.conf.sigil.$DOKKU_PID.missing"
|
||||
rm -f "${DOKKU_LIB_ROOT}/data/nginx-vhosts/app-$APP/nginx.conf.sigil"
|
||||
fi
|
||||
else
|
||||
nginx_clear_config "$APP"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
@@ -4,13 +4,21 @@ source "$PLUGIN_AVAILABLE_PATH/nginx-vhosts/internal-functions"
|
||||
set -eo pipefail
|
||||
[[ $DOKKU_TRACE ]] && set -x
|
||||
|
||||
trigger-nginx-vhosts-core-post-extract() {
|
||||
declare desc="nginx-vhosts post-extract plugin trigger"
|
||||
declare trigger="post-extract"
|
||||
declare APP="$1" SOURCECODE_WORK_DIR="$2"
|
||||
local CONF_SIGIL_PATH="$(fn-nginx-computed-nginx-conf-sigil-path "$APP")"
|
||||
fn-nginx-vhosts-copy-from-image() {
|
||||
declare APP="$1" IMAGE_NAME="$2" CONF_SIGIL_PATH="$3"
|
||||
|
||||
pushd "$TMP_WORK_DIR" >/dev/null
|
||||
mkdir -p "${DOKKU_LIB_ROOT}/data/nginx-vhosts/app-$APP"
|
||||
rm -f "${DOKKU_LIB_ROOT}/data/nginx-vhosts/app-$APP/nginx.conf.sigil."*
|
||||
copy_from_image "$IMAGE_NAME" "$CONF_SIGIL_PATH" "${DOKKU_LIB_ROOT}/data/nginx-vhosts/app-$APP/nginx.conf.sigil.$DOKKU_PID" 2>/dev/null || true
|
||||
if [[ ! -f "${DOKKU_LIB_ROOT}/data/nginx-vhosts/app-$APP/nginx.conf.sigil.$DOKKU_PID" ]]; then
|
||||
touch "${DOKKU_LIB_ROOT}/data/nginx-vhosts/app-$APP/nginx.conf.sigil.$DOKKU_PID.missing"
|
||||
fi
|
||||
}
|
||||
|
||||
fn-nginx-vhosts-copy-from-directory() {
|
||||
declare APP="$1" SOURCECODE_WORK_DIR="$2" CONF_SIGIL_PATH="$3"
|
||||
|
||||
pushd "$SOURCECODE_WORK_DIR" >/dev/null
|
||||
mkdir -p "${DOKKU_LIB_ROOT}/data/nginx-vhosts/app-$APP"
|
||||
|
||||
if [[ -z "$CONF_SIGIL_PATH" ]]; then
|
||||
@@ -28,4 +36,17 @@ trigger-nginx-vhosts-core-post-extract() {
|
||||
popd &>/dev/null || pushd "/tmp" >/dev/null
|
||||
}
|
||||
|
||||
trigger-nginx-vhosts-core-post-extract() {
|
||||
declare desc="nginx-vhosts post-extract plugin trigger"
|
||||
declare trigger="post-extract"
|
||||
declare APP="$1" SOURCECODE_WORK_DIR="$2"
|
||||
local CONF_SIGIL_PATH="$(fn-nginx-computed-nginx-conf-sigil-path "$APP")"
|
||||
|
||||
if [[ -n "$DOKKU_BUILDING_FROM_IMAGE" ]]; then
|
||||
fn-nginx-vhosts-copy-from-image "$APP" "$DOKKU_BUILDING_FROM_IMAGE" "$CONF_SIGIL_PATH"
|
||||
else
|
||||
fn-nginx-vhosts-copy-from-directory "$APP" "$SOURCECODE_WORK_DIR" "$CONF_SIGIL_PATH"
|
||||
fi
|
||||
}
|
||||
|
||||
trigger-nginx-vhosts-core-post-extract "$@"
|
||||
|
||||
@@ -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-extract triggers/post-stop triggers/pre-deploy triggers/procfile-extract triggers/procfile-get-command triggers/procfile-remove 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/ps-can-scale triggers/ps-current-scale triggers/ps-set-scale triggers/report
|
||||
BUILD = commands subcommands triggers
|
||||
PLUGIN_NAME = ps
|
||||
|
||||
|
||||
@@ -20,33 +20,6 @@ func canScaleApp(appName string) bool {
|
||||
return common.ToBool(canScale)
|
||||
}
|
||||
|
||||
func extractProcfile(appName, image string) error {
|
||||
destination := getProcfilePath(appName)
|
||||
common.CopyFromImage(appName, image, "Procfile", destination)
|
||||
if !common.FileExists(destination) {
|
||||
common.LogInfo1Quiet("No Procfile found in app image")
|
||||
return nil
|
||||
}
|
||||
|
||||
common.LogInfo1Quiet("App Procfile file found")
|
||||
checkCmd := common.NewShellCmd(strings.Join([]string{
|
||||
"procfile-util",
|
||||
"check",
|
||||
"--procfile",
|
||||
destination,
|
||||
}, " "))
|
||||
var stderr bytes.Buffer
|
||||
checkCmd.ShowOutput = false
|
||||
checkCmd.Command.Stderr = &stderr
|
||||
_, err := checkCmd.Output()
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf(strings.TrimSpace(stderr.String()))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getProcessStatus(appName string) map[string]string {
|
||||
statuses := make(map[string]string)
|
||||
containerFiles := common.ListFilesWithPrefix(common.AppRoot(appName), "CONTAINER.")
|
||||
@@ -127,6 +100,14 @@ func getRunningState(appName string) string {
|
||||
|
||||
func hasProcfile(appName string) bool {
|
||||
procfilePath := getProcfilePath(appName)
|
||||
if common.FileExists(fmt.Sprintf("%s.%s.missing", procfilePath, os.Getenv("DOKKU_PID"))) {
|
||||
return false
|
||||
}
|
||||
|
||||
if common.FileExists(fmt.Sprintf("%s.%s", procfilePath, os.Getenv("DOKKU_PID"))) {
|
||||
return true
|
||||
}
|
||||
|
||||
return common.FileExists(procfilePath)
|
||||
}
|
||||
|
||||
@@ -205,16 +186,22 @@ func getFormations(appName string) (FormationSlice, error) {
|
||||
return formations, err
|
||||
}
|
||||
|
||||
return parseProcessTuples(processTuples)
|
||||
}
|
||||
|
||||
func removeProcfile(appName string) error {
|
||||
procfile := getProcfilePath(appName)
|
||||
if !common.FileExists(procfile) {
|
||||
return nil
|
||||
oldProcessTuples, err := common.PropertyListGet("ps", appName, "scale.old")
|
||||
if err != nil {
|
||||
return formations, err
|
||||
}
|
||||
|
||||
return os.Remove(procfile)
|
||||
formations, err = parseProcessTuples(processTuples)
|
||||
if err != nil {
|
||||
return formations, err
|
||||
}
|
||||
|
||||
oldFormations, err := parseProcessTuples(oldProcessTuples)
|
||||
if err != nil {
|
||||
return formations, err
|
||||
}
|
||||
|
||||
return append(formations, oldFormations...), nil
|
||||
}
|
||||
|
||||
func restorePrep() error {
|
||||
@@ -288,6 +275,16 @@ func scaleSet(appName string, skipDeploy bool, clearExisting bool, processTuples
|
||||
return nil
|
||||
}
|
||||
|
||||
func getProcessSpecificProcfile(appName string) string {
|
||||
existingProcfile := getProcfilePath(appName)
|
||||
processSpecificProcfile := fmt.Sprintf("%s.%s", existingProcfile, os.Getenv("DOKKU_PID"))
|
||||
if common.FileExists(processSpecificProcfile) {
|
||||
return processSpecificProcfile
|
||||
}
|
||||
|
||||
return existingProcfile
|
||||
}
|
||||
|
||||
func updateScale(appName string, clearExisting bool, formationUpdates FormationSlice) error {
|
||||
formations := FormationSlice{}
|
||||
if !clearExisting {
|
||||
@@ -302,21 +299,23 @@ func updateScale(appName string, clearExisting bool, formationUpdates FormationS
|
||||
}
|
||||
}
|
||||
|
||||
procfilePath := getProcfilePath(appName)
|
||||
procfileExists := hasProcfile(appName)
|
||||
validProcessTypes := make(map[string]bool)
|
||||
if procfileExists {
|
||||
if hasProcfile(appName) {
|
||||
var err error
|
||||
validProcessTypes, err = processesInProcfile(procfilePath)
|
||||
validProcessTypes, err = processesInProcfile(getProcessSpecificProcfile(appName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if common.FileExists(getProcessSpecificProcfile(appName)) {
|
||||
common.CatFile(getProcessSpecificProcfile(appName))
|
||||
}
|
||||
|
||||
foundProcessTypes := map[string]bool{}
|
||||
updatedFormation := FormationSlice{}
|
||||
for _, formation := range formationUpdates {
|
||||
if procfileExists && !validProcessTypes[formation.ProcessType] && formation.Quantity != 0 {
|
||||
if hasProcfile(appName) && !validProcessTypes[formation.ProcessType] && formation.Quantity != 0 {
|
||||
return fmt.Errorf("%s is not a valid process name to scale up", formation.ProcessType)
|
||||
}
|
||||
|
||||
@@ -351,9 +350,19 @@ func updateScale(appName string, clearExisting bool, formationUpdates FormationS
|
||||
}
|
||||
|
||||
values := []string{}
|
||||
oldValues := []string{}
|
||||
for _, formation := range updatedFormation {
|
||||
if !validProcessTypes[formation.ProcessType] && formation.Quantity == 0 {
|
||||
oldValues = append(oldValues, fmt.Sprintf("%s=%d", formation.ProcessType, formation.Quantity))
|
||||
continue
|
||||
}
|
||||
|
||||
values = append(values, fmt.Sprintf("%s=%d", formation.ProcessType, formation.Quantity))
|
||||
}
|
||||
|
||||
if err := common.PropertyListWrite("ps", appName, "scale.old", oldValues); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return common.PropertyListWrite("ps", appName, "scale", values)
|
||||
}
|
||||
|
||||
@@ -52,10 +52,6 @@ func main() {
|
||||
case "post-delete":
|
||||
appName := flag.Arg(0)
|
||||
err = ps.TriggerPostDelete(appName)
|
||||
case "post-extract":
|
||||
appName := flag.Arg(0)
|
||||
tmpWorkDir := flag.Arg(1)
|
||||
err = ps.TriggerPostExtract(appName, tmpWorkDir)
|
||||
case "post-stop":
|
||||
appName := flag.Arg(0)
|
||||
err = ps.TriggerPostStop(appName)
|
||||
@@ -63,18 +59,11 @@ func main() {
|
||||
appName := flag.Arg(0)
|
||||
imageTag := flag.Arg(1)
|
||||
err = ps.TriggerPreDeploy(appName, imageTag)
|
||||
case "procfile-extract":
|
||||
appName := flag.Arg(0)
|
||||
image := flag.Arg(1)
|
||||
err = ps.TriggerProcfileExtract(appName, image)
|
||||
case "procfile-get-command":
|
||||
appName := flag.Arg(0)
|
||||
processType := flag.Arg(1)
|
||||
port := common.ToInt(flag.Arg(2), 5000)
|
||||
err = ps.TriggerProcfileGetCommand(appName, processType, port)
|
||||
case "procfile-remove":
|
||||
appName := flag.Arg(0)
|
||||
err = ps.TriggerProcfileRemove(appName)
|
||||
case "ps-can-scale":
|
||||
appName := flag.Arg(0)
|
||||
canScale := common.ToBool(flag.Arg(1))
|
||||
|
||||
@@ -24,6 +24,28 @@ func TriggerAppRestart(appName string) error {
|
||||
// TriggerCorePostDeploy sets a property to
|
||||
// allow the app to be restored on boot
|
||||
func TriggerCorePostDeploy(appName string) error {
|
||||
existingProcfile := getProcfilePath(appName)
|
||||
processSpecificProcfile := fmt.Sprintf("%s.%s", existingProcfile, os.Getenv("DOKKU_PID"))
|
||||
if common.FileExists(processSpecificProcfile) {
|
||||
if err := os.Rename(processSpecificProcfile, existingProcfile); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if common.FileExists(fmt.Sprintf("%s.missing", processSpecificProcfile)) {
|
||||
if err := os.Remove(fmt.Sprintf("%s.missing", processSpecificProcfile)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if common.FileExists(existingProcfile) {
|
||||
if err := os.Remove(existingProcfile); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := common.PropertyDelete("ps", appName, "scale.old"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
entries := map[string]string{
|
||||
"DOKKU_APP_RESTORE": "1",
|
||||
}
|
||||
@@ -36,26 +58,41 @@ func TriggerCorePostDeploy(appName string) error {
|
||||
// TriggerCorePostExtract ensures that the main Procfile is the one specified by procfile-path
|
||||
func TriggerCorePostExtract(appName string, sourceWorkDir string) error {
|
||||
procfilePath := strings.Trim(reportComputedProcfilePath(appName), "/")
|
||||
if procfilePath == "" || procfilePath == "Procfile" {
|
||||
return nil
|
||||
if procfilePath == "" {
|
||||
procfilePath = "Procfile"
|
||||
}
|
||||
|
||||
defaultProcfilePath := path.Join(sourceWorkDir, "Procfile")
|
||||
if common.FileExists(defaultProcfilePath) {
|
||||
if err := os.Remove(defaultProcfilePath); err != nil {
|
||||
return fmt.Errorf("Unable to remove existing Procfile: %v", err.Error())
|
||||
existingProcfile := getProcfilePath(appName)
|
||||
files, err := filepath.Glob(fmt.Sprintf("%s.*", existingProcfile))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, f := range files {
|
||||
if err := os.Remove(f); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
fullProcfilePath := path.Join(sourceWorkDir, procfilePath)
|
||||
if !common.FileExists(fullProcfilePath) {
|
||||
return nil
|
||||
processSpecificProcfile := fmt.Sprintf("%s.%s", existingProcfile, os.Getenv("DOKKU_PID"))
|
||||
if os.Getenv("DOKKU_BUILDING_FROM_IMAGE") == "" {
|
||||
repoProcfilePath := path.Join(sourceWorkDir, procfilePath)
|
||||
if !common.FileExists(repoProcfilePath) {
|
||||
return common.TouchFile(fmt.Sprintf("%s.missing", processSpecificProcfile))
|
||||
}
|
||||
|
||||
if err := copy.Copy(repoProcfilePath, processSpecificProcfile); err != nil {
|
||||
return fmt.Errorf("Unable to extract Procfile: %v", err.Error())
|
||||
}
|
||||
} else {
|
||||
if err := common.CopyFromImage(appName, os.Getenv("DOKKU_BUILDING_FROM_IMAGE"), procfilePath, processSpecificProcfile); err != nil {
|
||||
return common.TouchFile(fmt.Sprintf("%s.missing", processSpecificProcfile))
|
||||
}
|
||||
}
|
||||
|
||||
if err := copy.Copy(fullProcfilePath, path.Join(sourceWorkDir, "Procfile")); err != nil {
|
||||
return fmt.Errorf("Unable to move specified Procfile into place: %v", err.Error())
|
||||
b, err := sh.Command("procfile-util", "check", "-P", processSpecificProcfile).CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf(strings.TrimSpace(string(b[:])))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -137,6 +174,7 @@ func TriggerPostAppCloneSetup(oldAppName string, newAppName string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: Copy data dir
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -159,6 +197,7 @@ func TriggerPostAppRenameSetup(oldAppName string, newAppName string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: Move data dir
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -200,20 +239,6 @@ func TriggerPostDelete(appName string) error {
|
||||
return propertyErr
|
||||
}
|
||||
|
||||
// TriggerPostExtract validates a procfile
|
||||
func TriggerPostExtract(appName string, tempWorkDir string) error {
|
||||
procfile := filepath.Join(tempWorkDir, "Procfile")
|
||||
if !common.FileExists(procfile) {
|
||||
return nil
|
||||
}
|
||||
|
||||
b, err := sh.Command("procfile-util", "check", "-P", procfile).CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf(strings.TrimSpace(string(b[:])))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TriggerPostStop sets the restore property to false
|
||||
func TriggerPostStop(appName string) error {
|
||||
entries := map[string]string{
|
||||
@@ -227,16 +252,6 @@ func TriggerPostStop(appName string) error {
|
||||
|
||||
// TriggerPreDeploy ensures an app has an up to date scale parameters
|
||||
func TriggerPreDeploy(appName string, imageTag string) error {
|
||||
image := common.GetAppImageName(appName, imageTag, "")
|
||||
|
||||
if err := removeProcfile(appName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := extractProcfile(appName, image); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := updateScale(appName, false, FormationSlice{}); err != nil {
|
||||
common.LogDebug(fmt.Sprintf("Error generating scale file: %s", err.Error()))
|
||||
return err
|
||||
@@ -245,42 +260,15 @@ func TriggerPreDeploy(appName string, imageTag string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TriggerProcfileExtract extracted the procfile
|
||||
func TriggerProcfileExtract(appName string, image string) error {
|
||||
directory := filepath.Join(common.MustGetEnv("DOKKU_LIB_ROOT"), "data", "ps", appName)
|
||||
if err := os.MkdirAll(directory, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := common.SetPermissions(directory, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := removeProcfile(appName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return extractProcfile(appName, image)
|
||||
}
|
||||
|
||||
// TriggerProcfileGetCommand fetches a command from the procfile
|
||||
func TriggerProcfileGetCommand(appName string, processType string, port int) error {
|
||||
procfilePath := getProcfilePath(appName)
|
||||
if !common.FileExists(procfilePath) {
|
||||
extract := func() error {
|
||||
image, err := common.GetDeployingAppImageName(appName, "", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return extractProcfile(appName, image)
|
||||
}
|
||||
|
||||
if err := common.SuppressOutput(extract); err != nil {
|
||||
return err
|
||||
}
|
||||
existingProcfile := getProcfilePath(appName)
|
||||
processSpecificProcfile := fmt.Sprintf("%s.%s", existingProcfile, os.Getenv("DOKKU_PID"))
|
||||
if common.FileExists(fmt.Sprintf("%s.missing", processSpecificProcfile)) {
|
||||
return nil
|
||||
}
|
||||
command, err := getProcfileCommand(procfilePath, processType, port)
|
||||
|
||||
command, err := getProcfileCommand(getProcessSpecificProcfile(appName), processType, port)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -292,11 +280,6 @@ func TriggerProcfileGetCommand(appName string, processType string, port int) err
|
||||
return nil
|
||||
}
|
||||
|
||||
// TriggerProcfileRemove removes the procfile if it exists
|
||||
func TriggerProcfileRemove(appName string) error {
|
||||
return removeProcfile(appName)
|
||||
}
|
||||
|
||||
// TriggerPsCanScale sets whether or not a user can scale an app with ps:scale
|
||||
func TriggerPsCanScale(appName string, canScale bool) error {
|
||||
return common.PropertyWrite("ps", appName, "can-scale", strconv.FormatBool(canScale))
|
||||
|
||||
123
tests/unit/ps-general-1.bats
Normal file
123
tests/unit/ps-general-1.bats
Normal file
@@ -0,0 +1,123 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
load test_helper
|
||||
|
||||
setup() {
|
||||
global_setup
|
||||
create_app
|
||||
}
|
||||
|
||||
teardown() {
|
||||
destroy_app
|
||||
global_teardown
|
||||
}
|
||||
|
||||
@test "(ps) ps:help" {
|
||||
run /bin/bash -c "dokku ps"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_output_contains "Manage app processes"
|
||||
help_output="$output"
|
||||
|
||||
run /bin/bash -c "dokku ps:help"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_output_contains "Manage app processes"
|
||||
assert_output "$help_output"
|
||||
}
|
||||
|
||||
@test "(ps) ps:inspect" {
|
||||
dokku config:set "$TEST_APP" key=value key=value=value
|
||||
deploy_app dockerfile
|
||||
|
||||
CID=$(<$DOKKU_ROOT/$TEST_APP/CONTAINER.web.1)
|
||||
run /bin/bash -c "dokku ps:inspect $TEST_APP"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output_contains "$CID" 6
|
||||
}
|
||||
|
||||
@test "(ps:scale) procfile commands extraction" {
|
||||
source "$PLUGIN_CORE_AVAILABLE_PATH/ps/functions"
|
||||
cat <<EOF >"$DOKKU_LIB_ROOT/data/ps/$TEST_APP/Procfile"
|
||||
web: node web.js --port \$PORT
|
||||
worker: node worker.js
|
||||
EOF
|
||||
PLUGIN_PATH=/var/lib/dokku/plugins PLUGIN_CORE_AVAILABLE_PATH=/var/lib/dokku/core-plugins/available DOKKU_LIB_ROOT=/var/lib/dokku run plugn trigger procfile-get-command "$TEST_APP" web 5001
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_output "node web.js --port 5001"
|
||||
|
||||
PLUGIN_PATH=/var/lib/dokku/plugins PLUGIN_CORE_AVAILABLE_PATH=/var/lib/dokku/core-plugins/available DOKKU_LIB_ROOT=/var/lib/dokku run plugn trigger procfile-get-command "$TEST_APP" worker
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_output "node worker.js"
|
||||
}
|
||||
|
||||
@test "(ps:scale) update formations from Procfile" {
|
||||
local TMP=$(mktemp -d "/tmp/dokku.me.XXXXX")
|
||||
trap 'popd &>/dev/null || true; rm -rf "$TMP"' INT TERM
|
||||
|
||||
CUSTOM_TMP="$TMP" deploy_app nodejs-express
|
||||
|
||||
run /bin/bash -c "dokku --quiet ps:scale $TEST_APP"
|
||||
output=$(echo "$output" | tr -s " ")
|
||||
echo "output: ($output)"
|
||||
assert_output $'cron: 0\ncustom: 0\nrelease: 0\nweb: 1\nworker: 0'
|
||||
|
||||
pushd $TMP
|
||||
echo scaletest: sleep infinity >>Procfile
|
||||
git commit Procfile -m 'Add scaletest process'
|
||||
git push target master:master
|
||||
|
||||
run /bin/bash -c "dokku --quiet ps:scale $TEST_APP"
|
||||
output=$(echo "$output" | tr -s " ")
|
||||
echo "output: ($output)"
|
||||
assert_output $'cron: 0\ncustom: 0\nrelease: 0\nscaletest: 0\nweb: 1\nworker: 0'
|
||||
|
||||
popd
|
||||
rm -rf "$TMP"
|
||||
}
|
||||
|
||||
@test "(ps:restart-policy) default policy" {
|
||||
run /bin/bash -c "dokku --quiet ps:report $TEST_APP --ps-restart-policy"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_output "on-failure:10"
|
||||
}
|
||||
|
||||
@test "(ps:restart-policy) ps:set restart-policy" {
|
||||
for policy in no unless-stopped always on-failure on-failure:20; do
|
||||
run /bin/bash -c "dokku ps:set $TEST_APP restart-policy $policy"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
run /bin/bash -c "dokku --quiet ps:report $TEST_APP --ps-restart-policy"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_output "$policy"
|
||||
done
|
||||
}
|
||||
|
||||
@test "(ps:restart-policy) deployed policy" {
|
||||
test_restart_policy="on-failure:20"
|
||||
run /bin/bash -c "dokku ps:set $TEST_APP restart-policy $test_restart_policy"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
run /bin/bash -c "dokku --quiet ps:report $TEST_APP --ps-restart-policy"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_output "$test_restart_policy"
|
||||
|
||||
deploy_app dockerfile
|
||||
|
||||
CID=$(<$DOKKU_ROOT/$TEST_APP/CONTAINER.web.1)
|
||||
run /bin/bash -c "docker inspect -f '{{ .HostConfig.RestartPolicy.Name }}:{{ .HostConfig.RestartPolicy.MaximumRetryCount }}' $CID"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_output "$test_restart_policy"
|
||||
}
|
||||
@@ -12,116 +12,6 @@ teardown() {
|
||||
global_teardown
|
||||
}
|
||||
|
||||
@test "(ps) ps:help" {
|
||||
run /bin/bash -c "dokku ps"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_output_contains "Manage app processes"
|
||||
help_output="$output"
|
||||
|
||||
run /bin/bash -c "dokku ps:help"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_output_contains "Manage app processes"
|
||||
assert_output "$help_output"
|
||||
}
|
||||
|
||||
@test "(ps) ps:inspect" {
|
||||
dokku config:set "$TEST_APP" key=value key=value=value
|
||||
deploy_app dockerfile
|
||||
|
||||
CID=$(<$DOKKU_ROOT/$TEST_APP/CONTAINER.web.1)
|
||||
run /bin/bash -c "dokku ps:inspect $TEST_APP"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output_contains "$CID" 6
|
||||
}
|
||||
|
||||
@test "(ps:scale) procfile commands extraction" {
|
||||
source "$PLUGIN_CORE_AVAILABLE_PATH/ps/functions"
|
||||
cat <<EOF >"$DOKKU_LIB_ROOT/data/ps/$TEST_APP/Procfile"
|
||||
web: node web.js --port \$PORT
|
||||
worker: node worker.js
|
||||
EOF
|
||||
PLUGIN_PATH=/var/lib/dokku/plugins PLUGIN_CORE_AVAILABLE_PATH=/var/lib/dokku/core-plugins/available DOKKU_LIB_ROOT=/var/lib/dokku run plugn trigger procfile-get-command "$TEST_APP" web 5001
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_output "node web.js --port 5001"
|
||||
|
||||
PLUGIN_PATH=/var/lib/dokku/plugins PLUGIN_CORE_AVAILABLE_PATH=/var/lib/dokku/core-plugins/available DOKKU_LIB_ROOT=/var/lib/dokku run plugn trigger procfile-get-command "$TEST_APP" worker
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_output "node worker.js"
|
||||
}
|
||||
|
||||
@test "(ps:scale) update formations from Procfile" {
|
||||
local TMP=$(mktemp -d "/tmp/dokku.me.XXXXX")
|
||||
trap 'popd &>/dev/null || true; rm -rf "$TMP"' INT TERM
|
||||
|
||||
CUSTOM_TMP="$TMP" deploy_app nodejs-express
|
||||
|
||||
run /bin/bash -c "dokku --quiet ps:scale $TEST_APP"
|
||||
output=$(echo "$output" | tr -s " ")
|
||||
echo "output: ($output)"
|
||||
assert_output $'cron: 0\ncustom: 0\nrelease: 0\nweb: 1\nworker: 0'
|
||||
|
||||
pushd $TMP
|
||||
echo scaletest: sleep infinity >>Procfile
|
||||
git commit Procfile -m 'Add scaletest process'
|
||||
git push target master:master
|
||||
|
||||
run /bin/bash -c "dokku --quiet ps:scale $TEST_APP"
|
||||
output=$(echo "$output" | tr -s " ")
|
||||
echo "output: ($output)"
|
||||
assert_output $'cron: 0\ncustom: 0\nrelease: 0\nscaletest: 0\nweb: 1\nworker: 0'
|
||||
|
||||
popd
|
||||
rm -rf "$TMP"
|
||||
}
|
||||
|
||||
@test "(ps:restart-policy) default policy" {
|
||||
run /bin/bash -c "dokku --quiet ps:report $TEST_APP --ps-restart-policy"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_output "on-failure:10"
|
||||
}
|
||||
|
||||
@test "(ps:restart-policy) ps:set restart-policy" {
|
||||
for policy in no unless-stopped always on-failure on-failure:20; do
|
||||
run /bin/bash -c "dokku ps:set $TEST_APP restart-policy $policy"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
run /bin/bash -c "dokku --quiet ps:report $TEST_APP --ps-restart-policy"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_output "$policy"
|
||||
done
|
||||
}
|
||||
|
||||
@test "(ps:restart-policy) deployed policy" {
|
||||
test_restart_policy="on-failure:20"
|
||||
run /bin/bash -c "dokku ps:set $TEST_APP restart-policy $test_restart_policy"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
run /bin/bash -c "dokku --quiet ps:report $TEST_APP --ps-restart-policy"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_output "$test_restart_policy"
|
||||
|
||||
deploy_app dockerfile
|
||||
|
||||
CID=$(<$DOKKU_ROOT/$TEST_APP/CONTAINER.web.1)
|
||||
run /bin/bash -c "docker inspect -f '{{ .HostConfig.RestartPolicy.Name }}:{{ .HostConfig.RestartPolicy.MaximumRetryCount }}' $CID"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_output "$test_restart_policy"
|
||||
}
|
||||
|
||||
@test "(ps:rebuild) old app name" {
|
||||
run /bin/bash -c "dokku --force apps:destroy $TEST_APP"
|
||||
echo "output: $output"
|
||||
@@ -238,3 +128,50 @@ EOF
|
||||
assert_success
|
||||
assert_output_contains 'SECRET_KEY:'
|
||||
}
|
||||
|
||||
@test "(ps:scale) remove zerod processes" {
|
||||
run /bin/bash -c "dokku builder-herokuish:set $TEST_APP allowed true"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
run deploy_app python
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
run /bin/bash -c "dokku ps:scale $TEST_APP worker=1"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
run /bin/bash -c "dokku --quiet ps:scale $TEST_APP"
|
||||
output=$(echo "$output" | tr -s " ")
|
||||
echo "output: ($output)"
|
||||
assert_output $'cron: 0\ncustom: 0\nrelease: 0\nweb: 1\nworker: 1'
|
||||
|
||||
run /bin/bash -c "dokku ps:scale $TEST_APP worker=0"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
run /bin/bash -c "dokku --quiet ps:scale $TEST_APP"
|
||||
output=$(echo "$output" | tr -s " ")
|
||||
echo "output: ($output)"
|
||||
assert_output $'cron: 0\ncustom: 0\nrelease: 0\nweb: 1\nworker: 0'
|
||||
|
||||
run /bin/bash -c "dokku ps:set $TEST_APP procfile-path second.Procfile"
|
||||
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 --quiet ps:scale $TEST_APP"
|
||||
output=$(echo "$output" | tr -s " ")
|
||||
echo "output: ($output)"
|
||||
assert_output 'web: 1'
|
||||
}
|
||||
Reference in New Issue
Block a user