From d0816dc332014dcadf3112abe24432bc774cbeca Mon Sep 17 00:00:00 2001 From: Feroze Mohideen Date: Thu, 7 Dec 2023 15:26:41 -0500 Subject: [PATCH] changes to support new app revision statuses on front end (#4050) --- .../porter_app/create_and_update_events.go | 32 ++++++++++++------- .../handlers/porter_app/report_status.go | 4 +-- .../porter_app/update_app_revision_status.go | 2 +- cli/cmd/v2/update.go | 9 ++++-- dashboard/src/lib/revisions/types.ts | 3 ++ .../revisions-list/GHStatusBanner.tsx | 8 ++++- internal/models/app_revision.go | 32 +++++++++++-------- internal/porter_app/revisions.go | 23 +++++++------ 8 files changed, 72 insertions(+), 41 deletions(-) diff --git a/api/server/handlers/porter_app/create_and_update_events.go b/api/server/handlers/porter_app/create_and_update_events.go index 3834dbadf8..5c6b5f2974 100644 --- a/api/server/handlers/porter_app/create_and_update_events.go +++ b/api/server/handlers/porter_app/create_and_update_events.go @@ -86,7 +86,7 @@ func (p *CreateUpdatePorterAppEventHandler) ServeHTTP(w http.ResponseWriter, r * return } } else { - event, err = p.createNewAppEvent(ctx, *cluster, appName, request.DeploymentTargetID, request.Status, string(request.Type), request.TypeExternalSource, request.Metadata) + event, err = p.createNewAppEvent(ctx, *project, *cluster, appName, request.DeploymentTargetID, request.Status, string(request.Type), request.TypeExternalSource, request.Metadata) if err != nil { e := telemetry.Error(ctx, span, err, "error creating new app event") p.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(e, http.StatusBadRequest)) @@ -138,7 +138,7 @@ func reportBuildStatus(ctx context.Context, request *types.CreateOrUpdatePorterA } // createNewAppEvent will create a new app event for the given porter app name. If the app event is an agent event, then it will be created only if there is no existing event which has the agent ID. In the case that an existing event is found, that will be returned instead -func (p *CreateUpdatePorterAppEventHandler) createNewAppEvent(ctx context.Context, cluster models.Cluster, porterAppName string, deploymentTargetID string, status types.PorterAppEventStatus, eventType string, externalSource string, requestMetadata map[string]any) (types.PorterAppEvent, error) { +func (p *CreateUpdatePorterAppEventHandler) createNewAppEvent(ctx context.Context, project models.Project, cluster models.Cluster, porterAppName string, deploymentTargetID string, status types.PorterAppEventStatus, eventType string, externalSource string, requestMetadata map[string]any) (types.PorterAppEvent, error) { ctx, span := telemetry.NewSpan(ctx, "create-porter-app-event") defer span.End() @@ -193,17 +193,25 @@ func (p *CreateUpdatePorterAppEventHandler) createNewAppEvent(ctx context.Contex } return event, nil } else { - err := p.updateDeployEventV2(ctx, updateDeployEventV2Input{ - projectID: cluster.ProjectID, - appName: porterAppName, - appID: app.ID, - deploymentTargetID: deploymentTargetID, - updatedStatusMetadata: requestMetadata, - }) - if err != nil { - return types.PorterAppEvent{}, telemetry.Error(ctx, span, err, "error updating v2 deploy event") + betaFeaturesEnabled := project.GetFeatureFlag(models.BetaFeaturesEnabled, p.Config().LaunchDarklyClient) + telemetry.WithAttributes(span, + telemetry.AttributeKV{Key: "beta_features_enabled", Value: betaFeaturesEnabled}, + ) + // if beta features are not enabled, then porter makes a request to ccp to update the deploy status + // if beta features are enabled, ccp is checking the deploy status, so this request is not necessary + // TODO remove this entire branch once beta features are enabled by default + if !betaFeaturesEnabled { + err := p.updateDeployEventV2(ctx, updateDeployEventV2Input{ + projectID: cluster.ProjectID, + appName: porterAppName, + appID: app.ID, + deploymentTargetID: deploymentTargetID, + updatedStatusMetadata: requestMetadata, + }) + if err != nil { + return types.PorterAppEvent{}, telemetry.Error(ctx, span, err, "error updating v2 deploy event") + } } - // v2 method calls ccp and will not return an event, so we just return an empty event return types.PorterAppEvent{}, nil } } diff --git a/api/server/handlers/porter_app/report_status.go b/api/server/handlers/porter_app/report_status.go index 5c9ced9ae0..4ec5259483 100644 --- a/api/server/handlers/porter_app/report_status.go +++ b/api/server/handlers/porter_app/report_status.go @@ -242,9 +242,9 @@ func writePRComment(ctx context.Context, inp writePRCommentInput) error { switch inp.revision.Status { case models.AppRevisionStatus_BuildFailed: body = fmt.Sprintf("%s❌ The latest deploy failed to build. Check the [Porter Dashboard](%s) or [action logs](https://github.com/%s/actions/runs/) for more information.", body, porterURL, inp.porterApp.RepoName) - case models.AppRevisionStatus_DeployFailed: + case models.AppRevisionStatus_InstallFailed: body = fmt.Sprintf("%s❌ The latest SHA ([`%s`](https://github.com/%s/%s/commit/%s)) failed to deploy.\nCheck the [Porter Dashboard](%s) or [action logs](https://github.com/%s/actions/runs/) for more information.\nContact Porter Support if the errors persists", body, inp.commitSha, repoDetails[0], repoDetails[1], inp.commitSha, porterURL, inp.porterApp.RepoName) - case models.AppRevisionStatus_Deployed: + case models.AppRevisionStatus_InstallSuccessful: body = fmt.Sprintf("%s✅ The latest SHA ([`%s`](https://github.com/%s/%s/commit/%s)) has been successfully deployed.\nApp details available in the [Porter Dashboard](%s)", body, inp.commitSha, repoDetails[0], repoDetails[1], inp.commitSha, porterURL) default: return nil diff --git a/api/server/handlers/porter_app/update_app_revision_status.go b/api/server/handlers/porter_app/update_app_revision_status.go index db32d53ce5..20b7bd1c37 100644 --- a/api/server/handlers/porter_app/update_app_revision_status.go +++ b/api/server/handlers/porter_app/update_app_revision_status.go @@ -78,7 +78,7 @@ func (c *UpdateAppRevisionStatusHandler) ServeHTTP(w http.ResponseWriter, r *htt switch request.Status { case models.AppRevisionStatus_BuildFailed: statusProto = porterv1.EnumRevisionStatus_ENUM_REVISION_STATUS_BUILD_FAILED - case models.AppRevisionStatus_DeployFailed: + case models.AppRevisionStatus_InstallFailed: statusProto = porterv1.EnumRevisionStatus_ENUM_REVISION_STATUS_DEPLOY_FAILED case models.AppRevisionStatus_PredeployFailed: statusProto = porterv1.EnumRevisionStatus_ENUM_REVISION_STATUS_PREDEPLOY_FAILED diff --git a/cli/cmd/v2/update.go b/cli/cmd/v2/update.go index 7d1a32f23c..6c4c8fa148 100644 --- a/cli/cmd/v2/update.go +++ b/cli/cmd/v2/update.go @@ -215,7 +215,12 @@ func Update(ctx context.Context, inp UpdateInput) error { } status = revision.AppRevision.Status - if status == models.AppRevisionStatus_DeployFailed || status == models.AppRevisionStatus_PredeployFailed || status == models.AppRevisionStatus_Deployed { + if status == models.AppRevisionStatus_PredeployFailed || + status == models.AppRevisionStatus_InstallFailed || + status == models.AppRevisionStatus_InstallSuccessful || + status == models.AppRevisionStatus_DeploymentSuccessful || + status == models.AppRevisionStatus_DeploymentProgressing || + status == models.AppRevisionStatus_DeploymentFailed { break } if status == models.AppRevisionStatus_AwaitingPredeploy { @@ -234,7 +239,7 @@ func Update(ctx context.Context, inp UpdateInput) error { CommitSHA: commitSHA, }) - if status == models.AppRevisionStatus_DeployFailed { + if status == models.AppRevisionStatus_InstallFailed { return errors.New("app failed to deploy") } if status == models.AppRevisionStatus_PredeployFailed { diff --git a/dashboard/src/lib/revisions/types.ts b/dashboard/src/lib/revisions/types.ts index c3a75b9c0d..204e006d0e 100644 --- a/dashboard/src/lib/revisions/types.ts +++ b/dashboard/src/lib/revisions/types.ts @@ -19,6 +19,9 @@ export const appRevisionValidator = z.object({ "DEPLOY_FAILED", "APPLY_FAILED", "UPDATE_FAILED", + "DEPLOYMENT_PROGRESSING", + "DEPLOYMENT_SUCCESSFUL", + "DEPLOYMENT_FAILED", ]), b64_app_proto: z.string(), revision_number: z.number(), diff --git a/dashboard/src/main/home/app-dashboard/validate-apply/revisions-list/GHStatusBanner.tsx b/dashboard/src/main/home/app-dashboard/validate-apply/revisions-list/GHStatusBanner.tsx index f0843fc69a..31ee8bafbf 100644 --- a/dashboard/src/main/home/app-dashboard/validate-apply/revisions-list/GHStatusBanner.tsx +++ b/dashboard/src/main/home/app-dashboard/validate-apply/revisions-list/GHStatusBanner.tsx @@ -62,7 +62,10 @@ const GHStatusBanner: React.FC = () => { const previouslyBuilt = useMemo(() => { if (revisions.length === 1) { if ( - revisions[0].status === "DEPLOYED" && + // TODO: remove checking for DEPLOYED status once update flow is released, + // because once that happens, the new terminal status will be DEPLOYMENT_SUCCESSFUL + (revisions[0].status === "DEPLOYMENT_SUCCESSFUL" || + revisions[0].status === "DEPLOYED") && latestProto.image?.tag === HELLO_PORTER_PLACEHOLDER_TAG ) { return false; @@ -76,6 +79,9 @@ const GHStatusBanner: React.FC = () => { "DEPLOY_FAILED", "BUILD_FAILED", "IMAGE_AVAILABLE", + "DEPLOYMENT_PROGRESSING", + "DEPLOYMENT_SUCCESSFUL", + "DEPLOYMENT_FAILED", () => true ) .otherwise(() => false) diff --git a/internal/models/app_revision.go b/internal/models/app_revision.go index a61a6105b9..96d5b06e14 100644 --- a/internal/models/app_revision.go +++ b/internal/models/app_revision.go @@ -17,30 +17,34 @@ const ( AppRevisionStatus_ImageAvailable AppRevisionStatus = "IMAGE_AVAILABLE" // AppRevisionStatus_AwaitingBuild is the status for a revision that still needs to be built AppRevisionStatus_AwaitingBuild AppRevisionStatus = "AWAITING_BUILD_ARTIFACT" - // AppRevisionStatus_AwaitingPredeploy is the status for a revision that is waiting for a predeploy to be run - AppRevisionStatus_AwaitingPredeploy AppRevisionStatus = "AWAITING_PREDEPLOY" - // AppRevisionStatus_AwaitingDeploy is the status for a revision that is waiting to be deployed - AppRevisionStatus_AwaitingDeploy AppRevisionStatus = "AWAITING_DEPLOY" - // AppRevisionStatus_PredeployProgressing is the status for a revision that is currently running a predeploy - AppRevisionStatus_PredeployProgressing AppRevisionStatus = "PREDEPLOY_PROGRESSING" - // AppRevisionStatus_Deployed is the status for a revision that has been deployed - AppRevisionStatus_Deployed AppRevisionStatus = "DEPLOYED" - // AppRevisionStatus_Deploying is the status for a revision that is currently deploying - AppRevisionStatus_Deploying AppRevisionStatus = "DEPLOYING" - // AppRevisionStatus_BuildCanceled is the status for a revision that was canceled during the build process AppRevisionStatus_BuildCanceled AppRevisionStatus = "BUILD_CANCELED" // AppRevisionStatus_BuildFailed is the status for a revision that failed to build AppRevisionStatus_BuildFailed AppRevisionStatus = "BUILD_FAILED" // AppRevisionStatus_BuildSuccessful is the status for a revision that successfully built AppRevisionStatus_BuildSuccessful AppRevisionStatus = "BUILD_SUCCESSFUL" + // AppRevisionStatus_AwaitingPredeploy is the status for a revision that is waiting for a predeploy to be run + AppRevisionStatus_AwaitingPredeploy AppRevisionStatus = "AWAITING_PREDEPLOY" + // AppRevisionStatus_PredeployProgressing is the status for a revision that is currently running a predeploy + AppRevisionStatus_PredeployProgressing AppRevisionStatus = "PREDEPLOY_PROGRESSING" // AppRevisionStatus_PredeployFailed is the status for a revision that failed to predeploy AppRevisionStatus_PredeployFailed AppRevisionStatus = "PREDEPLOY_FAILED" // AppRevisionStatus_PredeploySuccessful is the status for a revision that successfully ran a predeploy AppRevisionStatus_PredeploySuccessful AppRevisionStatus = "PREDEPLOY_SUCCESSFUL" - // AppRevisionStatus_DeployFailed is the status for a revision that failed to deploy - AppRevisionStatus_DeployFailed AppRevisionStatus = "DEPLOY_FAILED" - + // AppRevisionStatus_AwaitingInstall is the status for a revision that is waiting to be installed + AppRevisionStatus_AwaitingInstall AppRevisionStatus = "AWAITING_DEPLOY" + // AppRevisionStatus_InstallProgressing is the status for a revision that is currently installing + AppRevisionStatus_InstallProgressing AppRevisionStatus = "DEPLOYING" + // AppRevisionStatus_InstallSuccessful is the status for a revision that has been installed + AppRevisionStatus_InstallSuccessful AppRevisionStatus = "DEPLOYED" + // AppRevisionStatus_InstallFailed is the status for a revision that failed to install + AppRevisionStatus_InstallFailed AppRevisionStatus = "DEPLOY_FAILED" + // AppRevisionStatus_DeploymentProgressing is the status for a revision that is currently deploying + AppRevisionStatus_DeploymentProgressing AppRevisionStatus = "DEPLOYMENT_PROGRESSING" + // AppRevisionStatus_DeploymentSuccessful is the status for a revision that successfully deployed + AppRevisionStatus_DeploymentSuccessful AppRevisionStatus = "DEPLOYMENT_SUCCESSFUL" + // AppRevisionStatus_DeploymentFailed is the status for a revision that failed to deploy + AppRevisionStatus_DeploymentFailed AppRevisionStatus = "DEPLOYMENT_FAILED" // AppRevisionStatus_ApplyFailed is the status for a revision that failed due to an internal system error AppRevisionStatus_ApplyFailed AppRevisionStatus = "APPLY_FAILED" // AppRevisionStatus_UpdateFailed is the status for a revision that failed due to an internal system error diff --git a/internal/porter_app/revisions.go b/internal/porter_app/revisions.go index e2b140f7cc..698924c2f6 100644 --- a/internal/porter_app/revisions.go +++ b/internal/porter_app/revisions.go @@ -217,12 +217,12 @@ func appRevisionStatusFromProto(status string) (models.AppRevisionStatus, error) appRevisionStatus = models.AppRevisionStatus_AwaitingBuild case string(models.AppRevisionStatus_AwaitingPredeploy): appRevisionStatus = models.AppRevisionStatus_AwaitingPredeploy - case string(models.AppRevisionStatus_Deployed): - appRevisionStatus = models.AppRevisionStatus_Deployed - case string(models.AppRevisionStatus_Deploying): - appRevisionStatus = models.AppRevisionStatus_Deploying - case string(models.AppRevisionStatus_AwaitingDeploy): - appRevisionStatus = models.AppRevisionStatus_AwaitingDeploy + case string(models.AppRevisionStatus_InstallSuccessful): + appRevisionStatus = models.AppRevisionStatus_InstallSuccessful + case string(models.AppRevisionStatus_InstallProgressing): + appRevisionStatus = models.AppRevisionStatus_InstallProgressing + case string(models.AppRevisionStatus_AwaitingInstall): + appRevisionStatus = models.AppRevisionStatus_AwaitingInstall case string(models.AppRevisionStatus_BuildCanceled): appRevisionStatus = models.AppRevisionStatus_BuildCanceled case string(models.AppRevisionStatus_BuildFailed): @@ -233,8 +233,8 @@ func appRevisionStatusFromProto(status string) (models.AppRevisionStatus, error) appRevisionStatus = models.AppRevisionStatus_PredeploySuccessful case string(models.AppRevisionStatus_PredeployProgressing): appRevisionStatus = models.AppRevisionStatus_PredeployProgressing - case string(models.AppRevisionStatus_DeployFailed): - appRevisionStatus = models.AppRevisionStatus_DeployFailed + case string(models.AppRevisionStatus_InstallFailed): + appRevisionStatus = models.AppRevisionStatus_InstallFailed case string(models.AppRevisionStatus_Created): appRevisionStatus = models.AppRevisionStatus_Created case string(models.AppRevisionStatus_BuildSuccessful): @@ -243,7 +243,12 @@ func appRevisionStatusFromProto(status string) (models.AppRevisionStatus, error) appRevisionStatus = models.AppRevisionStatus_ApplyFailed case string(models.AppRevisionStatus_UpdateFailed): appRevisionStatus = models.AppRevisionStatus_UpdateFailed - + case string(models.AppRevisionStatus_DeploymentProgressing): + appRevisionStatus = models.AppRevisionStatus_DeploymentProgressing + case string(models.AppRevisionStatus_DeploymentSuccessful): + appRevisionStatus = models.AppRevisionStatus_DeploymentSuccessful + case string(models.AppRevisionStatus_DeploymentFailed): + appRevisionStatus = models.AppRevisionStatus_DeploymentFailed default: return appRevisionStatus, errors.New("unknown app revision status") }