Bläddra i källkod

introduce wait flag on apply that will wait for app revision status ready, also refactor update-tag

Feroze Mohideen 2 år sedan
förälder
incheckning
4d6c1ba2ac
6 ändrade filer med 149 tillägg och 110 borttagningar
  1. 9 1
      cli/cmd/commands/app.go
  2. 16 1
      cli/cmd/commands/apply.go
  3. 68 0
      cli/cmd/v2/app_status.go
  4. 31 31
      cli/cmd/v2/apply.go
  5. 21 21
      cli/cmd/v2/update.go
  6. 4 56
      cli/cmd/v2/update_image.go

+ 9 - 1
cli/cmd/commands/app.go

@@ -1258,7 +1258,7 @@ func appUpdateTag(ctx context.Context, user *types.GetAuthenticatedUserResponse,
 	}
 
 	if project.ValidateApplyV2 {
-		err := v2.UpdateImage(ctx, v2.UpdateImageInput{
+		revisionId, err := v2.UpdateImage(ctx, v2.UpdateImageInput{
 			ProjectID:               cliConf.Project,
 			ClusterID:               cliConf.Cluster,
 			AppName:                 args[0],
@@ -1270,6 +1270,14 @@ func appUpdateTag(ctx context.Context, user *types.GetAuthenticatedUserResponse,
 		if err != nil {
 			return fmt.Errorf("error updating tag: %w", err)
 		}
+		if waitForSuccessfulUpdate {
+			return v2.WaitForAppRevisionStatus(ctx, v2.WaitForAppRevisionStatusInput{
+				ProjectID:  cliConf.Project,
+				ClusterID:  cliConf.Cluster,
+				AppName:    args[0],
+				RevisionID: revisionId,
+			})
+		}
 		return nil
 	} else {
 		namespace := fmt.Sprintf("porter-stack-%s", args[0])

+ 16 - 1
cli/cmd/commands/apply.go

@@ -107,6 +107,13 @@ applying a configuration:
 
 	applyCmd.PersistentFlags().StringVarP(&porterYAML, "file", "f", "", "path to porter.yaml")
 	applyCmd.PersistentFlags().BoolVarP(&previewApply, "preview", "p", false, "apply as preview environment based on current git branch")
+	applyCmd.PersistentFlags().BoolVarP(
+		&waitForSuccessfulUpdate,
+		"wait",
+		"w",
+		false,
+		"set this to wait and be notified when an apply is successful, otherwise time out",
+	)
 	applyCmd.MarkFlagRequired("file")
 
 	return applyCmd
@@ -142,10 +149,18 @@ func apply(ctx context.Context, _ *types.GetAuthenticatedUserResponse, client ap
 			AppName:        appName,
 			PreviewApply:   previewApply,
 		}
-		err = v2.Apply(ctx, inp)
+		revisionId, err := v2.Apply(ctx, inp)
 		if err != nil {
 			return err
 		}
+		if waitForSuccessfulUpdate {
+			return v2.WaitForAppRevisionStatus(ctx, v2.WaitForAppRevisionStatusInput{
+				ProjectID:  cliConfig.Project,
+				ClusterID:  cliConfig.Cluster,
+				AppName:    appName,
+				RevisionID: revisionId,
+			})
+		}
 		return nil
 	}
 

+ 68 - 0
cli/cmd/v2/app_status.go

@@ -0,0 +1,68 @@
+package v2
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"time"
+
+	"github.com/fatih/color"
+	api "github.com/porter-dev/porter/api/client"
+	"github.com/porter-dev/porter/api/server/handlers/porter_app"
+)
+
+const (
+	// DefaultWaitTimeoutMinutes is the default timeout for waiting for an update-image to complete
+	DefaultWaitTimeoutMinutes = 10
+	// DefaultRetryFrequencySeconds is the default frequency for checking the status of an update-image
+	DefaultRetryFrequencySeconds = 10
+)
+
+type WaitForAppRevisionStatusInput struct {
+	ProjectID  uint
+	ClusterID  uint
+	AppName    string
+	RevisionID string
+	Client     api.Client
+}
+
+func WaitForAppRevisionStatus(ctx context.Context, input WaitForAppRevisionStatusInput) error {
+	timeoutMinutes := DefaultWaitTimeoutMinutes
+	timeout := time.Duration(timeoutMinutes) * time.Minute
+	deadline := time.Now().Add(timeout)
+
+	color.New(color.FgBlue).Printf("Waiting %d minutes for update to complete\n", timeoutMinutes) // nolint:errcheck,gosec
+
+	var status porter_app.HighLevelStatus
+
+	for time.Now().Before(deadline) {
+		statusResp, err := input.Client.GetRevisionStatus(ctx, input.ProjectID, input.ClusterID, input.AppName, input.RevisionID)
+		if err != nil {
+			return fmt.Errorf("error getting app revision status: %w", err)
+		}
+
+		if statusResp == nil {
+			return errors.New("unable to determine status of app revision")
+		}
+
+		status = statusResp.HighLevelStatus
+
+		if status != porter_app.HighLevelStatus_Progressing {
+			break
+		}
+
+		time.Sleep(DefaultRetryFrequencySeconds * time.Second)
+	}
+
+	switch status {
+	case porter_app.HighLevelStatus_Progressing:
+		return fmt.Errorf("timeout exceeded")
+	case porter_app.HighLevelStatus_Successful:
+		_, _ = color.New(color.FgGreen).Printf("Update completed successfully\n") // nolint:errcheck,gosec
+		return nil
+	case porter_app.HighLevelStatus_Failed:
+		return fmt.Errorf("update failed: check dashboard for details")
+	default:
+		return fmt.Errorf("received unknown status: %s", status)
+	}
+}

+ 31 - 31
cli/cmd/v2/apply.go

@@ -38,7 +38,7 @@ type ApplyInput struct {
 }
 
 // Apply implements the functionality of the `porter apply` command for validate apply v2 projects
-func Apply(ctx context.Context, inp ApplyInput) error {
+func Apply(ctx context.Context, inp ApplyInput) (string, error) {
 	const forceBuild = true
 	var b64AppProto string
 
@@ -47,7 +47,7 @@ func Apply(ctx context.Context, inp ApplyInput) error {
 
 	useNewApplyResp, err := client.UseNewApplyLogic(ctx, cliConf.Project, cliConf.Cluster)
 	if err != nil {
-		return fmt.Errorf("error checking if project uses new apply logic: %w", err)
+		return "", fmt.Errorf("error checking if project uses new apply logic: %w", err)
 	}
 
 	if useNewApplyResp.UseNewApplyLogic {
@@ -56,7 +56,7 @@ func Apply(ctx context.Context, inp ApplyInput) error {
 
 	deploymentTargetID, err := deploymentTargetFromConfig(ctx, client, cliConf.Project, cliConf.Cluster, inp.PreviewApply)
 	if err != nil {
-		return fmt.Errorf("error getting deployment target from config: %w", err)
+		return "", fmt.Errorf("error getting deployment target from config: %w", err)
 	}
 
 	var prNumber int
@@ -64,7 +64,7 @@ func Apply(ctx context.Context, inp ApplyInput) error {
 	if prNumberEnv != "" {
 		prNumber, err = strconv.Atoi(prNumberEnv)
 		if err != nil {
-			return fmt.Errorf("error parsing PORTER_PR_NUMBER to int: %w", err)
+			return "", fmt.Errorf("error parsing PORTER_PR_NUMBER to int: %w", err)
 		}
 	}
 
@@ -74,7 +74,7 @@ func Apply(ctx context.Context, inp ApplyInput) error {
 		_, err := os.Stat(filepath.Clean(inp.PorterYamlPath))
 		if err != nil {
 			if !os.IsNotExist(err) {
-				return fmt.Errorf("error checking if porter yaml exists at path %s: %w", inp.PorterYamlPath, err)
+				return "", fmt.Errorf("error checking if porter yaml exists at path %s: %w", inp.PorterYamlPath, err)
 			}
 			// If a path was specified but the file does not exist, we will not immediately error out.
 			// This supports users migrated from v1 who use a workflow file that always specifies a porter yaml path
@@ -94,7 +94,7 @@ func Apply(ctx context.Context, inp ApplyInput) error {
 	if porterYamlExists {
 		porterYaml, err := os.ReadFile(filepath.Clean(inp.PorterYamlPath))
 		if err != nil {
-			return fmt.Errorf("could not read porter yaml file: %w", err)
+			return "", fmt.Errorf("could not read porter yaml file: %w", err)
 		}
 
 		b64YAML := base64.StdEncoding.EncodeToString(porterYaml)
@@ -102,11 +102,11 @@ func Apply(ctx context.Context, inp ApplyInput) error {
 		// last argument is passed to accommodate users with v1 porter yamls
 		parseResp, err := client.ParseYAML(ctx, cliConf.Project, cliConf.Cluster, b64YAML, appName)
 		if err != nil {
-			return fmt.Errorf("error calling parse yaml endpoint: %w", err)
+			return "", fmt.Errorf("error calling parse yaml endpoint: %w", err)
 		}
 
 		if parseResp.B64AppProto == "" {
-			return errors.New("b64 app proto is empty")
+			return "", errors.New("b64 app proto is empty")
 		}
 		b64AppProto = parseResp.B64AppProto
 
@@ -117,13 +117,13 @@ func Apply(ctx context.Context, inp ApplyInput) error {
 		// override app name if provided
 		appName, err = appNameFromB64AppProto(parseResp.B64AppProto)
 		if err != nil {
-			return fmt.Errorf("error getting app name from porter.yaml: %w", err)
+			return "", fmt.Errorf("error getting app name from porter.yaml: %w", err)
 		}
 
 		// we only need to create the app if a porter yaml is provided (otherwise it must already exist)
 		createPorterAppDBEntryInp, err := createPorterAppDbEntryInputFromProtoAndEnv(parseResp.B64AppProto)
 		if err != nil {
-			return fmt.Errorf("unable to form porter app creation input from yaml: %w", err)
+			return "", fmt.Errorf("unable to form porter app creation input from yaml: %w", err)
 		}
 
 		createPorterAppDBEntryInp.DeploymentTargetID = deploymentTargetID
@@ -131,9 +131,9 @@ func Apply(ctx context.Context, inp ApplyInput) error {
 		err = client.CreatePorterAppDBEntry(ctx, cliConf.Project, cliConf.Cluster, createPorterAppDBEntryInp)
 		if err != nil {
 			if err.Error() == porter_app.ErrMissingSourceType.Error() {
-				return fmt.Errorf("cannot find existing Porter app with name %s and no build or image settings were specified in porter.yaml", appName)
+				return "", fmt.Errorf("cannot find existing Porter app with name %s and no build or image settings were specified in porter.yaml", appName)
 			}
-			return fmt.Errorf("unable to create porter app from yaml: %w", err)
+			return "", fmt.Errorf("unable to create porter app from yaml: %w", err)
 		}
 
 		color.New(color.FgGreen).Printf("Successfully parsed Porter YAML: applying app \"%s\"\n", appName) // nolint:errcheck,gosec
@@ -150,7 +150,7 @@ func Apply(ctx context.Context, inp ApplyInput) error {
 	}
 
 	if appName == "" {
-		return errors.New("App name is empty.  Please provide a Porter YAML file specifying the name of the app or set the PORTER_APP_NAME environment variable.")
+		return "", errors.New("App name is empty.  Please provide a Porter YAML file specifying the name of the app or set the PORTER_APP_NAME environment variable.")
 	}
 
 	commitSHA := commitSHAFromEnv()
@@ -165,11 +165,11 @@ func Apply(ctx context.Context, inp ApplyInput) error {
 		CommitSHA:          commitSHA,
 	})
 	if err != nil {
-		return fmt.Errorf("error calling validate endpoint: %w", err)
+		return "", fmt.Errorf("error calling validate endpoint: %w", err)
 	}
 
 	if validateResp.ValidatedBase64AppProto == "" {
-		return errors.New("validated b64 app proto is empty")
+		return "", errors.New("validated b64 app proto is empty")
 	}
 	base64AppProto := validateResp.ValidatedBase64AppProto
 
@@ -185,11 +185,11 @@ func Apply(ctx context.Context, inp ApplyInput) error {
 
 	applyResp, err := client.ApplyPorterApp(ctx, applyInput)
 	if err != nil {
-		return fmt.Errorf("error calling apply endpoint: %w", err)
+		return "", fmt.Errorf("error calling apply endpoint: %w", err)
 	}
 
 	if applyResp.AppRevisionId == "" {
-		return errors.New("app revision id is empty")
+		return "", errors.New("app revision id is empty")
 	}
 
 	if applyResp.CLIAction == porterv1.EnumCLIAction_ENUM_CLI_ACTION_BUILD {
@@ -212,7 +212,7 @@ func Apply(ctx context.Context, inp ApplyInput) error {
 			err := errors.New("Build is required but commit SHA cannot be identified. Please set the PORTER_COMMIT_SHA environment variable or run apply in git repository with access to the git CLI.")
 			reportBuildFailureInput.buildError = err
 			_ = reportBuildFailure(ctx, reportBuildFailureInput)
-			return err
+			return "", err
 		}
 
 		buildSettings, err := buildSettingsFromBase64AppProto(base64AppProto)
@@ -220,7 +220,7 @@ func Apply(ctx context.Context, inp ApplyInput) error {
 			err := fmt.Errorf("error getting build settings from base64 app proto: %w", err)
 			reportBuildFailureInput.buildError = err
 			_ = reportBuildFailure(ctx, reportBuildFailureInput)
-			return err
+			return "", err
 		}
 
 		currentAppRevisionResp, err := client.CurrentAppRevision(ctx, cliConf.Project, cliConf.Cluster, appName, deploymentTargetID)
@@ -228,14 +228,14 @@ func Apply(ctx context.Context, inp ApplyInput) error {
 			err := fmt.Errorf("error getting current app revision: %w", err)
 			reportBuildFailureInput.buildError = err
 			_ = reportBuildFailure(ctx, reportBuildFailureInput)
-			return err
+			return "", err
 		}
 
 		if currentAppRevisionResp == nil {
 			err := errors.New("current app revision is nil")
 			reportBuildFailureInput.buildError = err
 			_ = reportBuildFailure(ctx, reportBuildFailureInput)
-			return err
+			return "", err
 		}
 
 		appRevision := currentAppRevisionResp.AppRevision
@@ -243,7 +243,7 @@ func Apply(ctx context.Context, inp ApplyInput) error {
 			err := errors.New("current app revision b64 app proto is empty")
 			reportBuildFailureInput.buildError = err
 			_ = reportBuildFailure(ctx, reportBuildFailureInput)
-			return err
+			return "", err
 		}
 
 		currentImageTag, err := imageTagFromBase64AppProto(appRevision.B64AppProto)
@@ -251,7 +251,7 @@ func Apply(ctx context.Context, inp ApplyInput) error {
 			err := fmt.Errorf("error getting image tag from current app revision: %w", err)
 			reportBuildFailureInput.buildError = err
 			_ = reportBuildFailure(ctx, reportBuildFailureInput)
-			return err
+			return "", err
 		}
 
 		buildSettings.CurrentImageTag = currentImageTag
@@ -262,7 +262,7 @@ func Apply(ctx context.Context, inp ApplyInput) error {
 			err := fmt.Errorf("error getting build env: %w", err)
 			reportBuildFailureInput.buildError = err
 			_ = reportBuildFailure(ctx, reportBuildFailureInput)
-			return err
+			return "", err
 		}
 		buildSettings.Env = buildEnv.BuildEnvVariables
 
@@ -272,7 +272,7 @@ func Apply(ctx context.Context, inp ApplyInput) error {
 			reportBuildFailureInput.buildLogs = buildOutput.Logs
 			reportBuildFailureInput.buildError = buildOutput.Error
 			_ = reportBuildFailure(ctx, reportBuildFailureInput)
-			return err
+			return "", err
 		}
 
 		color.New(color.FgGreen).Printf("Successfully built image (tag: %s)\n", buildSettings.ImageTag) // nolint:errcheck,gosec
@@ -290,7 +290,7 @@ func Apply(ctx context.Context, inp ApplyInput) error {
 
 		applyResp, err = client.ApplyPorterApp(ctx, applyInput)
 		if err != nil {
-			return fmt.Errorf("apply error post-build: %w", err)
+			return "", fmt.Errorf("apply error post-build: %w", err)
 		}
 	}
 
@@ -309,7 +309,7 @@ func Apply(ctx context.Context, inp ApplyInput) error {
 				metadata["end_time"] = time.Now().UTC()
 				_ = updateExistingEvent(ctx, client, appName, cliConf.Project, cliConf.Cluster, deploymentTargetID, types.PorterAppEventType_PreDeploy, eventID, eventStatus, metadata)
 
-				return errors.New("timed out waiting for predeploy to complete")
+				return "", errors.New("timed out waiting for predeploy to complete")
 			}
 
 			predeployStatusResp, err := client.PredeployStatus(ctx, cliConf.Project, cliConf.Cluster, appName, applyResp.AppRevisionId)
@@ -318,7 +318,7 @@ func Apply(ctx context.Context, inp ApplyInput) error {
 				metadata["end_time"] = time.Now().UTC()
 				_ = updateExistingEvent(ctx, client, appName, cliConf.Project, cliConf.Cluster, deploymentTargetID, types.PorterAppEventType_PreDeploy, eventID, eventStatus, metadata)
 
-				return fmt.Errorf("error calling predeploy status endpoint: %w", err)
+				return "", fmt.Errorf("error calling predeploy status endpoint: %w", err)
 			}
 
 			if predeployStatusResp.Status == porter_app.PredeployStatus_Failed {
@@ -344,12 +344,12 @@ func Apply(ctx context.Context, inp ApplyInput) error {
 
 		applyResp, err = client.ApplyPorterApp(ctx, applyInput)
 		if err != nil {
-			return fmt.Errorf("apply error post-predeploy: %w", err)
+			return "", fmt.Errorf("apply error post-predeploy: %w", err)
 		}
 	}
 
 	if applyResp.CLIAction != porterv1.EnumCLIAction_ENUM_CLI_ACTION_NONE {
-		return fmt.Errorf("unexpected CLI action: %s", applyResp.CLIAction)
+		return "", fmt.Errorf("unexpected CLI action: %s", applyResp.CLIAction)
 	}
 
 	_, _ = client.ReportRevisionStatus(ctx, api.ReportRevisionStatusInput{
@@ -362,7 +362,7 @@ func Apply(ctx context.Context, inp ApplyInput) error {
 	})
 
 	color.New(color.FgGreen).Printf("Successfully applied new revision %s for app %s\n", applyResp.AppRevisionId, appName) // nolint:errcheck,gosec
-	return nil
+	return applyResp.AppRevisionId, nil
 }
 
 func commitSHAFromEnv() string {

+ 21 - 21
cli/cmd/v2/update.go

@@ -36,7 +36,7 @@ type UpdateInput struct {
 }
 
 // Update implements the functionality of the `porter apply` command for validate apply v2 projects
-func Update(ctx context.Context, inp UpdateInput) error {
+func Update(ctx context.Context, inp UpdateInput) (string, error) {
 	ctx, cancel := context.WithCancel(ctx)
 	defer cancel()
 
@@ -56,7 +56,7 @@ func Update(ctx context.Context, inp UpdateInput) error {
 
 	deploymentTargetID, err := deploymentTargetFromConfig(ctx, client, cliConf.Project, cliConf.Cluster, inp.PreviewApply)
 	if err != nil {
-		return fmt.Errorf("error getting deployment target from config: %w", err)
+		return "", fmt.Errorf("error getting deployment target from config: %w", err)
 	}
 
 	var prNumber int
@@ -64,7 +64,7 @@ func Update(ctx context.Context, inp UpdateInput) error {
 	if prNumberEnv != "" {
 		prNumber, err = strconv.Atoi(prNumberEnv)
 		if err != nil {
-			return fmt.Errorf("error parsing PORTER_PR_NUMBER to int: %w", err)
+			return "", fmt.Errorf("error parsing PORTER_PR_NUMBER to int: %w", err)
 		}
 	}
 
@@ -74,7 +74,7 @@ func Update(ctx context.Context, inp UpdateInput) error {
 		_, err := os.Stat(filepath.Clean(inp.PorterYamlPath))
 		if err != nil {
 			if !os.IsNotExist(err) {
-				return fmt.Errorf("error checking if porter yaml exists at path %s: %w", inp.PorterYamlPath, err)
+				return "", fmt.Errorf("error checking if porter yaml exists at path %s: %w", inp.PorterYamlPath, err)
 			}
 			// If a path was specified but the file does not exist, we will not immediately error out.
 			// This supports users migrated from v1 who use a workflow file that always specifies a porter yaml path
@@ -87,7 +87,7 @@ func Update(ctx context.Context, inp UpdateInput) error {
 	if porterYamlExists {
 		porterYaml, err := os.ReadFile(filepath.Clean(inp.PorterYamlPath))
 		if err != nil {
-			return fmt.Errorf("could not read porter yaml file: %w", err)
+			return "", fmt.Errorf("could not read porter yaml file: %w", err)
 		}
 
 		b64YAML = base64.StdEncoding.EncodeToString(porterYaml)
@@ -97,7 +97,7 @@ func Update(ctx context.Context, inp UpdateInput) error {
 	commitSHA := commitSHAFromEnv()
 	gitSource, err := gitSourceFromEnv()
 	if err != nil {
-		return fmt.Errorf("error getting git source from env: %w", err)
+		return "", fmt.Errorf("error getting git source from env: %w", err)
 	}
 
 	updateInput := api.UpdateAppInput{
@@ -112,18 +112,18 @@ func Update(ctx context.Context, inp UpdateInput) error {
 
 	updateResp, err := client.UpdateApp(ctx, updateInput)
 	if err != nil {
-		return fmt.Errorf("error calling update app endpoint: %w", err)
+		return "", fmt.Errorf("error calling update app endpoint: %w", err)
 	}
 
 	if updateResp.AppRevisionId == "" {
-		return errors.New("app revision id is empty")
+		return "", errors.New("app revision id is empty")
 	}
 
 	appName := updateResp.AppName
 
 	buildSettings, err := client.GetBuildFromRevision(ctx, cliConf.Project, cliConf.Cluster, appName, updateResp.AppRevisionId)
 	if err != nil {
-		return fmt.Errorf("error getting build from revision: %w", err)
+		return "", fmt.Errorf("error getting build from revision: %w", err)
 	}
 
 	if buildSettings != nil && buildSettings.Build.Method != "" {
@@ -159,7 +159,7 @@ func Update(ctx context.Context, inp UpdateInput) error {
 		}()
 
 		if commitSHA == "" {
-			return errors.New("build is required but commit SHA cannot be identified. Please set the PORTER_COMMIT_SHA environment variable or run apply in git repository with access to the git CLI")
+			return "", errors.New("build is required but commit SHA cannot be identified. Please set the PORTER_COMMIT_SHA environment variable or run apply in git repository with access to the git CLI")
 		}
 
 		color.New(color.FgGreen).Printf("Building new image with tag %s...\n", commitSHA) // nolint:errcheck,gosec
@@ -174,20 +174,20 @@ func Update(ctx context.Context, inp UpdateInput) error {
 		})
 		if err != nil {
 			buildError = fmt.Errorf("error creating build input from build settings: %w", err)
-			return buildError
+			return "", buildError
 		}
 
 		buildOutput := build(ctx, client, buildInput)
 		if buildOutput.Error != nil {
 			buildError = fmt.Errorf("error building app: %w", buildOutput.Error)
 			buildLogs = buildOutput.Logs
-			return buildError
+			return "", buildError
 		}
 
 		_, err = client.UpdateRevisionStatus(ctx, cliConf.Project, cliConf.Cluster, appName, updateResp.AppRevisionId, models.AppRevisionStatus_BuildSuccessful)
 		if err != nil {
 			buildError = fmt.Errorf("error updating revision status post build: %w", err)
-			return buildError
+			return "", buildError
 		}
 
 		color.New(color.FgGreen).Printf("Successfully built image (tag: %s)\n", commitSHA) // nolint:errcheck,gosec
@@ -204,16 +204,16 @@ func Update(ctx context.Context, inp UpdateInput) error {
 
 	for {
 		if time.Since(now) > checkDeployTimeout {
-			return errors.New("timed out waiting for app to deploy")
+			return "", errors.New("timed out waiting for app to deploy")
 		}
 
 		status, err := client.GetRevisionStatus(ctx, cliConf.Project, cliConf.Cluster, appName, updateResp.AppRevisionId)
 		if err != nil {
-			return fmt.Errorf("error getting app revision status: %w", err)
+			return "", fmt.Errorf("error getting app revision status: %w", err)
 		}
 
 		if status == nil {
-			return errors.New("unable to determine status of app revision")
+			return "", errors.New("unable to determine status of app revision")
 		}
 
 		if status.AppRevisionStatus.IsInTerminalStatus {
@@ -242,22 +242,22 @@ func Update(ctx context.Context, inp UpdateInput) error {
 
 	status, err := client.GetRevisionStatus(ctx, cliConf.Project, cliConf.Cluster, appName, updateResp.AppRevisionId)
 	if err != nil {
-		return fmt.Errorf("error getting app revision status: %w", err)
+		return "", fmt.Errorf("error getting app revision status: %w", err)
 	}
 
 	if status == nil {
-		return errors.New("unable to determine status of app revision")
+		return "", errors.New("unable to determine status of app revision")
 	}
 
 	if status.AppRevisionStatus.InstallFailed {
-		return errors.New("app failed to deploy")
+		return "", errors.New("app failed to deploy")
 	}
 	if status.AppRevisionStatus.PredeployFailed {
-		return errors.New("predeploy failed for new revision")
+		return "", errors.New("predeploy failed for new revision")
 	}
 
 	color.New(color.FgGreen).Printf("Successfully applied new revision %s\n", updateResp.AppRevisionId) // nolint:errcheck,gosec
-	return nil
+	return updateResp.AppRevisionId, nil
 }
 
 // checkDeployTimeout is the timeout for checking if an app has been deployed

+ 4 - 56
cli/cmd/v2/update_image.go

@@ -4,9 +4,6 @@ import (
 	"context"
 	"errors"
 	"fmt"
-	"time"
-
-	"github.com/porter-dev/porter/api/server/handlers/porter_app"
 
 	"github.com/fatih/color"
 
@@ -24,17 +21,10 @@ type UpdateImageInput struct {
 	Client                  api.Client
 }
 
-const (
-	// DefaultWaitTimeoutMinutes is the default timeout for waiting for an update-image to complete
-	DefaultWaitTimeoutMinutes = 10
-	// DefaultRetryFrequencySeconds is the default frequency for checking the status of an update-image
-	DefaultRetryFrequencySeconds = 10
-)
-
 // UpdateImage updates the image of an application
-func UpdateImage(ctx context.Context, input UpdateImageInput) error {
+func UpdateImage(ctx context.Context, input UpdateImageInput) (string, error) {
 	if input.DeploymentTargetName == "" {
-		return errors.New("please provide a deployment target")
+		return "", errors.New("please provide a deployment target")
 	}
 
 	tag := input.Tag
@@ -44,7 +34,7 @@ func UpdateImage(ctx context.Context, input UpdateImageInput) error {
 
 	resp, err := input.Client.UpdateImage(ctx, input.ProjectID, input.ClusterID, input.AppName, input.DeploymentTargetName, tag)
 	if err != nil {
-		return fmt.Errorf("unable to update image: %w", err)
+		return "", fmt.Errorf("unable to update image: %w", err)
 	}
 
 	triggeredBackgroundColor := color.FgGreen
@@ -53,47 +43,5 @@ func UpdateImage(ctx context.Context, input UpdateImageInput) error {
 	}
 
 	_, _ = color.New(triggeredBackgroundColor).Printf("Updated application %s to use tag \"%s\"\n", input.AppName, tag)
-
-	if !input.WaitForSuccessfulUpdate {
-		return nil
-	}
-
-	timeoutMinutes := DefaultWaitTimeoutMinutes
-	timeout := time.Duration(timeoutMinutes) * time.Minute
-	deadline := time.Now().Add(timeout)
-
-	color.New(color.FgBlue).Printf("Waiting %d minutes for update to complete\n", timeoutMinutes) // nolint:errcheck,gosec
-
-	var status porter_app.HighLevelStatus
-
-	for time.Now().Before(deadline) {
-		statusResp, err := input.Client.GetRevisionStatus(ctx, input.ProjectID, input.ClusterID, input.AppName, resp.RevisionID)
-		if err != nil {
-			return fmt.Errorf("error getting app revision status: %w", err)
-		}
-
-		if statusResp == nil {
-			return errors.New("unable to determine status of app revision")
-		}
-
-		status = statusResp.HighLevelStatus
-
-		if status != porter_app.HighLevelStatus_Progressing {
-			break
-		}
-
-		time.Sleep(DefaultRetryFrequencySeconds * time.Second)
-	}
-
-	switch status {
-	case porter_app.HighLevelStatus_Progressing:
-		return fmt.Errorf("timeout exceeded")
-	case porter_app.HighLevelStatus_Successful:
-		_, _ = color.New(color.FgGreen).Printf("Update completed successfully\n") // nolint:errcheck,gosec
-		return nil
-	case porter_app.HighLevelStatus_Failed:
-		return fmt.Errorf("update failed: check dashboard for details")
-	default:
-		return fmt.Errorf("received unknown status: %s", status)
-	}
+	return resp.RevisionID, nil
 }