Browse Source

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

Feroze Mohideen 2 years ago
parent
commit
2bbc1173c5
6 changed files with 144 additions and 86 deletions
  1. 19 19
      cli/cmd/commands/app.go
  2. 14 6
      cli/cmd/commands/apply.go
  3. 70 0
      cli/cmd/v2/app_status.go
  4. 12 0
      cli/cmd/v2/apply.go
  5. 13 0
      cli/cmd/v2/update.go
  6. 16 61
      cli/cmd/v2/update_image.go

+ 19 - 19
cli/cmd/commands/app.go

@@ -35,17 +35,17 @@ import (
 )
 )
 
 
 var (
 var (
-	appNamespace            string
-	appVerbose              bool
-	appExistingPod          bool
-	appInteractive          bool
-	appContainerName        string
-	appTag                  string
-	deploymentTarget        string
-	appCpuMilli             int
-	appMemoryMi             int
-	jobName                 string
-	waitForSuccessfulUpdate bool
+	appNamespace                string
+	appVerbose                  bool
+	appExistingPod              bool
+	appInteractive              bool
+	appContainerName            string
+	appTag                      string
+	deploymentTarget            string
+	appCpuMilli                 int
+	appMemoryMi                 int
+	jobName                     string
+	waitForSuccessfulDeployment bool
 )
 )
 
 
 const (
 const (
@@ -112,7 +112,7 @@ func registerCommand_App(cliConf config.CLIConfig) *cobra.Command {
 	}
 	}
 
 
 	appUpdateTagCmd.PersistentFlags().BoolVarP(
 	appUpdateTagCmd.PersistentFlags().BoolVarP(
-		&waitForSuccessfulUpdate,
+		&waitForSuccessfulDeployment,
 		"wait",
 		"wait",
 		"w",
 		"w",
 		false,
 		false,
@@ -1259,13 +1259,13 @@ func appUpdateTag(ctx context.Context, user *types.GetAuthenticatedUserResponse,
 
 
 	if project.ValidateApplyV2 {
 	if project.ValidateApplyV2 {
 		err := v2.UpdateImage(ctx, v2.UpdateImageInput{
 		err := v2.UpdateImage(ctx, v2.UpdateImageInput{
-			ProjectID:               cliConf.Project,
-			ClusterID:               cliConf.Cluster,
-			AppName:                 args[0],
-			DeploymentTargetName:    deploymentTarget,
-			Tag:                     appTag,
-			WaitForSuccessfulUpdate: waitForSuccessfulUpdate,
-			Client:                  client,
+			ProjectID:                   cliConf.Project,
+			ClusterID:                   cliConf.Cluster,
+			AppName:                     args[0],
+			DeploymentTargetName:        deploymentTarget,
+			Tag:                         appTag,
+			Client:                      client,
+			WaitForSuccessfulDeployment: waitForSuccessfulDeployment,
 		})
 		})
 		if err != nil {
 		if err != nil {
 			return fmt.Errorf("error updating tag: %w", err)
 			return fmt.Errorf("error updating tag: %w", err)

+ 14 - 6
cli/cmd/commands/apply.go

@@ -107,6 +107,13 @@ applying a configuration:
 
 
 	applyCmd.PersistentFlags().StringVarP(&porterYAML, "file", "f", "", "path to porter.yaml")
 	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(&previewApply, "preview", "p", false, "apply as preview environment based on current git branch")
+	applyCmd.PersistentFlags().BoolVarP(
+		&waitForSuccessfulDeployment,
+		"wait",
+		"w",
+		false,
+		"set this to wait and be notified when an apply is successful, otherwise time out",
+	)
 	applyCmd.MarkFlagRequired("file")
 	applyCmd.MarkFlagRequired("file")
 
 
 	return applyCmd
 	return applyCmd
@@ -136,13 +143,14 @@ func apply(ctx context.Context, _ *types.GetAuthenticatedUserResponse, client ap
 		}
 		}
 
 
 		inp := v2.ApplyInput{
 		inp := v2.ApplyInput{
-			CLIConfig:      cliConfig,
-			Client:         client,
-			PorterYamlPath: porterYAML,
-			AppName:        appName,
-			PreviewApply:   previewApply,
+			CLIConfig:                   cliConfig,
+			Client:                      client,
+			PorterYamlPath:              porterYAML,
+			AppName:                     appName,
+			PreviewApply:                previewApply,
+			WaitForSuccessfulDeployment: waitForSuccessfulDeployment,
 		}
 		}
-		err = v2.Apply(ctx, inp)
+		err := v2.Apply(ctx, inp)
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}

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

@@ -0,0 +1,70 @@
+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
+)
+
+// waitForAppRevisionStatusInput is the input for the WaitForAppRevisionStatus function
+type waitForAppRevisionStatusInput struct {
+	ProjectID  uint
+	ClusterID  uint
+	AppName    string
+	RevisionID string
+	Client     api.Client
+}
+
+// waitForAppRevisionStatus waits for an app revision to complete
+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 up to %d minutes for all services to deploy\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("All services deployed 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)
+	}
+}

+ 12 - 0
cli/cmd/v2/apply.go

@@ -35,6 +35,8 @@ type ApplyInput struct {
 	AppName string
 	AppName string
 	// PreviewApply is true when Apply should create a new deployment target matching current git branch and apply to that target
 	// PreviewApply is true when Apply should create a new deployment target matching current git branch and apply to that target
 	PreviewApply bool
 	PreviewApply bool
+	// WaitForSuccessfulDeployment is true when Apply should wait for the update to complete before returning
+	WaitForSuccessfulDeployment bool
 }
 }
 
 
 // Apply implements the functionality of the `porter apply` command for validate apply v2 projects
 // Apply implements the functionality of the `porter apply` command for validate apply v2 projects
@@ -362,6 +364,16 @@ 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
 	color.New(color.FgGreen).Printf("Successfully applied new revision %s for app %s\n", applyResp.AppRevisionId, appName) // nolint:errcheck,gosec
+
+	if inp.WaitForSuccessfulDeployment {
+		return waitForAppRevisionStatus(ctx, waitForAppRevisionStatusInput{
+			ProjectID:  cliConf.Project,
+			ClusterID:  cliConf.Cluster,
+			AppName:    appName,
+			RevisionID: applyResp.AppRevisionId,
+			Client:     client,
+		})
+	}
 	return nil
 	return nil
 }
 }
 
 

+ 13 - 0
cli/cmd/v2/update.go

@@ -33,6 +33,8 @@ type UpdateInput struct {
 	AppName string
 	AppName string
 	// PreviewApply is true when Update should create a new deployment target matching current git branch and apply to that target
 	// PreviewApply is true when Update should create a new deployment target matching current git branch and apply to that target
 	PreviewApply bool
 	PreviewApply bool
+	// WaitForSuccessfulDeployment is true when Update should wait for the revision deployment to complete (all services deployed successfully)
+	WaitForSuccessfulDeployment bool
 }
 }
 
 
 // Update implements the functionality of the `porter apply` command for validate apply v2 projects
 // Update implements the functionality of the `porter apply` command for validate apply v2 projects
@@ -257,6 +259,17 @@ func Update(ctx context.Context, inp UpdateInput) error {
 	}
 	}
 
 
 	color.New(color.FgGreen).Printf("Successfully applied new revision %s\n", updateResp.AppRevisionId) // nolint:errcheck,gosec
 	color.New(color.FgGreen).Printf("Successfully applied new revision %s\n", updateResp.AppRevisionId) // nolint:errcheck,gosec
+
+	if inp.WaitForSuccessfulDeployment {
+		return waitForAppRevisionStatus(ctx, waitForAppRevisionStatusInput{
+			ProjectID:  cliConf.Project,
+			ClusterID:  cliConf.Cluster,
+			AppName:    appName,
+			RevisionID: updateResp.AppRevisionId,
+			Client:     client,
+		})
+	}
+
 	return nil
 	return nil
 }
 }
 
 

+ 16 - 61
cli/cmd/v2/update_image.go

@@ -4,9 +4,6 @@ import (
 	"context"
 	"context"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
-	"time"
-
-	"github.com/porter-dev/porter/api/server/handlers/porter_app"
 
 
 	"github.com/fatih/color"
 	"github.com/fatih/color"
 
 
@@ -15,22 +12,15 @@ import (
 
 
 // UpdateImageInput is the input for the UpdateImage function
 // UpdateImageInput is the input for the UpdateImage function
 type UpdateImageInput struct {
 type UpdateImageInput struct {
-	ProjectID               uint
-	ClusterID               uint
-	AppName                 string
-	DeploymentTargetName    string
-	Tag                     string
-	WaitForSuccessfulUpdate bool
-	Client                  api.Client
+	ProjectID                   uint
+	ClusterID                   uint
+	AppName                     string
+	DeploymentTargetName        string
+	Tag                         string
+	Client                      api.Client
+	WaitForSuccessfulDeployment bool
 }
 }
 
 
-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
 // UpdateImage updates the image of an application
 func UpdateImage(ctx context.Context, input UpdateImageInput) error {
 func UpdateImage(ctx context.Context, input UpdateImageInput) error {
 	if input.DeploymentTargetName == "" {
 	if input.DeploymentTargetName == "" {
@@ -48,52 +38,17 @@ func UpdateImage(ctx context.Context, input UpdateImageInput) error {
 	}
 	}
 
 
 	triggeredBackgroundColor := color.FgGreen
 	triggeredBackgroundColor := color.FgGreen
-	if input.WaitForSuccessfulUpdate {
-		triggeredBackgroundColor = color.FgBlue
-	}
 
 
 	_, _ = color.New(triggeredBackgroundColor).Printf("Updated application %s to use tag \"%s\"\n", input.AppName, tag)
 	_, _ = 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)
+	if input.WaitForSuccessfulDeployment {
+		return waitForAppRevisionStatus(ctx, waitForAppRevisionStatusInput{
+			ProjectID:  input.ProjectID,
+			ClusterID:  input.ClusterID,
+			AppName:    input.AppName,
+			RevisionID: resp.RevisionID,
+			Client:     input.Client,
+		})
 	}
 	}
+	return nil
 }
 }