diff --git a/docs/development/plugin-triggers.md b/docs/development/plugin-triggers.md index 22f0e7e833e..5ff757bd97a 100644 --- a/docs/development/plugin-triggers.md +++ b/docs/development/plugin-triggers.md @@ -428,6 +428,24 @@ case "$DOKKU_DISTRO" in esac ``` +### `deploy` + +- Description: Triggers a deploy for the given app. Can override the image tag to deploy, as well as specify a single process type to deploy. +- Invoked by: `dokku deploy` +- Arguments: `$APP [$IMAGE_TAG] [$PROC_TYPE]` +- Example: + +```shell +#!/usr/bin/env bash + +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" + +APP="$1" IMAGE_TAG="$2" PROC_TYPE="$3" + +# TODO +``` + ### `deploy-source` - Description: Used for reporting what the current detected deployment source is. The first detected source should always win. diff --git a/docs/processes/process-management.md b/docs/processes/process-management.md index 3499a2dfe39..d703051d918 100644 --- a/docs/processes/process-management.md +++ b/docs/processes/process-management.md @@ -6,7 +6,7 @@ ps:inspect # Displays a sanitized version of docker inspect for an app ps:rebuild [--parallel count] [--all|] # Rebuilds an app from source ps:report [] [] # Displays a process report for one or more apps -ps:restart [--parallel count] [--all|] # Restart an app +ps:restart [--parallel count] [--all|] [] # Restart an app ps:restore [] # Start previously running apps e.g. after reboot ps:scale [--skip-deploy] = [=...] # Get/Set how many instances of a given process to run ps:set # Set or clear a ps property for an app @@ -64,7 +64,13 @@ An app may be restarted using the `ps:restart` command. dokku ps:restart node-js-app ``` -All apps may be restarted by using the `--all` flag. +A single process type - such as `web` or `worker` - may also be specified. This _does not_ support specifying a given instance of a process type, and only supports restarting all instances of that process type. + +```shell +dokku ps:restart node-js-app web +``` + +All apps may be restarted by using the `--all` flag. This flag is incompatible with specifying a process type. ```shell dokku ps:restart --all diff --git a/plugins/apps/functions.go b/plugins/apps/functions.go index 492ddb44865..d19b8b37057 100644 --- a/plugins/apps/functions.go +++ b/plugins/apps/functions.go @@ -52,7 +52,7 @@ func destroyApp(appName string) error { common.LogInfo1(fmt.Sprintf("Destroying %s (including all add-ons)", appName)) - imageTag, _ := common.GetRunningImageTag(appName) + imageTag, _ := common.GetRunningImageTag(appName, "") if err := common.PlugnTrigger("pre-delete", []string{appName, imageTag}...); err != nil { return err } diff --git a/plugins/common/common.go b/plugins/common/common.go index 1d8ad86aa68..bd6516ae583 100644 --- a/plugins/common/common.go +++ b/plugins/common/common.go @@ -226,20 +226,21 @@ func GetAppRunningContainerIDs(appName string, containerType string) ([]string, return runningContainerIDs, nil } -// GetRunningImageTag retrieves current image tag for a given app and returns empty string if no deployed containers are found -func GetRunningImageTag(appName string) (string, error) { - containerIDs, err := GetAppContainerIDs(appName, "") +// GetRunningImageTag retrieves current deployed image tag for a given app +func GetRunningImageTag(appName string, imageTag string) (string, error) { + b, err := PlugnTriggerOutput("deployed-app-image-tag", []string{appName}...) if err != nil { - return "", err + return imageTag, err } - - for _, containerID := range containerIDs { - if image, err := DockerInspect(containerID, "{{ .Config.Image }}"); err == nil { - return strings.Split(image, ":")[1], nil - } + newImageTag := strings.TrimSpace(string(b[:])) + if newImageTag != "" { + imageTag = newImageTag + } + if imageTag == "" { + imageTag = "latest" } - return "", errors.New("No image tag found") + return imageTag, nil } // DokkuApps returns a list of all local apps diff --git a/plugins/common/deploy b/plugins/common/deploy new file mode 100755 index 00000000000..5a5b532add6 --- /dev/null +++ b/plugins/common/deploy @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x + +cmd-deploy "$@" diff --git a/plugins/common/functions b/plugins/common/functions index dd52d3fac04..b2bb8dd44d9 100755 --- a/plugins/common/functions +++ b/plugins/common/functions @@ -286,16 +286,14 @@ get_deploying_app_image_name() { declare desc="return deploying image identifier for a given app, tag tuple. validate if tag is presented" local APP="$1" local IMAGE_TAG="$2" - IMAGE_REPO="$3" + local IMAGE_REPO="$3" + IMAGE_TAG="$(get_running_image_tag "$APP" "$IMAGE_TAG")" local IMAGE_REMOTE_REPOSITORY=$(plugn trigger deployed-app-repository "$APP") - local NEW_IMAGE_TAG=$(plugn trigger deployed-app-image-tag "$APP") local NEW_IMAGE_REPO=$(plugn trigger deployed-app-image-repo "$APP") [[ -n "$NEW_IMAGE_REPO" ]] && IMAGE_REPO="$NEW_IMAGE_REPO" - [[ -n "$NEW_IMAGE_TAG" ]] && IMAGE_TAG="$NEW_IMAGE_TAG" [[ -z "$IMAGE_REPO" ]] && IMAGE_REPO=$(get_app_image_repo "$APP") - [[ -z "$IMAGE_TAG" ]] && IMAGE_TAG="latest" local IMAGE="${IMAGE_REMOTE_REPOSITORY}${IMAGE_REPO}:${IMAGE_TAG}" verify_image "$IMAGE" || dokku_log_fail "App image ($IMAGE) not found" @@ -329,12 +327,14 @@ get_app_scheduler() { } get_running_image_tag() { - declare desc="retrieve current image tag for a given app. returns empty string if no deployed containers are found" - local APP="$1" + declare desc="retrieves current deployed image tag for a given app" + local APP="$1" IMAGE_TAG="$2" - local CIDS=($(get_app_container_ids "$APP")) - local RUNNING_IMAGE_TAG=$("$DOCKER_BIN" container inspect --format '{{ .Config.Image }}' "${CIDS[0]}" 2>/dev/null | awk -F: '{ print $2 }' || echo '') - echo "$RUNNING_IMAGE_TAG" + local NEW_IMAGE_TAG=$(plugn trigger deployed-app-image-tag "$APP") + [[ -n "$NEW_IMAGE_TAG" ]] && IMAGE_TAG="$NEW_IMAGE_TAG" + [[ -z "$IMAGE_TAG" ]] && IMAGE_TAG="latest" + + echo "$IMAGE_TAG" } is_image_cnb_based() { @@ -638,12 +638,11 @@ dokku_release() { cmd-deploy() { declare desc="deploy phase" - declare APP="$1" IMAGE_TAG="$2" - source "$PLUGIN_AVAILABLE_PATH/config/functions" + declare APP="$1" IMAGE_TAG="$2" PROCESS_TYPE="$3" verify_app_name "$APP" local DOKKU_SCHEDULER=$(get_app_scheduler "$APP") - plugn trigger scheduler-deploy "$DOKKU_SCHEDULER" "$APP" "$IMAGE_TAG" + plugn trigger scheduler-deploy "$DOKKU_SCHEDULER" "$APP" "$IMAGE_TAG" "$PROCESS_TYPE" } release_and_deploy() { diff --git a/plugins/config/config.go b/plugins/config/config.go index 45fb9fb2095..17b478cc86e 100644 --- a/plugins/config/config.go +++ b/plugins/config/config.go @@ -117,7 +117,7 @@ func UnsetAll(appName string, restart bool) (err error) { func triggerRestart(appName string) { common.LogInfo1(fmt.Sprintf("Restarting app %s", appName)) - if err := common.PlugnTrigger("app-restart", appName); err != nil { + if err := common.PlugnTrigger("release-and-deploy", appName); err != nil { common.LogWarn(fmt.Sprintf("Failure while restarting app: %s", err)) } } diff --git a/plugins/ps/functions.go b/plugins/ps/functions.go index 991fdaa64b6..e62bffbed19 100644 --- a/plugins/ps/functions.go +++ b/plugins/ps/functions.go @@ -282,12 +282,18 @@ func scaleSet(appName string, skipDeploy bool, clearExisting bool, processTuples return nil } - imageTag, err := common.GetRunningImageTag(appName) + imageTag, err := common.GetRunningImageTag(appName, "") if err != nil { return err } - return common.PlugnTrigger("release-and-deploy", []string{appName, imageTag}...) + for _, formation := range formations { + if err := common.PlugnTrigger("deploy", []string{appName, imageTag, formation.ProcessType}...); err != nil { + return err + } + } + + return nil } func updateScale(appName string, clearExisting bool, formationUpdates FormationSlice) error { diff --git a/plugins/ps/ps.go b/plugins/ps/ps.go index 8b2ecddcf78..0c7287f5cde 100644 --- a/plugins/ps/ps.go +++ b/plugins/ps/ps.go @@ -57,7 +57,37 @@ func Restart(appName string) error { return nil } - return common.PlugnTrigger("release-and-deploy", []string{appName}...) + imageTag, err := common.GetRunningImageTag(appName, "") + if err != nil { + return err + } + + if imageTag == "" { + common.LogWarn("No deployed-image-tag property saved, falling back to full release-and-deploy") + return common.PlugnTrigger("release-and-deploy", []string{appName}...) + } + + return common.PlugnTrigger("deploy", []string{appName, imageTag}...) +} + +// RestartProcess restarts a process type within an app +func RestartProcess(appName string, processName string) error { + if !common.IsDeployed(appName) { + common.LogWarn(fmt.Sprintf("App %s has not been deployed", appName)) + return nil + } + + imageTag, err := common.GetRunningImageTag(appName, "") + if err != nil { + return err + } + + if imageTag == "" { + common.LogWarn("No deployed-image-tag property saved, falling back to full release-and-deploy") + return common.PlugnTrigger("release-and-deploy", []string{appName}...) + } + + return common.PlugnTrigger("deploy", []string{appName, imageTag, processName}...) } // Restore ensures an app that should be running is running on boot @@ -90,7 +120,7 @@ func Restore(appName string) error { // Start starts the app func Start(appName string) error { - imageTag, _ := common.GetRunningImageTag(appName) + imageTag, _ := common.GetRunningImageTag(appName, "") if !common.IsDeployed(appName) { common.LogWarn(fmt.Sprintf("App %s has not been deployed", appName)) diff --git a/plugins/ps/src/commands/commands.go b/plugins/ps/src/commands/commands.go index d578af0e731..00eb60d4542 100644 --- a/plugins/ps/src/commands/commands.go +++ b/plugins/ps/src/commands/commands.go @@ -21,7 +21,7 @@ Additional commands:` ps:inspect , Displays a sanitized version of docker inspect for an app ps:rebuild [--parallel count] [--all|], Rebuilds an app from source ps:report [] [], Displays a process report for one or more apps - ps:restart [--parallel count] [--all|], Restart an app + ps:restart [--parallel count] [--all|] [], Restart an app ps:restore [], Start previously running apps e.g. after reboot ps:scale [--skip-deploy] = [=...], Get/Set how many instances of a given process to run ps:set , Set or clear a ps property for an app diff --git a/plugins/ps/src/subcommands/subcommands.go b/plugins/ps/src/subcommands/subcommands.go index 306daea8a12..a970fef3378 100644 --- a/plugins/ps/src/subcommands/subcommands.go +++ b/plugins/ps/src/subcommands/subcommands.go @@ -45,7 +45,8 @@ func main() { parallelCount := args.Int("parallel", ps.RunInSerial, "--parallel: number of apps to restart in parallel, -1 to match cpu count") args.Parse(os.Args[2:]) appName := args.Arg(0) - err = ps.CommandRestart(appName, *allApps, *parallelCount) + processName := args.Arg(1) + err = ps.CommandRestart(appName, processName, *allApps, *parallelCount) case "restore": args := flag.NewFlagSet("ps:restore", flag.ExitOnError) allApps := args.Bool("all", false, "--all: restore all apps") diff --git a/plugins/ps/subcommands.go b/plugins/ps/subcommands.go index 3c4ea73a72f..ed4ecaba07b 100644 --- a/plugins/ps/subcommands.go +++ b/plugins/ps/subcommands.go @@ -54,8 +54,11 @@ func CommandReport(appName string, format string, infoFlag string) error { } // CommandRestart restarts an app -func CommandRestart(appName string, allApps bool, parallelCount int) error { +func CommandRestart(appName string, processName string, allApps bool, parallelCount int) error { if allApps { + if processName != "" { + return errors.New("Unable to restart all apps when specifying a process name") + } return common.RunCommandAgainstAllApps(Restart, "restart", parallelCount) } @@ -63,6 +66,10 @@ func CommandRestart(appName string, allApps bool, parallelCount int) error { return err } + if processName != "" { + return RestartProcess(appName, processName) + } + return Restart(appName) } diff --git a/plugins/registry/functions.go b/plugins/registry/functions.go index 62d85812a87..0644f75332d 100644 --- a/plugins/registry/functions.go +++ b/plugins/registry/functions.go @@ -57,7 +57,7 @@ func pushToRegistry(appName string, tag int, imageID string, imageRepo string) e common.LogVerboseQuiet("Retrieving image info for app") registryServer := getRegistryServerForApp(appName) - imageTag, _ := common.GetRunningImageTag(appName) + imageTag, _ := common.GetRunningImageTag(appName, "") fullImage := fmt.Sprintf("%s%s:%d", registryServer, imageRepo, tag) diff --git a/plugins/scheduler-docker-local/scheduler-deploy b/plugins/scheduler-docker-local/scheduler-deploy index b66531dfffb..4b38d48b7fe 100755 --- a/plugins/scheduler-docker-local/scheduler-deploy +++ b/plugins/scheduler-docker-local/scheduler-deploy @@ -9,7 +9,8 @@ source "$PLUGIN_AVAILABLE_PATH/scheduler-docker-local/internal-functions" trigger-scheduler-docker-local-scheduler-deploy() { declare desc="deploys an image tag for a given application" declare trigger="scheduler-deploy" - declare DOKKU_SCHEDULER="$1" APP="$2" IMAGE_TAG="$3" + declare DOKKU_SCHEDULER="$1" APP="$2" IMAGE_TAG="$3" PROCESS_TYPE="$4" + local PROCESS_TYPE if [[ "$DOKKU_SCHEDULER" != "docker-local" ]]; then return @@ -28,7 +29,7 @@ trigger-scheduler-docker-local-scheduler-deploy() { local IMAGE_SOURCE_TYPE="dockerfile" [[ "$DOKKU_HEROKUISH" == "true" ]] && IMAGE_SOURCE_TYPE="herokuish" [[ "$DOKKU_CNB" == "true" ]] && IMAGE_SOURCE_TYPE="pack" - local oldids=$(get_app_container_ids "$APP") + 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)" @@ -54,6 +55,10 @@ trigger-scheduler-docker-local-scheduler-deploy() { local PROC_TYPE=${line%%=*} local PROC_COUNT=${line#*=} + if [[ -n "$PROCESS_TYPE" ]] && [[ "$PROC_TYPE" != "$PROCESS_TYPE" ]]; then + 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