From 667f7211e7528a736956adcc67d327ec98210369 Mon Sep 17 00:00:00 2001 From: Ahmed Nour <125875466+ahmed-nour-fdc@users.noreply.github.com> Date: Fri, 23 Aug 2024 16:28:18 +0200 Subject: [PATCH] feat: Get commit deployment status for all applications (#1899) The `GetCommitDeploymentInfo` endpoint now accepts only a commit hash, and returns a map of all applications and their deployment status for all environments, as shown: ``` { "app1": { "dev": DEPLOYED, "staging": DEPLOYED, }, "app2": { "dev": DEPLOYED, "staging": PENDING, }, } ``` Ref: SRX-H0ZDQS --- pkg/api/v1/api.proto | 7 +- .../pkg/service/commit_deployment.go | 107 ++++++++++++------ 2 files changed, 79 insertions(+), 35 deletions(-) diff --git a/pkg/api/v1/api.proto b/pkg/api/v1/api.proto index 3d4c7673b..242a5db11 100644 --- a/pkg/api/v1/api.proto +++ b/pkg/api/v1/api.proto @@ -720,9 +720,12 @@ service CommitDeploymentService { message GetCommitDeploymentInfoRequest { string commit_id = 1; - string application = 2; } -message GetCommitDeploymentInfoResponse { +message AppCommitDeploymentStatus { map deployment_status = 1; } + +message GetCommitDeploymentInfoResponse { + map deployment_status = 1; +} diff --git a/services/cd-service/pkg/service/commit_deployment.go b/services/cd-service/pkg/service/commit_deployment.go index d6c3a20bb..683e5d809 100644 --- a/services/cd-service/pkg/service/commit_deployment.go +++ b/services/cd-service/pkg/service/commit_deployment.go @@ -26,6 +26,7 @@ import ( api "github.com/freiheit-com/kuberpult/pkg/api/v1" "github.com/freiheit-com/kuberpult/pkg/db" "github.com/freiheit-com/kuberpult/pkg/event" + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" ) type CommitStatus map[string]api.CommitDeploymentStatus @@ -35,45 +36,46 @@ type CommitDeploymentServer struct { } func (s *CommitDeploymentServer) GetCommitDeploymentInfo(ctx context.Context, in *api.GetCommitDeploymentInfoRequest) (*api.GetCommitDeploymentInfoResponse, error) { - status, err := getCommitDeploymentInfoForApp(ctx, s.DBHandler, in.CommitId, in.Application) - if err != nil { - return nil, fmt.Errorf("Could not get commit deployment info: %v", err) - } - return &api.GetCommitDeploymentInfoResponse{ - DeploymentStatus: status, - }, nil -} - -func getCommitDeploymentInfoForApp(ctx context.Context, h *db.DBHandler, commit, app string) (CommitStatus, error) { + commitDeploymentStatus := make(map[string]*api.AppCommitDeploymentStatus, 0) var jsonCommitEventsMetadata []byte - var jsonAllDeploymentsMetadata []byte var jsonAllEnvironmentsMetadata []byte - - err := h.WithTransaction(ctx, true, func(ctx context.Context, transaction *sql.Tx) error { + applicationReleases := make(map[string][]byte, 0) + allApplicationReleasesQuery := ` +SELECT + deployments.appname, + deployments.json +FROM ( + SELECT + MAX(eslVersion) AS latest, + appname + FROM + "public"."all_deployments" + GROUP BY + appname) AS latest +JOIN + "public".all_deployments AS deployments +ON + latest.latest=deployments.eslVersion + AND latest.appname=deployments.appname; +` + span, _ := tracer.StartSpanFromContext(ctx, "GetCommitDeploymentInfo") + defer span.Finish() + span.SetTag("commit_id", in.CommitId) + + err := s.DBHandler.WithTransaction(ctx, true, func(ctx context.Context, transaction *sql.Tx) error { // Get the latest new-release event for the commit - query := h.AdaptQuery("SELECT json FROM commit_events WHERE commithash = ? AND eventtype = ? ORDER BY timestamp DESC LIMIT 1;") - row := transaction.QueryRow(query, commit, "new-release") + query := s.DBHandler.AdaptQuery("SELECT json FROM commit_events WHERE commithash = ? AND eventtype = ? ORDER BY timestamp DESC LIMIT 1;") + row := transaction.QueryRow(query, in.CommitId, "new-release") err := row.Scan(&jsonCommitEventsMetadata) if err != nil { if errors.Is(err, sql.ErrNoRows) { - return fmt.Errorf("commit \"%s\" could not be found", commit) - } - return err - } - - // Get all deployments for the commit - query = h.AdaptQuery("SELECT json FROM all_deployments WHERE appname = ? ORDER BY eslversion DESC LIMIT 1;") - row = transaction.QueryRow(query, app) - err = row.Scan(&jsonAllDeploymentsMetadata) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return fmt.Errorf("application \"%s\" does not exist or has no deployments yet.", app) + return fmt.Errorf("commit \"%s\" could not be found", in.CommitId) } return err } // Get all environments - query = h.AdaptQuery("SELECT json FROM all_environments ORDER BY version DESC LIMIT 1;") + query = s.DBHandler.AdaptQuery("SELECT json FROM all_environments ORDER BY version DESC LIMIT 1;") row = transaction.QueryRow(query) err = row.Scan(&jsonAllEnvironmentsMetadata) if err != nil { @@ -82,11 +84,29 @@ func getCommitDeploymentInfoForApp(ctx context.Context, h *db.DBHandler, commit, } return err } + + // Get latest releases for all apps + rows, err := transaction.QueryContext(ctx, allApplicationReleasesQuery) + if err != nil { + return err + } + defer rows.Close() + + for rows.Next() { + var appName string + var appRelease []byte + err = rows.Scan(&appName, &appRelease) + if err != nil { + return err + } + applicationReleases[appName] = appRelease + } return nil }) if err != nil { return nil, err } + releaseNumber, err := getCommitReleaseNumber(jsonCommitEventsMetadata) if err != nil { return nil, fmt.Errorf("Could not get commit release number from commit_events metadata: %v", err) @@ -95,13 +115,34 @@ func getCommitDeploymentInfoForApp(ctx context.Context, h *db.DBHandler, commit, if err != nil { return nil, fmt.Errorf("Could not get all environments from all_environments metadata: %v", err) } - environmentReleases, err := getEnvironmentReleases(jsonAllDeploymentsMetadata) + + for app, releases := range applicationReleases { + commitDeploymentStatusForApp, err := getCommitDeploymentInfoForApp(ctx, s.DBHandler, releaseNumber, app, allEnvironments, releases) + if err != nil { + return nil, fmt.Errorf("Could not get commit deployment info for app %s: %v", app, err) + } + commitDeploymentStatus[app] = commitDeploymentStatusForApp + } + + return &api.GetCommitDeploymentInfoResponse{ + DeploymentStatus: commitDeploymentStatus, + }, nil +} + +func getCommitDeploymentInfoForApp(ctx context.Context, h *db.DBHandler, commitReleaseNumber uint64, app string, environments []string, appDeployments []byte) (*api.AppCommitDeploymentStatus, error) { + + span, _ := tracer.StartSpanFromContext(ctx, "getCommitDeploymentInfoForApp") + defer span.Finish() + span.SetTag("app", app) + + environmentReleases, err := getEnvironmentReleases(appDeployments) if err != nil { return nil, fmt.Errorf("Could not get environment releases from all_deployments metadata: %v", err) } - - commitStatus := getCommitStatus(releaseNumber, environmentReleases, allEnvironments) - return commitStatus, nil + commitStatus := getCommitStatus(commitReleaseNumber, environmentReleases, environments) + return &api.AppCommitDeploymentStatus{ + DeploymentStatus: commitStatus, + }, nil } func getCommitStatus(commitReleaseNumber uint64, environmentReleases map[string]uint64, allEnvironments []string) CommitStatus { @@ -116,7 +157,7 @@ func getCommitStatus(commitReleaseNumber uint64, environmentReleases map[string] } for _, env := range allEnvironments { - // by defauly, a commit is pending in all environments + // by default, a commit is pending in all environments commitStatus[env] = api.CommitDeploymentStatus_PENDING }