diff --git a/pkg/api/v1/api.proto b/pkg/api/v1/api.proto index f5d8c6506..cba85c881 100644 --- a/pkg/api/v1/api.proto +++ b/pkg/api/v1/api.proto @@ -441,7 +441,6 @@ message OverviewApplication { } message GetOverviewResponse { - map applications = 2; repeated EnvironmentGroup environment_groups = 3; string git_revision = 4; string branch = 5; @@ -563,45 +562,13 @@ message UpstreamNotDeployed { } message Environment { - - message Application { - message ArgoCD { - message SyncWindow { - string kind = 1; // "allow" or "deny" - string schedule = 2; // crontab format - string duration = 3; // duration the window is open (or closed) - } - repeated SyncWindow sync_windows = 1; - } - message DeploymentMetaData { - string deploy_author = 1; - // we use a string here, because the UI cannot handle int64 as a type. - // the string contains the unix timestamps in seconds (utc) - string deploy_time = 2; - } - - string name = 1; - // version=0 means "nothing is deployed" - uint64 version = 2; - map locks = 3; - // "version" describes the currently deployed version. "queuedVersion" describes a version that was to be deployed, but a lock stopped the deployment: - // "queuedVersion" has nothing to do with queue.go - // queued_version=0 means "nothing is queued" - uint64 queued_version = 4; - // google.protobuf.Timestamp deploy_date = 5; // This is never used - bool undeploy_version = 6; - ArgoCD argo_cd = 7; - DeploymentMetaData deployment_meta_data = 8; - map team_locks = 9; //Keep in mind that team lock information will be duplicated if there is a team with more than one application (which is usually the case) - string team = 10; - } - string name = 1; EnvironmentConfig config = 2; - map locks = 3; - map applications = 4; + map locks = 3; //Environment Locks. Lock ID -> Lock uint32 distance_to_upstream = 5; Priority priority = 6; + map app_locks = 7; //Application Locks. AppName -> []Locks + map team_locks = 8; //Team Locks. TeamName -> []Locks } message Release { diff --git a/pkg/db/db.go b/pkg/db/db.go index 752505632..1d810800a 100644 --- a/pkg/db/db.go +++ b/pkg/db/db.go @@ -1120,10 +1120,7 @@ func (h *DBHandler) DBInsertRelease(ctx context.Context, transaction *sql.Tx, re previousEslVersion+1, err) } - err = h.UpdateOverviewRelease(ctx, transaction, release) - if err != nil { - return err - } + logger.FromContext(ctx).Sugar().Infof( "inserted release: app '%s' and version '%v' and eslVersion %v", release.App, @@ -1979,7 +1976,10 @@ func processAllLatestDeploymentsForApp(rows *sql.Rows) (map[string]Deployment, e } return nil, fmt.Errorf("Error scanning deployments row from DB. Error: %w\n", err) } - + err = json.Unmarshal(([]byte)(jsonMetadata), &curr.Metadata) + if err != nil { + return nil, fmt.Errorf("Error during json unmarshal in deployments. Error: %w. Data: %s\n", err, jsonMetadata) + } if releaseVersion.Valid { curr.Version = &releaseVersion.Int64 } @@ -5661,7 +5661,6 @@ func (h *DBHandler) ReadLatestOverviewCache(ctx context.Context, transaction *sq result := &api.GetOverviewResponse{ Branch: "", ManifestRepoUrl: "", - Applications: map[string]*api.Application{}, LightweightApps: []*api.OverviewApplication{}, EnvironmentGroups: []*api.EnvironmentGroup{}, GitRevision: "", diff --git a/pkg/db/db_test.go b/pkg/db/db_test.go index 35563e9e6..63dc1813f 100644 --- a/pkg/db/db_test.go +++ b/pkg/db/db_test.go @@ -37,7 +37,6 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "go.uber.org/zap" - "google.golang.org/protobuf/types/known/timestamppb" ) type errMatcher struct { @@ -2578,7 +2577,7 @@ func TestReadWriteOverviewCache(t *testing.T) { Name string Overviews []*api.GetOverviewResponse } - + //TODO: This test suite has some commented out sections. These should be resolved in Ref: SRX-9PBRYS. tcs := []TestCase{ { Name: "Read and write", @@ -2597,36 +2596,42 @@ func TestReadWriteOverviewCache(t *testing.T) { Argocd: &api.EnvironmentConfig_ArgoCD{}, EnvironmentGroup: &dev, }, - Applications: map[string]*api.Environment_Application{ - "test": { - Name: "test", - Version: 1, - DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ - DeployAuthor: "testmail@example.com", - DeployTime: "1", - }, - Team: "team-123", - }, - }, + //Applications: map[string]*api.Environment_Application{ + // "test": { + // Name: "test", + // Version: 1, + // DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ + // DeployAuthor: "testmail@example.com", + // DeployTime: "1", + // }, + // Team: "team-123", + // }, + //}, Priority: api.Priority_YOLO, }, }, Priority: api.Priority_YOLO, }, }, - Applications: map[string]*api.Application{ - "test": { + //Applications: map[string]*api.Application{ + // "test": { + // Name: "test", + // Releases: []*api.Release{ + // { + // Version: 1, + // SourceCommitId: "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef", + // SourceAuthor: "example ", + // SourceMessage: "changed something (#678)", + // PrNumber: "678", + // CreatedAt: ×tamppb.Timestamp{Seconds: 1, Nanos: 1}, + // }, + // }, + // Team: "team-123", + // }, + //}, + LightweightApps: []*api.OverviewApplication{ + { Name: "test", - Releases: []*api.Release{ - { - Version: 1, - SourceCommitId: "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef", - SourceAuthor: "example ", - SourceMessage: "changed something (#678)", - PrNumber: "678", - CreatedAt: ×tamppb.Timestamp{Seconds: 1, Nanos: 1}, - }, - }, Team: "team-123", }, }, @@ -2651,36 +2656,42 @@ func TestReadWriteOverviewCache(t *testing.T) { Argocd: &api.EnvironmentConfig_ArgoCD{}, EnvironmentGroup: &dev, }, - Applications: map[string]*api.Environment_Application{ - "test": { - Name: "test", - Version: 1, - DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ - DeployAuthor: "testmail@example.com", - DeployTime: "1", - }, - Team: "team-123", - }, - }, + //Applications: map[string]*api.Environment_Application{ + // "test": { + // Name: "test", + // Version: 1, + // DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ + // DeployAuthor: "testmail@example.com", + // DeployTime: "1", + // }, + // Team: "team-123", + // }, + //}, Priority: api.Priority_YOLO, }, }, Priority: api.Priority_YOLO, }, }, - Applications: map[string]*api.Application{ - "test": { + //Applications: map[string]*api.Application{ + // "test": { + // Name: "test", + // Releases: []*api.Release{ + // { + // Version: 1, + // SourceCommitId: "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef", + // SourceAuthor: "example ", + // SourceMessage: "changed something (#678)", + // PrNumber: "678", + // CreatedAt: ×tamppb.Timestamp{Seconds: 1, Nanos: 1}, + // }, + // }, + // Team: "team-123", + // }, + //}, + LightweightApps: []*api.OverviewApplication{ + { Name: "test", - Releases: []*api.Release{ - { - Version: 1, - SourceCommitId: "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef", - SourceAuthor: "example ", - SourceMessage: "changed something (#678)", - PrNumber: "678", - CreatedAt: ×tamppb.Timestamp{Seconds: 1, Nanos: 1}, - }, - }, Team: "team-123", }, }, @@ -2700,36 +2711,42 @@ func TestReadWriteOverviewCache(t *testing.T) { Argocd: &api.EnvironmentConfig_ArgoCD{}, EnvironmentGroup: &dev, }, - Applications: map[string]*api.Environment_Application{ - "test2": { - Name: "test2", - Version: 1, - DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ - DeployAuthor: "testmail2@example.com", - DeployTime: "1", - }, - Team: "team-123", - }, - }, + //Applications: map[string]*api.Environment_Application{ + // "test2": { + // Name: "test2", + // Version: 1, + // DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ + // DeployAuthor: "testmail2@example.com", + // DeployTime: "1", + // }, + // Team: "team-123", + // }, + //}, Priority: api.Priority_CANARY, }, }, Priority: api.Priority_CANARY, }, }, - Applications: map[string]*api.Application{ - "test2": { + //Applications: map[string]*api.Application{ + // "test2": { + // Name: "test2", + // Releases: []*api.Release{ + // { + // Version: 1, + // SourceCommitId: "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef", + // SourceAuthor: "example ", + // SourceMessage: "changed something (#678)", + // PrNumber: "678", + // CreatedAt: ×tamppb.Timestamp{Seconds: 1, Nanos: 1}, + // }, + // }, + // Team: "team-123", + // }, + //}, + LightweightApps: []*api.OverviewApplication{ + { Name: "test2", - Releases: []*api.Release{ - { - Version: 1, - SourceCommitId: "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef", - SourceAuthor: "example ", - SourceMessage: "changed something (#678)", - PrNumber: "678", - CreatedAt: ×tamppb.Timestamp{Seconds: 1, Nanos: 1}, - }, - }, Team: "team-123", }, }, diff --git a/pkg/db/overview.go b/pkg/db/overview.go index 806d60513..9ec560bde 100644 --- a/pkg/db/overview.go +++ b/pkg/db/overview.go @@ -20,7 +20,6 @@ import ( "context" "database/sql" "fmt" - "regexp" "time" "github.com/freiheit-com/kuberpult/pkg/api/v1" @@ -40,12 +39,29 @@ func (h *DBHandler) UpdateOverviewTeamLock(ctx context.Context, transaction *sql if env == nil { return fmt.Errorf("could not find environment %s in overview", teamLock.Env) } - apps := getEnvironmentApplicationsByTeam(env.Applications, teamLock.Team) - for _, app := range apps { - if app.TeamLocks == nil { - app.TeamLocks = map[string]*api.Lock{} + if env.TeamLocks == nil { + env.TeamLocks = make(map[string]*api.Locks) + } + + if teamLock.Deleted { + locksToKeep := make([]*api.Lock, max(len(env.TeamLocks[teamLock.Team].Locks)-1, 0)) + for _, lock := range env.TeamLocks[teamLock.Team].Locks { + if lock.LockId != teamLock.LockID { + locksToKeep = append(locksToKeep, lock) + } + } + if len(locksToKeep) == 0 { + delete(env.TeamLocks, teamLock.Team) + } else { + env.TeamLocks[teamLock.Team].Locks = locksToKeep + } + } else { + if env.TeamLocks[teamLock.Team] == nil { + env.TeamLocks[teamLock.Team] = &api.Locks{ + Locks: make([]*api.Lock, 0), + } } - app.TeamLocks[teamLock.LockID] = &api.Lock{ + env.TeamLocks[teamLock.Team].Locks = append(env.TeamLocks[teamLock.Team].Locks, &api.Lock{ Message: teamLock.Metadata.Message, LockId: teamLock.LockID, CreatedAt: timestamppb.New(teamLock.Created), @@ -53,11 +69,9 @@ func (h *DBHandler) UpdateOverviewTeamLock(ctx context.Context, transaction *sql Name: teamLock.Metadata.CreatedByName, Email: teamLock.Metadata.CreatedByEmail, }, - } - if teamLock.Deleted { - delete(app.TeamLocks, teamLock.LockID) - } + }) } + err = h.WriteOverviewCache(ctx, transaction, latestOverview) if err != nil { return err @@ -111,55 +125,15 @@ func (h *DBHandler) UpdateOverviewDeployment(ctx context.Context, transaction *s if env == nil { return fmt.Errorf("could not find environment %s in overview", deployment.Env) } - appInEnv := getEnvironmentApplicationByName(env.Applications, deployment.App) - if appInEnv == nil { - selectApp, err := h.DBSelectApp(ctx, transaction, deployment.App) - if err != nil { - return fmt.Errorf("could not find application '%s' in apps table, got an error: %w", deployment.App, err) - } - if selectApp == nil { - return fmt.Errorf("could not find application '%s' in apps table: got no result", deployment.App) - } - env.Applications[deployment.App] = &api.Environment_Application{ - Version: 0, - QueuedVersion: 0, - UndeployVersion: false, - ArgoCd: nil, - Name: deployment.App, - Locks: map[string]*api.Lock{}, - TeamLocks: map[string]*api.Lock{}, - Team: selectApp.Metadata.Team, - DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ - DeployAuthor: "", - DeployTime: "", - }, - } - appInEnv = env.Applications[deployment.App] + + selectApp, err := h.DBSelectApp(ctx, transaction, deployment.App) + if err != nil { + return fmt.Errorf("could not find application '%s' in apps table, got an error: %w", deployment.App, err) } - if deployment.Version == nil { - appInEnv.Version = 0 - } else { - appInEnv.Version = uint64(*deployment.Version) - } - appInEnv.DeploymentMetaData.DeployAuthor = deployment.Metadata.DeployedByEmail - appInEnv.DeploymentMetaData.DeployTime = fmt.Sprintf("%d", createdTime.Unix()) - if deployment.Version != nil { //Check if not trying to deploy an undeploy version - //Get the undeploy information from the release - release, err := h.DBSelectReleaseByVersion(ctx, transaction, appInEnv.Name, appInEnv.Version, true) - if err != nil { - return fmt.Errorf("error getting release %d for app %s", appInEnv.Version, appInEnv.Name) - } - if release == nil { - return fmt.Errorf("could not find release %d for app %s", appInEnv.Version, appInEnv.Name) - } - appInEnv.UndeployVersion = release.Metadata.UndeployVersion + if selectApp == nil { + return fmt.Errorf("could not find application '%s' in apps table: got no result", deployment.App) } - app := getApplicationByName(latestOverview.Applications, deployment.App) - if app != nil { - app.Warnings = CalculateWarnings(ctx, app.Name, latestOverview.EnvironmentGroups) - app.UndeploySummary = deriveUndeploySummary(app.Name, latestOverview.EnvironmentGroups) - } err = h.WriteOverviewCache(ctx, transaction, latestOverview) if err != nil { return err @@ -182,33 +156,12 @@ func (h *DBHandler) UpdateOverviewDeploymentAttempt(ctx context.Context, transac if env == nil { return fmt.Errorf("could not find environment %s in overview", queuedDeployment.Env) } - app := getEnvironmentApplicationByName(env.Applications, queuedDeployment.App) - if app == nil { - selectApp, err := h.DBSelectApp(ctx, transaction, queuedDeployment.App) - if err != nil { - return fmt.Errorf("could not find application '%s' in apps table, got an error: %w", queuedDeployment.App, err) - } - if selectApp == nil { - return fmt.Errorf("could not find application '%s' in apps table: got no result", queuedDeployment.App) - } - env.Applications[queuedDeployment.App] = &api.Environment_Application{ - Version: 0, - QueuedVersion: 0, - UndeployVersion: false, - ArgoCd: nil, - Name: queuedDeployment.App, - Locks: map[string]*api.Lock{}, - TeamLocks: map[string]*api.Lock{}, - Team: selectApp.Metadata.Team, - DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ - DeployAuthor: "", - DeployTime: "", - }, - } - app = env.Applications[queuedDeployment.App] + selectApp, err := h.DBSelectApp(ctx, transaction, queuedDeployment.App) + if err != nil { + return fmt.Errorf("could not find application '%s' in apps table, got an error: %w", queuedDeployment.App, err) } - if queuedDeployment.Version != nil { - app.QueuedVersion = uint64(*queuedDeployment.Version) + if selectApp == nil { + return fmt.Errorf("could not find application '%s' in apps table: got no result", queuedDeployment.App) } err = h.WriteOverviewCache(ctx, transaction, latestOverview) if err != nil { @@ -217,35 +170,6 @@ func (h *DBHandler) UpdateOverviewDeploymentAttempt(ctx context.Context, transac return nil } -func deriveUndeploySummary(appName string, groups []*api.EnvironmentGroup) api.UndeploySummary { - var allNormal = true - var allUndeploy = true - for _, group := range groups { - for _, environment := range group.Environments { - var app, exists = environment.Applications[appName] - if !exists { - continue - } - if app.Version == 0 { - // if the app exists but nothing is deployed, we ignore this - continue - } - if app.UndeployVersion { - allNormal = false - } else { - allUndeploy = false - } - } - } - if allUndeploy { - return api.UndeploySummary_UNDEPLOY - } - if allNormal { - return api.UndeploySummary_NORMAL - } - return api.UndeploySummary_MIXED -} - func (h *DBHandler) UpdateOverviewApplicationLock(ctx context.Context, transaction *sql.Tx, applicationLock ApplicationLock, createdTime time.Time) error { latestOverview, err := h.ReadLatestOverviewCache(ctx, transaction) if err != nil { @@ -258,114 +182,45 @@ func (h *DBHandler) UpdateOverviewApplicationLock(ctx context.Context, transacti if env == nil { return fmt.Errorf("could not find environment %s in overview", applicationLock.Env) } - app := getEnvironmentApplicationByName(env.Applications, applicationLock.App) - if app == nil { - selectApp, err := h.DBSelectApp(ctx, transaction, applicationLock.App) - if err != nil { - return fmt.Errorf("could not find application '%s' in apps table, got an error: %w", applicationLock.App, err) - } - if selectApp == nil { - return fmt.Errorf("could not find application '%s' in apps table: got no result", applicationLock.App) - } - env.Applications[applicationLock.App] = &api.Environment_Application{ - Version: 0, - QueuedVersion: 0, - UndeployVersion: false, - ArgoCd: nil, - Name: applicationLock.App, - Locks: map[string]*api.Lock{}, - TeamLocks: map[string]*api.Lock{}, - Team: selectApp.Metadata.Team, - DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ - DeployAuthor: "", - DeployTime: "", - }, - } - app = env.Applications[applicationLock.App] - } - if app.Locks == nil { - app.Locks = map[string]*api.Lock{} - } - app.Locks[applicationLock.LockID] = &api.Lock{ - Message: applicationLock.Metadata.Message, - LockId: applicationLock.LockID, - CreatedAt: timestamppb.New(createdTime), - CreatedBy: &api.Actor{ - Name: applicationLock.Metadata.CreatedByName, - Email: applicationLock.Metadata.CreatedByEmail, - }, - } - if applicationLock.Deleted { - delete(app.Locks, applicationLock.LockID) - } - err = h.WriteOverviewCache(ctx, transaction, latestOverview) + selectApp, err := h.DBSelectApp(ctx, transaction, applicationLock.App) if err != nil { - return err + return fmt.Errorf("could not find application '%s' in apps table, got an error: %w", applicationLock.App, err) } - return nil -} - -func (h *DBHandler) UpdateOverviewRelease(ctx context.Context, transaction *sql.Tx, release DBReleaseWithMetaData) error { - latestOverview, err := h.ReadLatestOverviewCache(ctx, transaction) - if err != nil { - return err + if selectApp == nil { + return fmt.Errorf("could not find application '%s' in apps table: got no result", applicationLock.App) } - if h.IsOverviewEmpty(latestOverview) { - return nil + if env.AppLocks == nil { + env.AppLocks = make(map[string]*api.Locks) } - app := getApplicationByName(latestOverview.Applications, release.App) - if app == nil { - if release.Deleted { - return nil - } - selectApp, err := h.DBSelectApp(ctx, transaction, release.App) - if err != nil { - return fmt.Errorf("could not find application '%s' in apps table, got an error: %w", release.App, err) - } - if selectApp == nil { - return fmt.Errorf("could not find application '%s' in apps table: got no result", release.App) + + if applicationLock.Deleted { + locksToKeep := make([]*api.Lock, max(len(env.AppLocks[applicationLock.App].Locks)-1, 0)) + for _, lock := range env.AppLocks[applicationLock.App].Locks { + if lock.LockId != applicationLock.LockID { + locksToKeep = append(locksToKeep, lock) + } } - app = &api.Application{ - Name: release.App, - Releases: []*api.Release{}, - SourceRepoUrl: "", // TODO - Team: selectApp.Metadata.Team, - UndeploySummary: 0, - Warnings: []*api.Warning{}, + if len(locksToKeep) == 0 { + delete(env.AppLocks, applicationLock.App) + } else { + env.AppLocks[applicationLock.App].Locks = locksToKeep } - latestOverview.Applications[release.App] = app - } - apiRelease := &api.Release{ - PrNumber: extractPrNumber(release.Metadata.SourceMessage), - Version: release.ReleaseNumber, - UndeployVersion: release.Metadata.UndeployVersion, - SourceAuthor: release.Metadata.SourceAuthor, - SourceCommitId: release.Metadata.SourceCommitId, - SourceMessage: release.Metadata.SourceMessage, - CreatedAt: timestamppb.New(release.Created), - DisplayVersion: release.Metadata.DisplayVersion, - IsMinor: release.Metadata.IsMinor, - IsPrepublish: release.Metadata.IsPrepublish, - } - foundRelease := false - for relIndex, currentRelease := range app.Releases { - if currentRelease.Version == release.ReleaseNumber { - if release.Deleted { - app.Releases = append(app.Releases[:relIndex], app.Releases[relIndex+1:]...) - } else { - app.Releases[relIndex] = apiRelease + } else { + if env.AppLocks[applicationLock.App] == nil { + env.AppLocks[applicationLock.App] = &api.Locks{ + Locks: make([]*api.Lock, 0), } - foundRelease = true } + env.AppLocks[applicationLock.App].Locks = append(env.AppLocks[applicationLock.App].Locks, &api.Lock{ + Message: applicationLock.Metadata.Message, + LockId: applicationLock.LockID, + CreatedAt: timestamppb.New(applicationLock.Created), + CreatedBy: &api.Actor{ + Name: applicationLock.Metadata.CreatedByName, + Email: applicationLock.Metadata.CreatedByEmail, + }, + }) } - if !foundRelease && !release.Deleted { - app.Releases = append(app.Releases, apiRelease) - } - - if release.Metadata.UndeployVersion { - app.UndeploySummary = deriveUndeploySummary(app.Name, latestOverview.EnvironmentGroups) - } - err = h.WriteOverviewCache(ctx, transaction, latestOverview) if err != nil { return err @@ -377,7 +232,7 @@ func (h *DBHandler) IsOverviewEmpty(overviewResp *api.GetOverviewResponse) bool if overviewResp == nil { return true } - if len(overviewResp.Applications) == 0 && len(overviewResp.EnvironmentGroups) == 0 && overviewResp.GitRevision == "" { + if len(overviewResp.EnvironmentGroups) == 0 && overviewResp.GitRevision == "" { return true } return false @@ -429,100 +284,3 @@ func getEnvironmentByName(groups []*api.EnvironmentGroup, envNameToReturn string } return nil } - -func getEnvironmentApplicationsByTeam(apps map[string]*api.Environment_Application, team string) []*api.Environment_Application { - foundApps := []*api.Environment_Application{} - for _, app := range apps { - if app.Team == team { - foundApps = append(foundApps, app) - } - } - return foundApps -} - -func getEnvironmentApplicationByName(apps map[string]*api.Environment_Application, appNameToReturn string) *api.Environment_Application { - for _, app := range apps { - if app.Name == appNameToReturn { - return app - } - } - return nil -} - -func getApplicationByName(apps map[string]*api.Application, appNameToReturn string) *api.Application { - for _, app := range apps { - if app.Name == appNameToReturn { - return app - } - } - return nil -} - -func extractPrNumber(sourceMessage string) string { - re := regexp.MustCompile(`\(#(\d+)\)`) - res := re.FindAllStringSubmatch(sourceMessage, -1) - - if len(res) == 0 { - return "" - } else { - return res[len(res)-1][1] - } -} - -func CalculateWarnings(ctx context.Context, appName string, groups []*api.EnvironmentGroup) []*api.Warning { - result := make([]*api.Warning, 0) - for e := 0; e < len(groups); e++ { - group := groups[e] - for i := 0; i < len(groups[e].Environments); i++ { - env := group.Environments[i] - if env.Config.Upstream == nil || env.Config.Upstream.Environment == nil { - // if the env has no upstream, there's nothing to warn about - continue - } - upstreamEnvName := env.Config.GetUpstream().Environment - upstreamEnv := getEnvironmentByName(groups, *upstreamEnvName) - if upstreamEnv == nil { - // this is already checked on startup and therefore shouldn't happen here - continue - } - - appInEnv := env.Applications[appName] - if appInEnv == nil { - // appName is not deployed here, ignore it - continue - } - versionInEnv := appInEnv.Version - appInUpstreamEnv := upstreamEnv.Applications[appName] - if appInUpstreamEnv == nil { - // appName is not deployed upstream... that's unusual! - var warning = api.Warning{ - WarningType: &api.Warning_UpstreamNotDeployed{ - UpstreamNotDeployed: &api.UpstreamNotDeployed{ - UpstreamEnvironment: *upstreamEnvName, - ThisVersion: versionInEnv, - ThisEnvironment: env.Name, - }, - }, - } - result = append(result, &warning) - continue - } - versionInUpstreamEnv := appInUpstreamEnv.Version - - if versionInEnv > versionInUpstreamEnv && len(appInEnv.Locks) == 0 { - var warning = api.Warning{ - WarningType: &api.Warning_UnusualDeploymentOrder{ - UnusualDeploymentOrder: &api.UnusualDeploymentOrder{ - UpstreamVersion: versionInUpstreamEnv, - UpstreamEnvironment: *upstreamEnvName, - ThisVersion: versionInEnv, - ThisEnvironment: env.Name, - }, - }, - } - result = append(result, &warning) - } - } - } - return result -} diff --git a/pkg/db/overview_test.go b/pkg/db/overview_test.go index f66c28c6b..ec31c1e21 100644 --- a/pkg/db/overview_test.go +++ b/pkg/db/overview_test.go @@ -43,11 +43,12 @@ func getOverviewIgnoredTypes() cmp.Option { timestamppb.Timestamp{}, api.EnvironmentConfig{}, api.EnvironmentConfig_Upstream{}, - api.Environment_Application{}, - api.Environment_Application_DeploymentMetaData{}, api.EnvironmentConfig_ArgoCD{}, api.Lock{}, - api.Actor{}) + api.Actor{}, + api.OverviewApplication{}, + api.Deployment{}, + api.Locks{}) } func makeTestStartingOverview() *api.GetOverviewResponse { @@ -67,48 +68,16 @@ func makeTestStartingOverview() *api.GetOverviewResponse { Argocd: &api.EnvironmentConfig_ArgoCD{}, EnvironmentGroup: &dev, }, - Applications: map[string]*api.Environment_Application{ - "test": { - Name: "test", - Version: 1, - DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ - DeployAuthor: "testmail@example.com", - DeployTime: "1", - }, - Team: "team-123", - }, - }, Priority: api.Priority_YOLO, }, }, Priority: api.Priority_YOLO, }, }, - Applications: map[string]*api.Application{ - "test": { + LightweightApps: []*api.OverviewApplication{ + { Name: "test", - Releases: []*api.Release{ - { - Version: 1, - SourceCommitId: "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef", - SourceAuthor: "example ", - SourceMessage: "changed something (#678)", - PrNumber: "678", - CreatedAt: ×tamppb.Timestamp{Seconds: 1, Nanos: 1}, - }, - }, Team: "team-123", - Warnings: []*api.Warning{ - { - WarningType: &api.Warning_UnusualDeploymentOrder{ - UnusualDeploymentOrder: &api.UnusualDeploymentOrder{ - UpstreamEnvironment: "staging", - ThisVersion: 12, - ThisEnvironment: "development", - }, - }, - }, - }, }, }, GitRevision: "0", @@ -155,17 +124,10 @@ func TestUpdateOverviewTeamLock(t *testing.T) { Argocd: &api.EnvironmentConfig_ArgoCD{}, EnvironmentGroup: &dev, }, - Applications: map[string]*api.Environment_Application{ - "test": { - Name: "test", - Version: 1, - DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ - DeployAuthor: "testmail@example.com", - DeployTime: "1", - }, - Team: "team-123", - TeamLocks: map[string]*api.Lock{ - "dev-lock": { + TeamLocks: map[string]*api.Locks{ + "team-123": { + Locks: []*api.Lock{ + { Message: "My lock on dev for my-team", LockId: "dev-lock", CreatedAt: timestamppb.New(time.Date(2024, time.July, 12, 15, 30, 0, 0, time.UTC)), @@ -183,31 +145,10 @@ func TestUpdateOverviewTeamLock(t *testing.T) { Priority: api.Priority_YOLO, }, }, - Applications: map[string]*api.Application{ - "test": { + LightweightApps: []*api.OverviewApplication{ + { Name: "test", - Releases: []*api.Release{ - { - Version: 1, - SourceCommitId: "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef", - SourceAuthor: "example ", - SourceMessage: "changed something (#678)", - PrNumber: "678", - CreatedAt: ×tamppb.Timestamp{Seconds: 1, Nanos: 1}, - }, - }, Team: "team-123", - Warnings: []*api.Warning{ - { - WarningType: &api.Warning_UnusualDeploymentOrder{ - UnusualDeploymentOrder: &api.UnusualDeploymentOrder{ - UpstreamEnvironment: "staging", - ThisVersion: 12, - ThisEnvironment: "development", - }, - }, - }, - }, }, }, GitRevision: "0", @@ -279,6 +220,7 @@ func TestUpdateOverviewTeamLock(t *testing.T) { if diff := cmp.Diff(startingOverview, latestOverview, opts); diff != "" { return fmt.Errorf("starting overview and last overview mismatch (-want +got):\n%s", diff) } + return nil }) @@ -327,17 +269,6 @@ func TestUpdateOverviewEnvironmentLock(t *testing.T) { Argocd: &api.EnvironmentConfig_ArgoCD{}, EnvironmentGroup: &dev, }, - Applications: map[string]*api.Environment_Application{ - "test": { - Name: "test", - Version: 1, - DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ - DeployAuthor: "testmail@example.com", - DeployTime: "1", - }, - Team: "team-123", - }, - }, Locks: map[string]*api.Lock{ "dev-lock": { LockId: "dev-lock", @@ -355,31 +286,10 @@ func TestUpdateOverviewEnvironmentLock(t *testing.T) { Priority: api.Priority_YOLO, }, }, - Applications: map[string]*api.Application{ - "test": { + LightweightApps: []*api.OverviewApplication{ + { Name: "test", - Releases: []*api.Release{ - { - Version: 1, - SourceCommitId: "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef", - SourceAuthor: "example ", - SourceMessage: "changed something (#678)", - PrNumber: "678", - CreatedAt: ×tamppb.Timestamp{Seconds: 1, Nanos: 1}, - }, - }, Team: "team-123", - Warnings: []*api.Warning{ - { - WarningType: &api.Warning_UnusualDeploymentOrder{ - UnusualDeploymentOrder: &api.UnusualDeploymentOrder{ - UpstreamEnvironment: "staging", - ThisVersion: 12, - ThisEnvironment: "development", - }, - }, - }, - }, }, }, GitRevision: "0", @@ -463,6 +373,7 @@ func TestUpdateOverviewDeployment(t *testing.T) { ExcpectedOverview *api.GetOverviewResponse ExpectedError error }{ + //TODO: This test suite has some commented out sections. These tests should either be adapted or reimplemented in Ref: SRX-9PBRYS. { Name: "Update overview", NewDeployment: Deployment{ @@ -488,36 +399,42 @@ func TestUpdateOverviewDeployment(t *testing.T) { Argocd: &api.EnvironmentConfig_ArgoCD{}, EnvironmentGroup: &dev, }, - Applications: map[string]*api.Environment_Application{ - "test": { - Name: "test", - Version: 12, - DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ - DeployAuthor: "testmail2@example.com", - DeployTime: fmt.Sprintf("%d", time.Date(2024, time.July, 12, 15, 30, 0, 0, time.UTC).Unix()), - }, - Team: "team-123", - }, - }, + //Applications: map[string]*api.Environment_Application{ + // "test": { + // Name: "test", + // Version: 12, + // DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ + // DeployAuthor: "testmail2@example.com", + // DeployTime: fmt.Sprintf("%d", time.Date(2024, time.July, 12, 15, 30, 0, 0, time.UTC).Unix()), + // }, + // Team: "team-123", + // }, + //}, Priority: api.Priority_YOLO, }, }, Priority: api.Priority_YOLO, }, }, - Applications: map[string]*api.Application{ - "test": { + //Applications: map[string]*api.Application{ + // "test": { + // Name: "test", + // Releases: []*api.Release{ + // { + // Version: 1, + // SourceCommitId: "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef", + // SourceAuthor: "example ", + // SourceMessage: "changed something (#678)", + // PrNumber: "678", + // CreatedAt: ×tamppb.Timestamp{Seconds: 1, Nanos: 1}, + // }, + // }, + // Team: "team-123", + // }, + //}, + LightweightApps: []*api.OverviewApplication{ + { Name: "test", - Releases: []*api.Release{ - { - Version: 1, - SourceCommitId: "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef", - SourceAuthor: "example ", - SourceMessage: "changed something (#678)", - PrNumber: "678", - CreatedAt: ×tamppb.Timestamp{Seconds: 1, Nanos: 1}, - }, - }, Team: "team-123", }, }, @@ -587,1094 +504,251 @@ func TestUpdateOverviewDeployment(t *testing.T) { } } -func TestUpdateOverviewDeploymentAttempt(t *testing.T) { - var dev = "dev" - var upstreamLatest = true - var version int64 = 12 - startingOverview := makeTestStartingOverview() - tcs := []struct { - Name string - NewDeployment *QueuedDeployment - ExpectedError error - ExpectedOverview *api.GetOverviewResponse +//TODO: This test suite has some commented out sections. These tests should either be adapted or reimplemented in Ref: SRX-9PBRYS. +//func TestCalculateWarnings(t *testing.T) { +// var dev = "dev" +// tcs := []struct { +// Name string +// AppName string +// Groups []*api.EnvironmentGroup +// ExpectedWarnings []*api.Warning +// }{ +// { +// Name: "no envs - no warning", +// AppName: "foo", +// Groups: []*api.EnvironmentGroup{ +// makeEnvGroup(dev, []*api.Environment{ +// makeEnv("dev-de", dev, makeUpstreamLatest(), nil), +// })}, +// ExpectedWarnings: []*api.Warning{}, +// }, +// { +// Name: "app deployed in higher version on upstream should warn", +// AppName: "foo", +// Groups: []*api.EnvironmentGroup{ +// makeEnvGroup(dev, []*api.Environment{ +// makeEnv("prod", dev, makeUpstreamEnv("dev"), +// makeApps(makeApp("foo", 2))), +// }), +// makeEnvGroup(dev, []*api.Environment{ +// makeEnv("dev", dev, makeUpstreamLatest(), +// makeApps(makeApp("foo", 1))), +// }), +// }, +// ExpectedWarnings: []*api.Warning{ +// { +// WarningType: &api.Warning_UnusualDeploymentOrder{ +// UnusualDeploymentOrder: &api.UnusualDeploymentOrder{ +// UpstreamVersion: 1, +// UpstreamEnvironment: "dev", +// ThisVersion: 2, +// ThisEnvironment: "prod", +// }, +// }, +// }, +// }, +// }, +// { +// Name: "app deployed in same version on upstream should not warn", +// AppName: "foo", +// Groups: []*api.EnvironmentGroup{ +// makeEnvGroup(dev, []*api.Environment{ +// makeEnv("prod", dev, makeUpstreamEnv("dev"), +// makeApps(makeApp("foo", 2))), +// }), +// makeEnvGroup(dev, []*api.Environment{ +// makeEnv("dev", dev, makeUpstreamLatest(), +// makeApps(makeApp("foo", 2))), +// }), +// }, +// ExpectedWarnings: []*api.Warning{}, +// }, +// { +// Name: "app deployed in no version on upstream should warn", +// AppName: "foo", +// Groups: []*api.EnvironmentGroup{ +// makeEnvGroup(dev, []*api.Environment{ +// makeEnv("prod", dev, makeUpstreamEnv("dev"), +// makeApps(makeApp("foo", 1))), +// }), +// makeEnvGroup(dev, []*api.Environment{ +// makeEnv("dev", dev, makeUpstreamLatest(), +// makeApps()), +// }), +// }, +// ExpectedWarnings: []*api.Warning{ +// { +// WarningType: &api.Warning_UpstreamNotDeployed{ +// UpstreamNotDeployed: &api.UpstreamNotDeployed{ +// UpstreamEnvironment: "dev", +// ThisVersion: 1, +// ThisEnvironment: "prod", +// }, +// }, +// }, +// }, +// }, +// } +// for _, tc := range tcs { +// tc := tc +// t.Run(tc.Name, func(t *testing.T) { +// actualWarnings := CalculateWarnings(testutil.MakeTestContext(), tc.AppName, tc.Groups) +// if len(actualWarnings) != len(tc.ExpectedWarnings) { +// t.Errorf("Different number of warnings. got: %s\nwant: %s", actualWarnings, tc.ExpectedWarnings) +// } +// for i := 0; i < len(actualWarnings); i++ { +// actualWarning := actualWarnings[i] +// expectedWarning := tc.ExpectedWarnings[i] +// if diff := cmp.Diff(actualWarning.String(), expectedWarning.String()); diff != "" { +// t.Errorf("Different warning at index [%d]:\ngot: %s\nwant: %s", i, actualWarning, expectedWarning) +// } +// } +// }) +// } +//} +// +//func groupFromEnvs(environments []*api.Environment) []*api.EnvironmentGroup { +// return []*api.EnvironmentGroup{ +// { +// EnvironmentGroupName: "group1", +// Environments: environments, +// }, +// } +//} + +func TestDBDeleteOldOverview(t *testing.T) { + upstreamLatest := true + dev := "dev" + var tcs = []struct { + Name string + inputOverviews []*api.GetOverviewResponse + timeThresholdDiff time.Duration + numberOfOverviewsToKeep uint64 + expectedNumberOfRemainingOverviews uint64 }{ + //TODO: This test suite has some commented out sections. These tests should either be adapted or reimplemented in Ref: SRX-9PBRYS. { - Name: "Update overview Deployment Attempt", - NewDeployment: &QueuedDeployment{ - EslVersion: 1, - Env: "development", - App: "test", - Version: &version, - Created: time.Date(2024, time.July, 12, 15, 30, 0, 0, time.UTC), - }, - ExpectedOverview: &api.GetOverviewResponse{ - EnvironmentGroups: []*api.EnvironmentGroup{ - { - EnvironmentGroupName: "dev", - Environments: []*api.Environment{ - { - Name: "development", - Config: &api.EnvironmentConfig{ - Upstream: &api.EnvironmentConfig_Upstream{ - Latest: &upstreamLatest, - }, - Argocd: &api.EnvironmentConfig_ArgoCD{}, - EnvironmentGroup: &dev, - }, - Applications: map[string]*api.Environment_Application{ - "test": { - Name: "test", - Version: 1, - DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ - DeployAuthor: "testmail@example.com", - DeployTime: "1", + Name: "4 overviews, should keep two", + inputOverviews: []*api.GetOverviewResponse{ + &api.GetOverviewResponse{}, + &api.GetOverviewResponse{}, + &api.GetOverviewResponse{}, + &api.GetOverviewResponse{ + EnvironmentGroups: []*api.EnvironmentGroup{ + { + EnvironmentGroupName: "dev", + Environments: []*api.Environment{ + { + Name: "development", + Config: &api.EnvironmentConfig{ + Upstream: &api.EnvironmentConfig_Upstream{ + Latest: &upstreamLatest, }, - Team: "team-123", - QueuedVersion: 12, + Argocd: &api.EnvironmentConfig_ArgoCD{}, + EnvironmentGroup: &dev, }, + //Applications: map[string]*api.Environment_Application{ + // "test": { + // Name: "test", + // Version: 1, + // DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ + // DeployAuthor: "testmail@example.com", + // DeployTime: "1", + // }, + // Team: "team-123", + // }, + //}, + Priority: api.Priority_YOLO, }, - Priority: api.Priority_YOLO, }, + Priority: api.Priority_YOLO, }, - Priority: api.Priority_YOLO, }, - }, - Applications: map[string]*api.Application{ - "test": { - Name: "test", - Releases: []*api.Release{ - { - Version: 1, - SourceCommitId: "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef", - SourceAuthor: "example ", - SourceMessage: "changed something (#678)", - PrNumber: "678", - CreatedAt: ×tamppb.Timestamp{Seconds: 1, Nanos: 1}, - }, - }, - Team: "team-123", - Warnings: []*api.Warning{ - { - WarningType: &api.Warning_UnusualDeploymentOrder{ - UnusualDeploymentOrder: &api.UnusualDeploymentOrder{ - UpstreamEnvironment: "staging", - ThisVersion: 12, - ThisEnvironment: "development", - }, - }, - }, + //Applications: map[string]*api.Application{ + // "test": { + // Name: "test", + // Releases: []*api.Release{ + // { + // Version: 1, + // SourceCommitId: "changedcommitId", + // SourceAuthor: "changedAuthor", + // SourceMessage: "changed changed something (#679)", + // PrNumber: "679", + // CreatedAt: ×tamppb.Timestamp{Seconds: 1, Nanos: 1}, + // }, + // }, + // Team: "team-123", + // }, + //}, + LightweightApps: []*api.OverviewApplication{ + { + Name: "test", + Team: "team-123", }, }, + GitRevision: "0", }, - GitRevision: "0", - }, - }, - { - Name: "app does not exists", - NewDeployment: &QueuedDeployment{ - EslVersion: 1, - Env: "development", - App: "does-not-exists", - Version: &version, - Created: time.Date(2024, time.July, 12, 15, 30, 0, 0, time.UTC), - }, - ExpectedError: errMatcher{"could not find application 'does-not-exists' in apps table: got no result"}, - }, - { - Name: "env does not exists", - NewDeployment: &QueuedDeployment{ - EslVersion: 1, - Env: "does-not-exists", - App: "test", - Version: &version, - Created: time.Date(2024, time.July, 12, 15, 30, 0, 0, time.UTC), }, - ExpectedError: errMatcher{"could not find environment does-not-exists in overview"}, - }, - { - Name: "nil queued deployment", - ExpectedOverview: startingOverview, + timeThresholdDiff: 150 * time.Second, + numberOfOverviewsToKeep: 2, + expectedNumberOfRemainingOverviews: 2, }, - } - - for _, tc := range tcs { - t.Run(tc.Name, func(t *testing.T) { - ctx := testutil.MakeTestContext() - dbHandler := setupDB(t) - - err := dbHandler.WithTransaction(ctx, false, func(ctx context.Context, transaction *sql.Tx) error { - err := dbHandler.WriteOverviewCache(ctx, transaction, startingOverview) - if err != nil { - return err - } - err = dbHandler.UpdateOverviewDeploymentAttempt(ctx, transaction, tc.NewDeployment) - if err != nil { - if diff := cmp.Diff(tc.ExpectedError, err, cmpopts.EquateErrors()); diff != "" { - return fmt.Errorf("mismatch between errors (-want +got):\n%s", diff) - } - return nil - } - latestOverview, err := dbHandler.ReadLatestOverviewCache(ctx, transaction) - if err != nil { - return err - } - opts := getOverviewIgnoredTypes() - if diff := cmp.Diff(tc.ExpectedOverview, latestOverview, opts); diff != "" { - return fmt.Errorf("mismatch (-want +got):\n%s", diff) - } - return nil - }) - if err != nil { - t.Fatal(err) - } - }) - } -} - -func TestUpdateOverviewApplicationLock(t *testing.T) { - var dev = "dev" - var upstreamLatest = true - startingOverview := makeTestStartingOverview() - tcs := []struct { - Name string - NewApplicationLock ApplicationLock - ExcpectedOverview *api.GetOverviewResponse - ExpectedError error - }{ { - Name: "Update overview", - NewApplicationLock: ApplicationLock{ - Env: "development", - App: "test", - LockID: "dev-lock", - EslVersion: 2, - Deleted: false, - Metadata: LockMetadata{ - Message: "My lock on dev for my-team", - CreatedByName: "myself", - CreatedByEmail: "myself@example.com", - }, - Created: time.Date(2024, time.July, 12, 15, 30, 0, 0, time.UTC), - }, - ExcpectedOverview: &api.GetOverviewResponse{ - EnvironmentGroups: []*api.EnvironmentGroup{ - { - EnvironmentGroupName: "dev", - Environments: []*api.Environment{ - { - Name: "development", - Config: &api.EnvironmentConfig{ - Upstream: &api.EnvironmentConfig_Upstream{ - Latest: &upstreamLatest, - }, - Argocd: &api.EnvironmentConfig_ArgoCD{}, - EnvironmentGroup: &dev, - }, - Applications: map[string]*api.Environment_Application{ - "test": { - Name: "test", - Version: 1, - DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ - DeployAuthor: "testmail@example.com", - DeployTime: "1", - }, - Team: "team-123", - Locks: map[string]*api.Lock{ - "dev-lock": { - Message: "My lock on dev for my-team", - LockId: "dev-lock", - CreatedAt: timestamppb.New(time.Date(2024, time.July, 12, 15, 30, 0, 0, time.UTC)), - CreatedBy: &api.Actor{ - Name: "myself", - Email: "myself@example.com", - }, - }, + Name: "4 overviews, early time threshhold, all should remain", + inputOverviews: []*api.GetOverviewResponse{ + &api.GetOverviewResponse{}, + &api.GetOverviewResponse{}, + &api.GetOverviewResponse{}, + &api.GetOverviewResponse{ + EnvironmentGroups: []*api.EnvironmentGroup{ + { + EnvironmentGroupName: "dev", + Environments: []*api.Environment{ + { + Name: "development", + Config: &api.EnvironmentConfig{ + Upstream: &api.EnvironmentConfig_Upstream{ + Latest: &upstreamLatest, }, + Argocd: &api.EnvironmentConfig_ArgoCD{}, + EnvironmentGroup: &dev, }, - }, - Priority: api.Priority_YOLO, - }, - }, - Priority: api.Priority_YOLO, - }, - }, - Applications: map[string]*api.Application{ - "test": { - Name: "test", - Releases: []*api.Release{ - { - Version: 1, - SourceCommitId: "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef", - SourceAuthor: "example ", - SourceMessage: "changed something (#678)", - PrNumber: "678", - CreatedAt: ×tamppb.Timestamp{Seconds: 1, Nanos: 1}, - }, - }, - Team: "team-123", - Warnings: []*api.Warning{ - { - WarningType: &api.Warning_UnusualDeploymentOrder{ - UnusualDeploymentOrder: &api.UnusualDeploymentOrder{ - UpstreamEnvironment: "staging", - ThisVersion: 12, - ThisEnvironment: "development", - }, + //Applications: map[string]*api.Environment_Application{ + // "test": { + // Name: "test", + // Version: 1, + // DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ + // DeployAuthor: "testmail@example.com", + // DeployTime: "1", + // }, + // Team: "team-123", + // }, + //}, + Priority: api.Priority_YOLO, }, }, + Priority: api.Priority_YOLO, }, }, - }, - GitRevision: "0", - }, - }, - { - Name: "env does not exists", - NewApplicationLock: ApplicationLock{ - Env: "does-not-exists", - App: "test", - LockID: "dev-lock", - EslVersion: 2, - Deleted: false, - Metadata: LockMetadata{ - Message: "My lock on dev for my-team", - CreatedByName: "myself", - CreatedByEmail: "myself@example.com", - }, - Created: time.Date(2024, time.July, 12, 15, 30, 0, 0, time.UTC), - }, - ExpectedError: errMatcher{"could not find environment does-not-exists in overview"}, - }, - { - Name: "app does not exists", - NewApplicationLock: ApplicationLock{ - Env: "development", - App: "does-not-exists", - LockID: "dev-lock", - EslVersion: 2, - Deleted: false, - Metadata: LockMetadata{ - Message: "My lock on dev for my-team", - CreatedByName: "myself", - CreatedByEmail: "myself@example.com", - }, - Created: time.Date(2024, time.July, 12, 15, 30, 0, 0, time.UTC), - }, - ExpectedError: errMatcher{"could not find application 'does-not-exists' in apps table: got no result"}, - }, - } - - for _, tc := range tcs { - tc := tc - t.Run(tc.Name, func(t *testing.T) { - t.Parallel() - ctx := testutil.MakeTestContext() - dbHandler := setupDB(t) - - err := dbHandler.WithTransaction(ctx, false, func(ctx context.Context, transaction *sql.Tx) error { - err := dbHandler.WriteOverviewCache(ctx, transaction, startingOverview) - if err != nil { - return err - } - err = dbHandler.UpdateOverviewApplicationLock(ctx, transaction, tc.NewApplicationLock, tc.NewApplicationLock.Created) - if err != nil { - if diff := cmp.Diff(tc.ExpectedError, err, cmpopts.EquateErrors()); diff != "" { - return fmt.Errorf("mismatch between errors (-want +got):\n%s", diff) - } - return nil - } - latestOverview, err := dbHandler.ReadLatestOverviewCache(ctx, transaction) - if err != nil { - return err - } - opts := getOverviewIgnoredTypes() - if diff := cmp.Diff(tc.ExcpectedOverview, latestOverview, opts); diff != "" { - return fmt.Errorf("mismatch (-want +got):\n%s", diff) - } - tc.NewApplicationLock.Deleted = true - err = dbHandler.UpdateOverviewApplicationLock(ctx, transaction, tc.NewApplicationLock, tc.NewApplicationLock.Created) - if err != nil { - return err - } - latestOverview, err = dbHandler.ReadLatestOverviewCache(ctx, transaction) - if err != nil { - return err - } - if diff := cmp.Diff(startingOverview, latestOverview, opts); diff != "" { - return fmt.Errorf("mismatch (-want +got):\n%s", diff) - } - return nil - }) - - if err != nil { - t.Fatal(err) - } - }) - } -} - -func TestUpdateOverviewRelease(t *testing.T) { - var dev = "dev" - var upstreamLatest = true - startingOverview := makeTestStartingOverview() - tcs := []struct { - Name string - NewRelease DBReleaseWithMetaData - ExcpectedOverview *api.GetOverviewResponse - ExpectedError error - }{ - { - Name: "Update overview add release", - NewRelease: DBReleaseWithMetaData{ - App: "test", - ReleaseNumber: 12, - Metadata: DBReleaseMetaData{ - SourceAuthor: "testmail@example.com", - SourceCommitId: "testcommit", - SourceMessage: "changed something (#677)", - DisplayVersion: "12", - }, - Created: time.Date(2024, time.July, 12, 15, 30, 0, 0, time.UTC), - }, - ExcpectedOverview: &api.GetOverviewResponse{ - EnvironmentGroups: []*api.EnvironmentGroup{ - { - EnvironmentGroupName: "dev", - Environments: []*api.Environment{ - { - Name: "development", - Config: &api.EnvironmentConfig{ - Upstream: &api.EnvironmentConfig_Upstream{ - Latest: &upstreamLatest, - }, - Argocd: &api.EnvironmentConfig_ArgoCD{}, - EnvironmentGroup: &dev, - }, - Applications: map[string]*api.Environment_Application{ - "test": { - Name: "test", - Version: 1, - DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ - DeployAuthor: "testmail@example.com", - DeployTime: "1", - }, - Team: "team-123", - }, - }, - Priority: api.Priority_YOLO, - }, - }, - Priority: api.Priority_YOLO, - }, - }, - Applications: map[string]*api.Application{ - "test": { - Name: "test", - Releases: []*api.Release{ - { - Version: 1, - SourceCommitId: "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef", - SourceAuthor: "example ", - SourceMessage: "changed something (#678)", - PrNumber: "678", - CreatedAt: ×tamppb.Timestamp{Seconds: 1, Nanos: 1}, - }, - { - Version: 12, - SourceCommitId: "testcommit", - SourceAuthor: "testmail@example.com", - SourceMessage: "changed something (#678)", - PrNumber: "677", - CreatedAt: ×tamppb.Timestamp{Seconds: 1, Nanos: 1}, - }, - }, - Team: "team-123", - }, - }, - GitRevision: "0", - }, - }, - { - Name: "Update overview delete release", - NewRelease: DBReleaseWithMetaData{ - App: "test", - ReleaseNumber: 1, - Metadata: DBReleaseMetaData{ - SourceAuthor: "example ", - SourceCommitId: "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef", - SourceMessage: "changed something (#678)", - DisplayVersion: "12", - }, - Deleted: true, - Created: time.Date(2024, time.July, 12, 15, 30, 0, 0, time.UTC), - }, - ExcpectedOverview: &api.GetOverviewResponse{ - EnvironmentGroups: []*api.EnvironmentGroup{ - { - EnvironmentGroupName: "dev", - Environments: []*api.Environment{ - { - Name: "development", - Config: &api.EnvironmentConfig{ - Upstream: &api.EnvironmentConfig_Upstream{ - Latest: &upstreamLatest, - }, - Argocd: &api.EnvironmentConfig_ArgoCD{}, - EnvironmentGroup: &dev, - }, - Applications: map[string]*api.Environment_Application{ - "test": { - Name: "test", - Version: 1, - DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ - DeployAuthor: "testmail@example.com", - DeployTime: "1", - }, - Team: "team-123", - }, - }, - Priority: api.Priority_YOLO, - }, - }, - Priority: api.Priority_YOLO, - }, - }, - Applications: map[string]*api.Application{ - "test": { - Name: "test", - Releases: []*api.Release{}, - Team: "team-123", - }, - }, - GitRevision: "0", - }, - }, - { - Name: "Update overview update release", - NewRelease: DBReleaseWithMetaData{ - App: "test", - ReleaseNumber: 1, - Metadata: DBReleaseMetaData{ - SourceAuthor: "changedAuthor", - SourceCommitId: "changedcommitId", - SourceMessage: "changed changed something (#679)", - DisplayVersion: "12", - }, - Created: time.Date(2024, time.July, 12, 15, 30, 0, 0, time.UTC), - }, - ExcpectedOverview: &api.GetOverviewResponse{ - EnvironmentGroups: []*api.EnvironmentGroup{ - { - EnvironmentGroupName: "dev", - Environments: []*api.Environment{ - { - Name: "development", - Config: &api.EnvironmentConfig{ - Upstream: &api.EnvironmentConfig_Upstream{ - Latest: &upstreamLatest, - }, - Argocd: &api.EnvironmentConfig_ArgoCD{}, - EnvironmentGroup: &dev, - }, - Applications: map[string]*api.Environment_Application{ - "test": { - Name: "test", - Version: 1, - DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ - DeployAuthor: "testmail@example.com", - DeployTime: "1", - }, - Team: "team-123", - }, - }, - Priority: api.Priority_YOLO, - }, - }, - Priority: api.Priority_YOLO, - }, - }, - Applications: map[string]*api.Application{ - "test": { - Name: "test", - Releases: []*api.Release{ - { - Version: 1, - SourceCommitId: "changedcommitId", - SourceAuthor: "changedAuthor", - SourceMessage: "changed changed something (#679)", - PrNumber: "679", - CreatedAt: ×tamppb.Timestamp{Seconds: 1, Nanos: 1}, - }, - }, - Team: "team-123", - }, - }, - GitRevision: "0", - }, - }, - - { - Name: "app does not exists", - NewRelease: DBReleaseWithMetaData{ - App: "does-not-exists", - ReleaseNumber: 12, - Metadata: DBReleaseMetaData{ - SourceAuthor: "testmail@example.com", - SourceCommitId: "testcommit", - SourceMessage: "changed something (#677)", - DisplayVersion: "12", - }, - Created: time.Date(2024, time.July, 12, 15, 30, 0, 0, time.UTC), - }, - ExpectedError: errMatcher{"could not find application does-not-exists in overview"}, - }, - { - Name: "Update overview with prepublish release", - NewRelease: DBReleaseWithMetaData{ - App: "test", - ReleaseNumber: 12, - Metadata: DBReleaseMetaData{ - SourceAuthor: "testmail@example.com", - SourceCommitId: "testcommit", - SourceMessage: "changed something (#677)", - DisplayVersion: "12", - IsPrepublish: true, - }, - Created: time.Date(2024, time.July, 12, 15, 30, 0, 0, time.UTC), - }, - ExcpectedOverview: &api.GetOverviewResponse{ - EnvironmentGroups: []*api.EnvironmentGroup{ - { - EnvironmentGroupName: "dev", - Environments: []*api.Environment{ - { - Name: "development", - Config: &api.EnvironmentConfig{ - Upstream: &api.EnvironmentConfig_Upstream{ - Latest: &upstreamLatest, - }, - Argocd: &api.EnvironmentConfig_ArgoCD{}, - EnvironmentGroup: &dev, - }, - Applications: map[string]*api.Environment_Application{ - "test": { - Name: "test", - Version: 1, - DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ - DeployAuthor: "testmail@example.com", - DeployTime: "1", - }, - Team: "team-123", - }, - }, - Priority: api.Priority_YOLO, - }, - }, - Priority: api.Priority_YOLO, - }, - }, - Applications: map[string]*api.Application{ - "test": { - Name: "test", - Warnings: []*api.Warning{ - { - WarningType: &api.Warning_UnusualDeploymentOrder{ - UnusualDeploymentOrder: &api.UnusualDeploymentOrder{ - UpstreamEnvironment: "staging", - ThisVersion: 12, - ThisEnvironment: "development", - }, - }, - }, - }, - Releases: []*api.Release{ - { - Version: 1, - SourceCommitId: "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef", - SourceAuthor: "example ", - SourceMessage: "changed something (#678)", - PrNumber: "678", - CreatedAt: ×tamppb.Timestamp{Seconds: 1, Nanos: 1}, - }, - { - Version: 12, - SourceCommitId: "testcommit", - SourceAuthor: "testmail@example.com", - SourceMessage: "changed something (#677)", - DisplayVersion: "12", - PrNumber: "677", - IsPrepublish: true, - CreatedAt: ×tamppb.Timestamp{Seconds: 1720798200, Nanos: 0}, - }, - }, - Team: "team-123", - }, - }, - GitRevision: "0", - }, - }, - } - - for _, tc := range tcs { - t.Run(tc.Name, func(t *testing.T) { - t.Parallel() - ctx := testutil.MakeTestContext() - dbHandler := setupDB(t) - - err := dbHandler.WithTransaction(ctx, false, func(ctx context.Context, transaction *sql.Tx) error { - err := dbHandler.WriteOverviewCache(ctx, transaction, startingOverview) - if err != nil { - return err - } - err = dbHandler.UpdateOverviewRelease(ctx, transaction, tc.NewRelease) - if err != nil { - if diff := cmp.Diff(tc.ExpectedError, err, cmpopts.EquateErrors()); diff != "" { - return fmt.Errorf("mismatch between errors (-want +got):\n%s", diff) - } - return nil - } - latestOverview, err := dbHandler.ReadLatestOverviewCache(ctx, transaction) - if err != nil { - return err - } - opts := getOverviewIgnoredTypes() - if diff := cmp.Diff(tc.ExcpectedOverview, latestOverview, opts); diff != "" { - return fmt.Errorf("mismatch (-want +got):\n%s", diff) - } - return nil - }) - - if err != nil { - t.Fatal(err) - } - }) - } -} - -func makeApps(apps ...*api.Environment_Application) map[string]*api.Environment_Application { - var result map[string]*api.Environment_Application = map[string]*api.Environment_Application{} - for i := 0; i < len(apps); i++ { - app := apps[i] - result[app.Name] = app - } - return result -} - -func makeEnv(envName string, groupName string, upstream *api.EnvironmentConfig_Upstream, apps map[string]*api.Environment_Application) *api.Environment { - return &api.Environment{ - Name: envName, - Config: &api.EnvironmentConfig{ - Upstream: upstream, - EnvironmentGroup: &groupName, - }, - Locks: map[string]*api.Lock{}, - - Applications: apps, - DistanceToUpstream: 0, - Priority: api.Priority_UPSTREAM, // we are 1 away from prod, hence pre-prod - } -} - -func makeApp(appName string, version uint64) *api.Environment_Application { - return &api.Environment_Application{ - Name: appName, - Version: version, - Locks: nil, - QueuedVersion: 0, - UndeployVersion: false, - ArgoCd: nil, - } -} -func makeEnvGroup(envGroupName string, environments []*api.Environment) *api.EnvironmentGroup { - return &api.EnvironmentGroup{ - EnvironmentGroupName: envGroupName, - Environments: environments, - DistanceToUpstream: 0, - } -} - -func makeUpstreamLatest() *api.EnvironmentConfig_Upstream { - f := true - return &api.EnvironmentConfig_Upstream{ - Latest: &f, - } -} - -func makeUpstreamEnv(upstream string) *api.EnvironmentConfig_Upstream { - return &api.EnvironmentConfig_Upstream{ - Environment: &upstream, - } -} - -func TestCalculateWarnings(t *testing.T) { - var dev = "dev" - tcs := []struct { - Name string - AppName string - Groups []*api.EnvironmentGroup - ExpectedWarnings []*api.Warning - }{ - { - Name: "no envs - no warning", - AppName: "foo", - Groups: []*api.EnvironmentGroup{ - makeEnvGroup(dev, []*api.Environment{ - makeEnv("dev-de", dev, makeUpstreamLatest(), nil), - })}, - ExpectedWarnings: []*api.Warning{}, - }, - { - Name: "app deployed in higher version on upstream should warn", - AppName: "foo", - Groups: []*api.EnvironmentGroup{ - makeEnvGroup(dev, []*api.Environment{ - makeEnv("prod", dev, makeUpstreamEnv("dev"), - makeApps(makeApp("foo", 2))), - }), - makeEnvGroup(dev, []*api.Environment{ - makeEnv("dev", dev, makeUpstreamLatest(), - makeApps(makeApp("foo", 1))), - }), - }, - ExpectedWarnings: []*api.Warning{ - { - WarningType: &api.Warning_UnusualDeploymentOrder{ - UnusualDeploymentOrder: &api.UnusualDeploymentOrder{ - UpstreamVersion: 1, - UpstreamEnvironment: "dev", - ThisVersion: 2, - ThisEnvironment: "prod", - }, - }, - }, - }, - }, - { - Name: "app deployed in same version on upstream should not warn", - AppName: "foo", - Groups: []*api.EnvironmentGroup{ - makeEnvGroup(dev, []*api.Environment{ - makeEnv("prod", dev, makeUpstreamEnv("dev"), - makeApps(makeApp("foo", 2))), - }), - makeEnvGroup(dev, []*api.Environment{ - makeEnv("dev", dev, makeUpstreamLatest(), - makeApps(makeApp("foo", 2))), - }), - }, - ExpectedWarnings: []*api.Warning{}, - }, - { - Name: "app deployed in no version on upstream should warn", - AppName: "foo", - Groups: []*api.EnvironmentGroup{ - makeEnvGroup(dev, []*api.Environment{ - makeEnv("prod", dev, makeUpstreamEnv("dev"), - makeApps(makeApp("foo", 1))), - }), - makeEnvGroup(dev, []*api.Environment{ - makeEnv("dev", dev, makeUpstreamLatest(), - makeApps()), - }), - }, - ExpectedWarnings: []*api.Warning{ - { - WarningType: &api.Warning_UpstreamNotDeployed{ - UpstreamNotDeployed: &api.UpstreamNotDeployed{ - UpstreamEnvironment: "dev", - ThisVersion: 1, - ThisEnvironment: "prod", - }, - }, - }, - }, - }, - } - for _, tc := range tcs { - tc := tc - t.Run(tc.Name, func(t *testing.T) { - actualWarnings := CalculateWarnings(testutil.MakeTestContext(), tc.AppName, tc.Groups) - if len(actualWarnings) != len(tc.ExpectedWarnings) { - t.Errorf("Different number of warnings. got: %s\nwant: %s", actualWarnings, tc.ExpectedWarnings) - } - for i := 0; i < len(actualWarnings); i++ { - actualWarning := actualWarnings[i] - expectedWarning := tc.ExpectedWarnings[i] - if diff := cmp.Diff(actualWarning.String(), expectedWarning.String()); diff != "" { - t.Errorf("Different warning at index [%d]:\ngot: %s\nwant: %s", i, actualWarning, expectedWarning) - } - } - }) - } -} - -func groupFromEnvs(environments []*api.Environment) []*api.EnvironmentGroup { - return []*api.EnvironmentGroup{ - { - EnvironmentGroupName: "group1", - Environments: environments, - }, - } -} - -func TestDeriveUndeploySummary(t *testing.T) { - var tcs = []struct { - Name string - AppName string - groups []*api.EnvironmentGroup - ExpectedResult api.UndeploySummary - }{ - { - Name: "No Environments", - AppName: "foo", - groups: []*api.EnvironmentGroup{}, - ExpectedResult: api.UndeploySummary_UNDEPLOY, - }, - { - Name: "one Environment but no Application", - AppName: "foo", - groups: groupFromEnvs([]*api.Environment{ - { - Applications: map[string]*api.Environment_Application{ - "bar": { // different app - UndeployVersion: true, - Version: 666, - }, - }, - }, - }), - ExpectedResult: api.UndeploySummary_UNDEPLOY, - }, - { - Name: "One Env with undeploy", - AppName: "foo", - groups: groupFromEnvs([]*api.Environment{ - { - Applications: map[string]*api.Environment_Application{ - "foo": { - UndeployVersion: true, - Version: 666, - }, - }, - }, - }), - ExpectedResult: api.UndeploySummary_UNDEPLOY, - }, - { - Name: "One Env with normal version", - AppName: "foo", - groups: groupFromEnvs([]*api.Environment{ - { - Applications: map[string]*api.Environment_Application{ - "foo": { - UndeployVersion: false, - Version: 666, - }, - }, - }, - }), - ExpectedResult: api.UndeploySummary_NORMAL, - }, - { - Name: "Two Envs all undeploy", - AppName: "foo", - groups: groupFromEnvs([]*api.Environment{ - { - Applications: map[string]*api.Environment_Application{ - "foo": { - UndeployVersion: true, - Version: 666, - }, - }, - }, - { - Applications: map[string]*api.Environment_Application{ - "foo": { - UndeployVersion: true, - Version: 666, - }, - }, - }, - }), - ExpectedResult: api.UndeploySummary_UNDEPLOY, - }, - { - Name: "Two Envs all normal", - AppName: "foo", - groups: groupFromEnvs([]*api.Environment{ - { - Applications: map[string]*api.Environment_Application{ - "foo": { - UndeployVersion: false, - Version: 666, - }, - }, - }, - { - Applications: map[string]*api.Environment_Application{ - "foo": { - UndeployVersion: false, - Version: 666, - }, - }, - }, - }), - ExpectedResult: api.UndeploySummary_NORMAL, - }, - { - Name: "Two Envs all different", - AppName: "foo", - groups: groupFromEnvs([]*api.Environment{ - { - Applications: map[string]*api.Environment_Application{ - "foo": { - UndeployVersion: true, - Version: 666, - }, - }, - }, - { - Applications: map[string]*api.Environment_Application{ - "foo": { - UndeployVersion: false, - Version: 666, - }, - }, - }, - }), - ExpectedResult: api.UndeploySummary_MIXED, - }, - } - for _, tc := range tcs { - t.Run(tc.Name, func(t *testing.T) { - actualResult := deriveUndeploySummary(tc.AppName, tc.groups) - if !cmp.Equal(tc.ExpectedResult, actualResult) { - t.Fatal("Output mismatch (-want +got):\n", cmp.Diff(tc.ExpectedResult, actualResult)) - } - }) - } -} - -func TestDBDeleteOldOverview(t *testing.T) { - upstreamLatest := true - dev := "dev" - var tcs = []struct { - Name string - inputOverviews []*api.GetOverviewResponse - timeThresholdDiff time.Duration - numberOfOverviewsToKeep uint64 - expectedNumberOfRemainingOverviews uint64 - }{ - { - Name: "4 overviews, should keep two", - inputOverviews: []*api.GetOverviewResponse{ - &api.GetOverviewResponse{}, - &api.GetOverviewResponse{}, - &api.GetOverviewResponse{}, - &api.GetOverviewResponse{ - EnvironmentGroups: []*api.EnvironmentGroup{ - { - EnvironmentGroupName: "dev", - Environments: []*api.Environment{ - { - Name: "development", - Config: &api.EnvironmentConfig{ - Upstream: &api.EnvironmentConfig_Upstream{ - Latest: &upstreamLatest, - }, - Argocd: &api.EnvironmentConfig_ArgoCD{}, - EnvironmentGroup: &dev, - }, - Applications: map[string]*api.Environment_Application{ - "test": { - Name: "test", - Version: 1, - DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ - DeployAuthor: "testmail@example.com", - DeployTime: "1", - }, - Team: "team-123", - }, - }, - Priority: api.Priority_YOLO, - }, - }, - Priority: api.Priority_YOLO, - }, - }, - Applications: map[string]*api.Application{ - "test": { - Name: "test", - Releases: []*api.Release{ - { - Version: 1, - SourceCommitId: "changedcommitId", - SourceAuthor: "changedAuthor", - SourceMessage: "changed changed something (#679)", - PrNumber: "679", - CreatedAt: ×tamppb.Timestamp{Seconds: 1, Nanos: 1}, - }, - }, - Team: "team-123", - }, - }, - GitRevision: "0", - }, - }, - timeThresholdDiff: 150 * time.Second, - numberOfOverviewsToKeep: 2, - expectedNumberOfRemainingOverviews: 2, - }, - { - Name: "4 overviews, early time threshhold, all should remain", - inputOverviews: []*api.GetOverviewResponse{ - &api.GetOverviewResponse{}, - &api.GetOverviewResponse{}, - &api.GetOverviewResponse{}, - &api.GetOverviewResponse{ - EnvironmentGroups: []*api.EnvironmentGroup{ + //Applications: map[string]*api.Application{ + // "test": { + // Name: "test", + // Releases: []*api.Release{ + // { + // Version: 1, + // SourceCommitId: "changedcommitId", + // SourceAuthor: "changedAuthor", + // SourceMessage: "changed changed something (#679)", + // PrNumber: "679", + // CreatedAt: ×tamppb.Timestamp{Seconds: 1, Nanos: 1}, + // }, + // }, + // Team: "team-123", + // }, + //}, + LightweightApps: []*api.OverviewApplication{ { - EnvironmentGroupName: "dev", - Environments: []*api.Environment{ - { - Name: "development", - Config: &api.EnvironmentConfig{ - Upstream: &api.EnvironmentConfig_Upstream{ - Latest: &upstreamLatest, - }, - Argocd: &api.EnvironmentConfig_ArgoCD{}, - EnvironmentGroup: &dev, - }, - Applications: map[string]*api.Environment_Application{ - "test": { - Name: "test", - Version: 1, - DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ - DeployAuthor: "testmail@example.com", - DeployTime: "1", - }, - Team: "team-123", - }, - }, - Priority: api.Priority_YOLO, - }, - }, - Priority: api.Priority_YOLO, - }, - }, - Applications: map[string]*api.Application{ - "test": { Name: "test", - Releases: []*api.Release{ - { - Version: 1, - SourceCommitId: "changedcommitId", - SourceAuthor: "changedAuthor", - SourceMessage: "changed changed something (#679)", - PrNumber: "679", - CreatedAt: ×tamppb.Timestamp{Seconds: 1, Nanos: 1}, - }, - }, Team: "team-123", }, }, diff --git a/pkg/mapper/environments_config.go b/pkg/mapper/environments_config.go index 328210f3c..c6618e63a 100644 --- a/pkg/mapper/environments_config.go +++ b/pkg/mapper/environments_config.go @@ -17,8 +17,6 @@ Copyright freiheit.com*/ package mapper import ( - "fmt" - "path/filepath" "sort" api "github.com/freiheit-com/kuberpult/pkg/api/v1" @@ -53,8 +51,9 @@ func MapEnvironmentsToGroups(envs map[string]config.EnvironmentConfig) []*api.En Upstream: TransformUpstream(env.Upstream), EnvironmentGroup: &groupNameCopy, }, - Locks: map[string]*api.Lock{}, - Applications: map[string]*api.Environment_Application{}, + Locks: map[string]*api.Lock{}, + AppLocks: map[string]*api.Locks{}, + TeamLocks: map[string]*api.Locks{}, } bucket.Environments = append(bucket.Environments, newEnv) } @@ -320,24 +319,6 @@ func TransformUpstream(upstream *config.EnvironmentConfigUpstream) *api.Environm return nil } -func TransformSyncWindows(syncWindows []config.ArgoCdSyncWindow, appName string) ([]*api.Environment_Application_ArgoCD_SyncWindow, error) { - var envAppSyncWindows []*api.Environment_Application_ArgoCD_SyncWindow - for _, syncWindow := range syncWindows { - for _, pattern := range syncWindow.Apps { - if match, err := filepath.Match(pattern, appName); err != nil { - return nil, fmt.Errorf("failed to match app pattern %s of sync window to %s at %s with duration %s: %w", pattern, syncWindow.Kind, syncWindow.Schedule, syncWindow.Duration, err) - } else if match { - envAppSyncWindows = append(envAppSyncWindows, &api.Environment_Application_ArgoCD_SyncWindow{ - Kind: syncWindow.Kind, - Schedule: syncWindow.Schedule, - Duration: syncWindow.Duration, - }) - } - } - } - return envAppSyncWindows, nil -} - func TransformArgocd(config config.EnvironmentConfigArgoCd) *api.EnvironmentConfig_ArgoCD { var syncWindows []*api.EnvironmentConfig_ArgoCD_SyncWindows var accessList []*api.EnvironmentConfig_ArgoCD_AccessEntry diff --git a/pkg/mapper/environments_config_test.go b/pkg/mapper/environments_config_test.go index ea4066369..2bfeaca28 100644 --- a/pkg/mapper/environments_config_test.go +++ b/pkg/mapper/environments_config_test.go @@ -73,7 +73,8 @@ func makeEnv(envName string, groupName string, upstream *api.EnvironmentConfig_U EnvironmentGroup: &groupName, }, Locks: map[string]*api.Lock{}, - Applications: map[string]*api.Environment_Application{}, + AppLocks: make(map[string]*api.Locks), + TeamLocks: make(map[string]*api.Locks), DistanceToUpstream: distanceToUpstream, Priority: priority, // we are 1 away from prod, hence pre-prod } diff --git a/services/cd-service/pkg/repository/repository.go b/services/cd-service/pkg/repository/repository.go index b1f992fdc..6e5bd359c 100644 --- a/services/cd-service/pkg/repository/repository.go +++ b/services/cd-service/pkg/repository/repository.go @@ -2445,29 +2445,8 @@ func (s *State) DBInsertApplicationWithOverview(ctx context.Context, transaction return err } - for envGroupIndex := range cache.EnvironmentGroups { - envGroup := cache.EnvironmentGroups[envGroupIndex] - - for i := range envGroup.Environments { - env := envGroup.Environments[i] - - if shouldDelete { - delete(env.Applications, appName) - } else { - envApp, err := s.UpdateOneAppEnvInOverview(ctx, transaction, appName, env.Name, nil, map[string]*int64{}) - if err != nil { - return err - } - - if env.Applications == nil { - env.Applications = map[string]*api.Environment_Application{} - } - env.Applications[appName] = envApp - } - } - } if shouldDelete { - lApps := make([]*api.OverviewApplication, len(cache.LightweightApps)-1) + lApps := make([]*api.OverviewApplication, max(len(cache.LightweightApps)-1, 0)) for _, curr := range cache.LightweightApps { if curr.Name != appName { @@ -2516,68 +2495,26 @@ func (s *State) DBInsertEnvironmentWithOverview(ctx context.Context, tx *sql.Tx, func (s *State) UpdateTopLevelAppInOverview(ctx context.Context, transaction *sql.Tx, appName string, result *api.GetOverviewResponse, deleteApp bool, allReleasesOfAllApps map[string][]int64) error { if deleteApp { - delete(result.Applications, appName) - return nil - } - app := api.Application{ - UndeploySummary: 0, - Warnings: nil, - Name: appName, - Releases: []*api.Release{}, - SourceRepoUrl: "", - Team: "", - } - allReleasesOfApp, found := allReleasesOfAllApps[appName] - var rels []uint64 - if found { - rels = conversion.ToUint64Slice(allReleasesOfApp) - } else { - retrievedReleasesOfApp, err := s.GetAllApplicationReleases(ctx, transaction, appName) - if err != nil { - logger.FromContext(ctx).Sugar().Warnf("app without releases: %v", err) - } - rels = retrievedReleasesOfApp - } - if releasesInDb, err := s.GetApplicationReleasesDB(ctx, transaction, appName, rels); err != nil { - return err - } else { - for _, rel := range releasesInDb { - if rel == nil { - // ignore - } else { - release := rel.ToProto() - app.Releases = append(app.Releases, release) - } - } - } - - if team, err := s.GetApplicationTeamOwner(ctx, transaction, appName); err != nil { - return err - } else { - app.Team = team - } - if result == nil { + removeOverviewAppFromLightweightApps(result, appName) return nil } - app.UndeploySummary = deriveUndeploySummary(appName, result.EnvironmentGroups) - app.Warnings = CalculateWarnings(ctx, app.Name, result.EnvironmentGroups) - if result.Applications == nil { - result.Applications = map[string]*api.Application{} + team, err := s.GetApplicationTeamOwner(ctx, transaction, appName) + if err != nil { + return fmt.Errorf("could not obtain application team owner to update top level app in overview: %w", err) } - result.Applications[appName] = &app - result.LightweightApps = append(result.LightweightApps, &api.OverviewApplication{Name: appName, Team: app.Team}) + result.LightweightApps = append(result.LightweightApps, &api.OverviewApplication{Name: appName, Team: team}) return nil } -func getEnvironmentByName(groups []*api.EnvironmentGroup, envNameToReturn string) *api.Environment { - for _, currentGroup := range groups { - for _, currentEnv := range currentGroup.Environments { - if currentEnv.Name == envNameToReturn { - return currentEnv - } +func removeOverviewAppFromLightweightApps(result *api.GetOverviewResponse, appName string) { + lApps := make([]*api.OverviewApplication, max(len(result.LightweightApps)-1, 0)) + + for _, curr := range result.LightweightApps { + if curr.Name != appName { + lApps = append(lApps, curr) } } - return nil + result.LightweightApps = lApps } func getEnvironmentInGroup(groups []*api.EnvironmentGroup, groupNameToReturn string, envNameToReturn string) *api.Environment { @@ -2593,215 +2530,6 @@ func getEnvironmentInGroup(groups []*api.EnvironmentGroup, groupNameToReturn str return nil } -/* -CalculateWarnings returns warnings for the User to be displayed in the UI. -For really unusual configurations, these will be logged and not returned. -*/ -func CalculateWarnings(_ context.Context, appName string, groups []*api.EnvironmentGroup) []*api.Warning { - result := make([]*api.Warning, 0) - for e := 0; e < len(groups); e++ { - group := groups[e] - for i := 0; i < len(groups[e].Environments); i++ { - env := group.Environments[i] - if env == nil || env.Config == nil || env.Config.Upstream == nil || env.Config.Upstream.Environment == nil { - // if the env has no upstream, there's nothing to warn about - continue - } - upstreamEnvName := env.Config.GetUpstream().Environment - upstreamEnv := getEnvironmentByName(groups, *upstreamEnvName) - if upstreamEnv == nil { - // this is already checked on startup and therefore shouldn't happen here - continue - } - - appInEnv := env.Applications[appName] - if appInEnv == nil { - // appName is not deployed here, ignore it - continue - } - versionInEnv := appInEnv.Version - appInUpstreamEnv := upstreamEnv.Applications[appName] - if appInUpstreamEnv == nil { - // appName is not deployed upstream... that's unusual! - var warning = api.Warning{ - WarningType: &api.Warning_UpstreamNotDeployed{ - UpstreamNotDeployed: &api.UpstreamNotDeployed{ - UpstreamEnvironment: *upstreamEnvName, - ThisVersion: versionInEnv, - ThisEnvironment: env.Name, - }, - }, - } - result = append(result, &warning) - continue - } - versionInUpstreamEnv := appInUpstreamEnv.Version - - if versionInEnv > versionInUpstreamEnv && len(appInEnv.Locks) == 0 { - var warning = api.Warning{ - WarningType: &api.Warning_UnusualDeploymentOrder{ - UnusualDeploymentOrder: &api.UnusualDeploymentOrder{ - UpstreamVersion: versionInUpstreamEnv, - UpstreamEnvironment: *upstreamEnvName, - ThisVersion: versionInEnv, - ThisEnvironment: env.Name, - }, - }, - } - result = append(result, &warning) - } - } - } - return result -} - -func deriveUndeploySummary(appName string, groups []*api.EnvironmentGroup) api.UndeploySummary { - var allNormal = true - var allUndeploy = true - for _, group := range groups { - for _, environment := range group.Environments { - var app, exists = environment.Applications[appName] - if !exists { - continue - } - if app.Version == 0 { - // if the app exists but nothing is deployed, we ignore this - continue - } - if app.UndeployVersion { - allNormal = false - } else { - allUndeploy = false - } - } - } - if allUndeploy { - return api.UndeploySummary_UNDEPLOY - } - if allNormal { - return api.UndeploySummary_NORMAL - } - return api.UndeploySummary_MIXED - -} - -func (s *State) UpdateOneAppEnvInOverview(ctx context.Context, transaction *sql.Tx, appName string, envName string, configParam *config.EnvironmentConfig, allEnvironmentApplicationVersions map[string]*int64) (*api.Environment_Application, error) { - var envConfig = configParam - if envConfig == nil { - var err error - envConfig, err = s.GetEnvironmentConfig(ctx, transaction, envName) - if err != nil { - return nil, fmt.Errorf("could not get environment to update app %s in env %s: %w", appName, envName, err) - } - } - - app := api.Environment_Application{ - Version: 0, - QueuedVersion: 0, - UndeployVersion: false, - ArgoCd: nil, - Name: appName, - Locks: map[string]*api.Lock{}, - TeamLocks: map[string]*api.Lock{}, - Team: "", - DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ - DeployAuthor: "", - DeployTime: "", - }, - } - teamName, err := s.GetTeamName(ctx, transaction, appName) - if err == nil { - app.Team = teamName - if teamLocks, teamErr := s.GetEnvironmentTeamLocks(ctx, transaction, envName, teamName); teamErr != nil { - return nil, teamErr - } else { - for lockId, lock := range teamLocks { - app.TeamLocks[lockId] = &api.Lock{ - Message: lock.Message, - LockId: lockId, - CreatedAt: timestamppb.New(lock.CreatedAt), - CreatedBy: &api.Actor{ - Name: lock.CreatedBy.Name, - Email: lock.CreatedBy.Email, - }, - } - } - } - } // Err != nil means no team name was found so no need to parse team locks - - var version *uint64 = new(uint64) - allAppsVersion, found := allEnvironmentApplicationVersions[appName] - if found { - *version = uint64(*allAppsVersion) - } else { - version, err = s.GetEnvironmentApplicationVersion(ctx, transaction, envName, appName) - } - if err != nil && !errors.Is(err, os.ErrNotExist) { - return nil, err - } else { - - if version == nil { - app.Version = 0 - } else { - app.Version = *version - } - } - - if queuedVersion, err := s.GetQueuedVersion(ctx, transaction, envName, appName); err != nil && !errors.Is(err, os.ErrNotExist) { - return nil, err - } else { - if queuedVersion == nil { - app.QueuedVersion = 0 - } else { - app.QueuedVersion = *queuedVersion - } - } - app.UndeployVersion = false - if version != nil { - if release, err := s.GetApplicationRelease(ctx, transaction, appName, *version); err != nil && !errors.Is(err, os.ErrNotExist) { - return nil, err - } else if release != nil { - app.UndeployVersion = release.UndeployVersion - } - } - - if appLocks, err := s.GetEnvironmentApplicationLocks(ctx, transaction, envName, appName); err != nil { - return nil, err - } else { - for lockId, lock := range appLocks { - app.Locks[lockId] = &api.Lock{ - Message: lock.Message, - LockId: lockId, - CreatedAt: timestamppb.New(lock.CreatedAt), - CreatedBy: &api.Actor{ - Name: lock.CreatedBy.Name, - Email: lock.CreatedBy.Email, - }, - } - } - } - if envConfig != nil && envConfig.ArgoCd != nil { - if syncWindows, err := mapper.TransformSyncWindows(envConfig.ArgoCd.SyncWindows, appName); err != nil { - return nil, err - } else { - app.ArgoCd = &api.Environment_Application_ArgoCD{ - SyncWindows: syncWindows, - } - } - } - deployAuthor, deployTime, err := s.GetDeploymentMetaData(ctx, transaction, envName, appName) - if err != nil { - return nil, err - } - app.DeploymentMetaData.DeployAuthor = deployAuthor - if deployTime.IsZero() { - app.DeploymentMetaData.DeployTime = "" - } else { - app.DeploymentMetaData.DeployTime = fmt.Sprintf("%d", deployTime.Unix()) - } - return &app, nil -} - func (s *State) UpdateEnvironmentsInOverview(ctx context.Context, transaction *sql.Tx, result *api.GetOverviewResponse) error { if envs, err := s.GetAllEnvironmentConfigs(ctx, transaction); err != nil { return err @@ -2837,8 +2565,9 @@ func (s *State) UpdateEnvironmentsInOverview(ctx context.Context, transaction *s Argocd: argocd, EnvironmentGroup: &groupName, }, - Locks: map[string]*api.Lock{}, - Applications: map[string]*api.Environment_Application{}, + Locks: map[string]*api.Lock{}, + AppLocks: make(map[string]*api.Locks), + TeamLocks: make(map[string]*api.Locks), } envInGroup.Config = env.Config if locks, err := s.GetEnvironmentLocks(ctx, transaction, envName); err != nil { @@ -2862,16 +2591,61 @@ func (s *State) UpdateEnvironmentsInOverview(ctx context.Context, transaction *s return err } else { for _, appName := range apps { - app, err2 := s.UpdateOneAppEnvInOverview(ctx, transaction, appName, envName, &config, map[string]*int64{}) - if err2 != nil { + + if appLocks, err := s.GetEnvironmentApplicationLocks(ctx, transaction, envName, appName); err != nil { + return err + } else { + apiAppLocks := api.Locks{ + Locks: make([]*api.Lock, 0), + } + for lockId, lock := range appLocks { + apiAppLocks.Locks = append(apiAppLocks.Locks, &api.Lock{ + Message: lock.Message, + LockId: lockId, + CreatedAt: timestamppb.New(lock.CreatedAt), + CreatedBy: &api.Actor{ + Name: lock.CreatedBy.Name, + Email: lock.CreatedBy.Email, + }, + }) + + } + if len(apiAppLocks.Locks) > 0 { + env.AppLocks[appName] = &apiAppLocks + } + } + team, err := s.GetApplicationTeamOwner(ctx, transaction, appName) + if err != nil { + return fmt.Errorf("error obtaining team information for app '%s': %w\n", appName, err) + } + if teamLocks, err := s.GetEnvironmentTeamLocks(ctx, transaction, envName, team); err != nil { return err + } else { + apiTeamLocks := api.Locks{ + Locks: make([]*api.Lock, 0), + } + for lockId, lock := range teamLocks { + apiTeamLocks.Locks = append(apiTeamLocks.Locks, &api.Lock{ + Message: lock.Message, + LockId: lockId, + CreatedAt: timestamppb.New(lock.CreatedAt), + CreatedBy: &api.Actor{ + Name: lock.CreatedBy.Name, + Email: lock.CreatedBy.Email, + }, + }) + } + if len(apiTeamLocks.Locks) > 0 { + env.TeamLocks[appName] = &apiTeamLocks + } } - env.Applications[appName] = app } } - envInGroup.Applications = env.Applications + envInGroup.TeamLocks = env.TeamLocks + envInGroup.AppLocks = env.AppLocks } } + return nil } diff --git a/services/cd-service/pkg/repository/repository_test.go b/services/cd-service/pkg/repository/repository_test.go index 29c74fc0f..512730581 100644 --- a/services/cd-service/pkg/repository/repository_test.go +++ b/services/cd-service/pkg/repository/repository_test.go @@ -2178,22 +2178,13 @@ func TestUpdateOverviewCache(t *testing.T) { StateChange: db.AppStateChangeCreate, InitialCache: nil, ExpectedOverview: &api.GetOverviewResponse{ - Applications: map[string]*api.Application{ - "app1": { - Name: "app1", - Releases: nil, - SourceRepoUrl: "", - Team: "", - UndeploySummary: api.UndeploySummary_UNDEPLOY, - Warnings: nil, - }, - }, LightweightApps: []*api.OverviewApplication{ { Name: "app1", Team: "", }, }, + EnvironmentGroups: []*api.EnvironmentGroup{}, }, }, @@ -2201,7 +2192,6 @@ func TestUpdateOverviewCache(t *testing.T) { Name: "overview creates new app", StateChange: db.AppStateChangeCreate, InitialCache: &api.GetOverviewResponse{ - Applications: nil, EnvironmentGroups: []*api.EnvironmentGroup{ { EnvironmentGroupName: "dev", @@ -2210,7 +2200,6 @@ func TestUpdateOverviewCache(t *testing.T) { Name: "dev", Config: nil, Locks: nil, - Applications: nil, DistanceToUpstream: 0, Priority: 0, }, @@ -2224,16 +2213,6 @@ func TestUpdateOverviewCache(t *testing.T) { ManifestRepoUrl: "https://example.com", }, ExpectedOverview: &api.GetOverviewResponse{ - Applications: map[string]*api.Application{ - "app1": { - Name: "app1", - Releases: nil, - SourceRepoUrl: "", - Team: "", - UndeploySummary: api.UndeploySummary_UNDEPLOY, - Warnings: nil, - }, - }, LightweightApps: []*api.OverviewApplication{ { Name: "app1", @@ -2248,22 +2227,22 @@ func TestUpdateOverviewCache(t *testing.T) { Name: "dev", Config: nil, Locks: nil, - Applications: map[string]*api.Environment_Application{ - "app1": { - Name: "app1", - Version: 0, - Locks: nil, - QueuedVersion: 0, - UndeployVersion: false, - ArgoCd: nil, - DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ - DeployAuthor: "", - DeployTime: "", - }, - TeamLocks: nil, - Team: "", - }, - }, + //Applications: map[string]*api.Environment_Application{ + // "app1": { + // Name: "app1", + // Version: 0, + // Locks: nil, + // QueuedVersion: 0, + // UndeployVersion: false, + // ArgoCd: nil, + // DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ + // DeployAuthor: "", + // DeployTime: "", + // }, + // TeamLocks: nil, + // Team: "", + // }, + //}, DistanceToUpstream: 0, Priority: 0, }, @@ -2281,16 +2260,6 @@ func TestUpdateOverviewCache(t *testing.T) { Name: "overview deletes an app", StateChange: db.AppStateChangeDelete, InitialCache: &api.GetOverviewResponse{ - Applications: map[string]*api.Application{ - "app1": { - Name: "app1", - Releases: nil, - SourceRepoUrl: "", - Team: "", - UndeploySummary: api.UndeploySummary_UNDEPLOY, - Warnings: nil, - }, - }, EnvironmentGroups: []*api.EnvironmentGroup{ { EnvironmentGroupName: "dev", @@ -2299,22 +2268,22 @@ func TestUpdateOverviewCache(t *testing.T) { Name: "dev", Config: nil, Locks: nil, - Applications: map[string]*api.Environment_Application{ - "app1": { - Name: "app1", - Version: 0, - Locks: nil, - QueuedVersion: 0, - UndeployVersion: false, - ArgoCd: nil, - DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ - DeployAuthor: "", - DeployTime: "", - }, - TeamLocks: nil, - Team: "", - }, - }, + //Applications: map[string]*api.Environment_Application{ + // "app1": { + // Name: "app1", + // Version: 0, + // Locks: nil, + // QueuedVersion: 0, + // UndeployVersion: false, + // ArgoCd: nil, + // DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ + // DeployAuthor: "", + // DeployTime: "", + // }, + // TeamLocks: nil, + // Team: "", + // }, + //}, DistanceToUpstream: 0, Priority: 0, }, @@ -2334,7 +2303,6 @@ func TestUpdateOverviewCache(t *testing.T) { ManifestRepoUrl: "https://example.com", }, ExpectedOverview: &api.GetOverviewResponse{ - Applications: nil, EnvironmentGroups: []*api.EnvironmentGroup{ { EnvironmentGroupName: "dev", @@ -2343,7 +2311,6 @@ func TestUpdateOverviewCache(t *testing.T) { Name: "dev", Config: nil, Locks: nil, - Applications: nil, DistanceToUpstream: 0, Priority: 0, }, diff --git a/services/cd-service/pkg/repository/transformer.go b/services/cd-service/pkg/repository/transformer.go index 0651a83fb..c42bde80b 100644 --- a/services/cd-service/pkg/repository/transformer.go +++ b/services/cd-service/pkg/repository/transformer.go @@ -2776,7 +2776,6 @@ func (c *CreateEnvironment) Transform( overview = &api.GetOverviewResponse{ Branch: "", ManifestRepoUrl: "", - Applications: map[string]*api.Application{}, EnvironmentGroups: []*api.EnvironmentGroup{}, GitRevision: "0000000000000000000000000000000000000000", LightweightApps: make([]*api.OverviewApplication, 0), @@ -3374,17 +3373,6 @@ func getOverrideVersions(ctx context.Context, transaction *sql.Tx, commitHash, u return nil, fmt.Errorf("unable to get EnvironmentApplication for env %s: %w", envName, err) } for _, appName := range apps { - app := api.Environment_Application{ - Version: 0, - Locks: nil, - QueuedVersion: 0, - UndeployVersion: false, - ArgoCd: nil, - DeploymentMetaData: nil, - Name: appName, - TeamLocks: nil, - Team: "", - } version, err := s.GetEnvironmentApplicationVersion(ctx, transaction, envName, appName) if err != nil && !errors.Is(err, os.ErrNotExist) { return nil, fmt.Errorf("unable to get EnvironmentApplicationVersion for %s: %w", appName, err) @@ -3392,8 +3380,8 @@ func getOverrideVersions(ctx context.Context, transaction *sql.Tx, commitHash, u if version == nil { continue } - app.Version = *version - resp = append(resp, Overview{App: app.Name, Version: app.Version}) + + resp = append(resp, Overview{App: appName, Version: *version}) } } return resp, nil @@ -4170,10 +4158,7 @@ func (c *envReleaseTrain) Transform( deployCounter++ } span.SetTag("DeployedApps", deployCounter) - allEnvironmentApplicationVersions, err := state.GetAllLatestDeployments(ctx, transaction, c.Env, appNames) - if err != nil { - return "", grpc.InternalError(ctx, fmt.Errorf("unexpected error while retrieving all environment application versions: %w", err)) - } + allReleasesOfAllApps, err := state.GetAllLatestReleases(ctx, transaction, appNames) if err != nil { return "", grpc.InternalError(ctx, fmt.Errorf("unexpected error while retrieving all releases of all apps: %w", err)) @@ -4184,11 +4169,6 @@ func (c *envReleaseTrain) Transform( if err != nil { return "", grpc.InternalError(ctx, fmt.Errorf("unexpected error while updating top level app %q to env %q: %w", appName, c.Env, err)) } - envApp, err := state.UpdateOneAppEnvInOverview(ctx, transaction, appName, c.Env, &envConfig, allEnvironmentApplicationVersions) - if err != nil { - return "", grpc.InternalError(ctx, fmt.Errorf("unexpected error while updating top level app %q to env %q: %w", appName, c.Env, err)) - } - envOfOverview.Applications[appName] = envApp } } teamInfo := "" diff --git a/services/cd-service/pkg/repository/transformer_db_test.go b/services/cd-service/pkg/repository/transformer_db_test.go index 305b19c3e..ae931f01f 100644 --- a/services/cd-service/pkg/repository/transformer_db_test.go +++ b/services/cd-service/pkg/repository/transformer_db_test.go @@ -36,11 +36,9 @@ import ( "github.com/freiheit-com/kuberpult/pkg/db" "github.com/freiheit-com/kuberpult/pkg/testutil" "github.com/freiheit-com/kuberpult/pkg/time" + "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "google.golang.org/protobuf/testing/protocmp" - "google.golang.org/protobuf/types/known/timestamppb" - - "github.com/google/go-cmp/cmp" ) var ( @@ -1723,18 +1721,6 @@ func TestCreateEnvironmentUpdatesOverview(t *testing.T) { }, ExpectedOverviewCache: &api.GetOverviewResponse{ GitRevision: "0000000000000000000000000000000000000000", - Applications: map[string]*api.Application{ - "app": &api.Application{ - Name: "app", - Releases: []*api.Release{ - { - Version: 1, - SourceCommitId: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - CreatedAt: timestamppb.Now(), - }, - }, - }, - }, LightweightApps: []*api.OverviewApplication{ { Name: "app", @@ -1756,16 +1742,6 @@ func TestCreateEnvironmentUpdatesOverview(t *testing.T) { }, EnvironmentGroup: &developmentEnvGroup, }, - Applications: map[string]*api.Environment_Application{ - "app": &api.Environment_Application{ - Name: "app", - DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ - DeployAuthor: "testmail@example.com", - DeployTime: timestamppb.Now().String(), - }, - Version: uint64(1), - }, - }, Priority: api.Priority_YOLO, }, }, @@ -1786,12 +1762,6 @@ func TestCreateEnvironmentUpdatesOverview(t *testing.T) { EnvironmentGroup: &stagingEnvGroup, }, Priority: api.Priority_YOLO, - Applications: map[string]*api.Environment_Application{ - "app": &api.Environment_Application{ - Name: "app", - DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{}, - }, - }, }, }, Priority: api.Priority_YOLO, @@ -1817,7 +1787,7 @@ func TestCreateEnvironmentUpdatesOverview(t *testing.T) { if err != nil { return err } - if diff := cmp.Diff(tc.ExpectedOverviewCache, overview, protocmp.Transform(), protocmp.IgnoreFields(&api.Release{}, "created_at"), protocmp.IgnoreFields(&api.Environment_Application_DeploymentMetaData{}, "deploy_time")); diff != "" { + if diff := cmp.Diff(tc.ExpectedOverviewCache, overview, protocmp.Transform(), protocmp.IgnoreFields(&api.Release{}, "created_at"), protocmp.IgnoreFields(&api.Deployment_DeploymentMetaData{}, "deploy_time")); diff != "" { t.Errorf("error mismatch (-want, +got):\n%s", diff) } return nil diff --git a/services/cd-service/pkg/service/git.go b/services/cd-service/pkg/service/git.go index d1a49e0e3..c1358ed8c 100644 --- a/services/cd-service/pkg/service/git.go +++ b/services/cd-service/pkg/service/git.go @@ -22,6 +22,8 @@ import ( "errors" "fmt" "github.com/freiheit-com/kuberpult/pkg/db" + "github.com/freiheit-com/kuberpult/pkg/logger" + "os" "sort" "strconv" @@ -54,6 +56,35 @@ func (s *GitServer) GetGitTags(ctx context.Context, _ *api.GetGitTagsRequest) (* return &api.GetGitTagsResponse{TagData: tags}, nil } +func getProductSummaryForEnv(ctx context.Context, transaction *sql.Tx, state *repository.State, overviewService *OverviewServiceServer, envName string, appDetailsCache map[string]*api.GetAppDetailsResponse) ([]api.ProductSummary, error) { + var summaryFromEnv []api.ProductSummary + appsForEnv, err := state.GetEnvironmentApplications(ctx, transaction, envName) + if err != nil { + return nil, fmt.Errorf("unable to get apps on environment for %s: %v", envName, err) + } + for _, appName := range appsForEnv { + currentAppDetails, err := overviewService.GetAppDetails(ctx, &api.GetAppDetailsRequest{AppName: appName}) + if err != nil { + return nil, fmt.Errorf("unable to get app details for app: '%s': %w", appName, err) + } + appDetailsCache[appName] = currentAppDetails + if _, ok := currentAppDetails.Deployments[envName]; !ok { + //The call state.GetEnvironmentApplications should guarantee us a deployment, but just in case it doesn't, we warn an error and continue + logger.FromContext(ctx).Sugar().Warnf("Could not find deployment information for app '%s' on environment '%s'", appName, envName) + continue + } + summaryFromEnv = append(summaryFromEnv, api.ProductSummary{ + CommitId: "", + DisplayVersion: "", + Team: "", + App: appName, + Version: strconv.FormatUint(currentAppDetails.Deployments[envName].Version, 10), + Environment: envName, + }) + } + return summaryFromEnv, nil +} + func (s *GitServer) GetProductSummary(ctx context.Context, in *api.GetProductSummaryRequest) (*api.GetProductSummaryResponse, error) { if in.Environment == nil && in.EnvironmentGroup == nil { return nil, fmt.Errorf("Must have an environment or environmentGroup to get the product summary for") @@ -66,82 +97,87 @@ func (s *GitServer) GetProductSummary(ctx context.Context, in *api.GetProductSum if in.ManifestRepoCommitHash == "" { return nil, fmt.Errorf("Must have a commit to get the product summary for") } - response, err := s.OverviewService.GetOverview(ctx, &api.GetOverviewRequest{GitRevision: in.ManifestRepoCommitHash}) - if err != nil { - return nil, fmt.Errorf("unable to get overview for %s: %v", in.ManifestRepoCommitHash, err) - } + if s.Config.DBHandler.ShouldUseOtherTables() { + dbHandler := s.OverviewService.DBHandler + state := s.OverviewService.Repository.State() - var summaryFromEnv []api.ProductSummary - if in.Environment != nil && *in.Environment != "" { - for _, group := range response.EnvironmentGroups { - for _, env := range group.Environments { - if env.Name == *in.Environment { - for _, app := range env.Applications { - summaryFromEnv = append(summaryFromEnv, api.ProductSummary{ - CommitId: "", - DisplayVersion: "", - Team: "", - App: app.Name, - Version: strconv.FormatUint(app.Version, 10), - Environment: *in.Environment, - }) + response, err := db.WithTransactionT[api.GetProductSummaryResponse](dbHandler, ctx, db.DefaultNumRetries, true, func(ctx context.Context, transaction *sql.Tx) (*api.GetProductSummaryResponse, error) { + overview, err := s.OverviewService.GetOverview(ctx, &api.GetOverviewRequest{GitRevision: in.ManifestRepoCommitHash}) + if err != nil { + return nil, fmt.Errorf("unable to get overview for %s: %v", in.ManifestRepoCommitHash, err) + } + accessedApps := make(map[string]*api.GetAppDetailsResponse) //store app details we access for later use and avoid calling the endpoint twice for each app + var summaryFromEnv []api.ProductSummary + if in.Environment != nil && *in.Environment != "" { + for _, group := range overview.EnvironmentGroups { + for _, env := range group.Environments { + if env.Name == *in.Environment { + summaryFromEnv, err = getProductSummaryForEnv(ctx, transaction, state, s.OverviewService, env.Name, accessedApps) + if err != nil { + return nil, fmt.Errorf("unable to get product summary for environment: '%s'", env.Name) + } + } } } - } - } - if len(summaryFromEnv) == 0 { - return &api.GetProductSummaryResponse{ - ProductSummary: nil, - }, nil - } - sort.Slice(summaryFromEnv, func(i, j int) bool { - a := summaryFromEnv[i].App - b := summaryFromEnv[j].App - return a < b - }) - } else { - for _, group := range response.EnvironmentGroups { - if *in.EnvironmentGroup == group.EnvironmentGroupName { - for _, env := range group.Environments { - var singleEnvSummary []api.ProductSummary - for _, app := range env.Applications { - singleEnvSummary = append(singleEnvSummary, api.ProductSummary{ - CommitId: "", - DisplayVersion: "", - Team: "", - App: app.Name, - Version: strconv.FormatUint(app.Version, 10), - Environment: env.Name, - }) + if len(summaryFromEnv) == 0 { + return &api.GetProductSummaryResponse{ + ProductSummary: nil, + }, nil + } + sort.Slice(summaryFromEnv, func(i, j int) bool { + a := summaryFromEnv[i].App + b := summaryFromEnv[j].App + return a < b + }) + } else { + for _, group := range overview.EnvironmentGroups { + if *in.EnvironmentGroup == group.EnvironmentGroupName { + for _, env := range group.Environments { + var singleEnvSummary []api.ProductSummary + singleEnvSummary, err := getProductSummaryForEnv(ctx, transaction, state, s.OverviewService, env.Name, accessedApps) + if err != nil { + return nil, fmt.Errorf("unable to get product summary for environment: '%s': %w", env.Name, err) + } + sort.Slice(singleEnvSummary, func(i, j int) bool { + a := singleEnvSummary[i].App + b := singleEnvSummary[j].App + return a < b + }) + summaryFromEnv = append(summaryFromEnv, singleEnvSummary...) + } } - sort.Slice(singleEnvSummary, func(i, j int) bool { - a := singleEnvSummary[i].App - b := singleEnvSummary[j].App - return a < b - }) - summaryFromEnv = append(summaryFromEnv, singleEnvSummary...) + } + if len(summaryFromEnv) == 0 { + return nil, nil } } - } - if len(summaryFromEnv) == 0 { - return nil, nil - } - } + var productVersion []*api.ProductSummary - var productVersion []*api.ProductSummary - for _, row := range summaryFromEnv { //nolint: govet - for _, app := range response.Applications { - if row.App == app.Name { - for _, release := range app.Releases { - if strconv.FormatUint(release.Version, 10) == row.Version { - productVersion = append(productVersion, &api.ProductSummary{App: row.App, Version: row.Version, CommitId: release.SourceCommitId, DisplayVersion: release.DisplayVersion, Environment: row.Environment, Team: app.Team}) - break + for _, row := range summaryFromEnv { //nolint: govet + appsForEnv, err := state.GetEnvironmentApplications(ctx, transaction, row.Environment) + if err != nil { + return nil, fmt.Errorf("unable to get environment applications for env: '%s': %w", row.Environment, err) + } + for _, app := range appsForEnv { + if row.App == app { + currentAppDetails := accessedApps[app] + if currentAppDetails == nil { + continue + } + for _, release := range currentAppDetails.Application.Releases { + if strconv.FormatUint(release.Version, 10) == row.Version { + productVersion = append(productVersion, &api.ProductSummary{App: row.App, Version: row.Version, CommitId: release.SourceCommitId, DisplayVersion: release.DisplayVersion, Environment: row.Environment, Team: currentAppDetails.Application.Team}) + break + } + } } } } - } + return &api.GetProductSummaryResponse{ProductSummary: productVersion}, nil + }) + return response, err } - return &api.GetProductSummaryResponse{ProductSummary: productVersion}, nil + return &api.GetProductSummaryResponse{ProductSummary: nil}, nil } func (s *GitServer) GetCommitInfo(ctx context.Context, in *api.GetCommitInfoRequest) (*api.GetCommitInfoResponse, error) { diff --git a/services/cd-service/pkg/service/overview.go b/services/cd-service/pkg/service/overview.go index fc29526c7..8ce41eab1 100644 --- a/services/cd-service/pkg/service/overview.go +++ b/services/cd-service/pkg/service/overview.go @@ -302,7 +302,6 @@ func (o *OverviewServiceServer) getOverview( result := api.GetOverviewResponse{ Branch: "", ManifestRepoUrl: "", - Applications: map[string]*api.Application{}, EnvironmentGroups: []*api.EnvironmentGroup{}, GitRevision: rev, LightweightApps: make([]*api.OverviewApplication, 0), @@ -313,6 +312,7 @@ func (o *OverviewServiceServer) getOverview( if err != nil { return nil, err } + if apps, err := s.GetApplications(ctx, transaction); err != nil { return nil, err } else { diff --git a/services/cd-service/pkg/service/overview_test.go b/services/cd-service/pkg/service/overview_test.go index f3a6363a0..0b653cab2 100644 --- a/services/cd-service/pkg/service/overview_test.go +++ b/services/cd-service/pkg/service/overview_test.go @@ -19,12 +19,11 @@ package service import ( "context" "database/sql" + "github.com/freiheit-com/kuberpult/pkg/testutil" "github.com/google/go-cmp/cmp/cmpopts" "sync" "testing" - "github.com/freiheit-com/kuberpult/pkg/testutil" - api "github.com/freiheit-com/kuberpult/pkg/api/v1" "github.com/freiheit-com/kuberpult/pkg/auth" "github.com/freiheit-com/kuberpult/pkg/config" @@ -53,6 +52,9 @@ func (m *mockOverviewService_StreamOverviewServer) Context() context.Context { func TestOverviewService(t *testing.T) { var dev = "dev" + var development = "development" + var staging = "staging" + var prod = "production" var upstreamLatest = true tcs := []struct { Name string @@ -60,6 +62,7 @@ func TestOverviewService(t *testing.T) { Test func(t *testing.T, svc *OverviewServiceServer) DB bool ExpectedCachedOverview *api.GetOverviewResponse + ExpectedAppDetails map[string]*api.GetAppDetailsResponse //appName -> appDetails }{ { Name: "A simple overview works", @@ -92,6 +95,7 @@ func TestOverviewService(t *testing.T) { }, &repository.CreateApplicationVersion{ Application: "test", + Version: 1, Manifests: map[string]string{ "development": "dev", }, @@ -101,6 +105,7 @@ func TestOverviewService(t *testing.T) { }, &repository.CreateApplicationVersion{ Application: "test-with-team", + Version: 1, Manifests: map[string]string{ "development": "dev", }, @@ -108,6 +113,7 @@ func TestOverviewService(t *testing.T) { }, &repository.CreateApplicationVersion{ Application: "test-with-incorrect-pr-number", + Version: 1, Manifests: map[string]string{ "development": "dev", }, @@ -117,6 +123,7 @@ func TestOverviewService(t *testing.T) { }, &repository.CreateApplicationVersion{ Application: "test-with-only-pr-number", + Version: 1, Manifests: map[string]string{ "development": "dev", }, @@ -152,7 +159,165 @@ func TestOverviewService(t *testing.T) { Message: "team lock message", }, }, + ExpectedAppDetails: map[string]*api.GetAppDetailsResponse{ + "test": { + Application: &api.Application{ + Name: "test", + SourceRepoUrl: "", + Releases: []*api.Release{ + { + Version: 1, + PrNumber: "678", + SourceAuthor: "example ", + SourceCommitId: "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef", + SourceMessage: "changed something (#678)", + }, + }, + }, + Deployments: map[string]*api.Deployment{ + "prod": { + Version: 1, + QueuedVersion: 0, + UndeployVersion: false, + DeploymentMetaData: &api.Deployment_DeploymentMetaData{}, + }, + }, + }, + "test-with-team": { + Application: &api.Application{ + Name: "test", + Team: "test-team", + }, + Deployments: map[string]*api.Deployment{ + "dev": {}, + }, + }, + }, + DB: true, + ExpectedCachedOverview: &api.GetOverviewResponse{ + EnvironmentGroups: []*api.EnvironmentGroup{ + { + EnvironmentGroupName: "dev", + Environments: []*api.Environment{ + { + Name: development, + Locks: map[string]*api.Lock{ + "manual": { + Message: "please", + LockId: "manual", + CreatedBy: &api.Actor{ + Name: "test tester", + Email: "testmail@example.com", + }, + }, + }, + TeamLocks: map[string]*api.Locks{ + "test-team": { + Locks: []*api.Lock{ + { + Message: "team lock message", + LockId: "manual-team-lock", + CreatedBy: &api.Actor{ + Name: "test tester", + Email: "testmail@example.com", + }, + }, + }, + }, + }, + Config: &api.EnvironmentConfig{ + Upstream: &api.EnvironmentConfig_Upstream{ + Latest: &upstreamLatest, + }, + Argocd: &api.EnvironmentConfig_ArgoCD{ + Destination: &api.EnvironmentConfig_ArgoCD_Destination{}, + }, + EnvironmentGroup: &dev, + }, + Priority: api.Priority_UPSTREAM, + }, + }, + Priority: api.Priority_UPSTREAM, + }, + { + EnvironmentGroupName: staging, + Environments: []*api.Environment{ + { + Name: staging, + Config: &api.EnvironmentConfig{ + Upstream: &api.EnvironmentConfig_Upstream{ + Environment: &development, + }, + Argocd: &api.EnvironmentConfig_ArgoCD{ + Destination: &api.EnvironmentConfig_ArgoCD_Destination{}, + }, + EnvironmentGroup: &staging, + }, + DistanceToUpstream: 1, + Priority: api.Priority_PRE_PROD, + }, + }, + Priority: api.Priority_PRE_PROD, + DistanceToUpstream: 1, + }, + { + EnvironmentGroupName: prod, + Environments: []*api.Environment{ + { + Name: prod, + AppLocks: map[string]*api.Locks{ + "test": { + Locks: []*api.Lock{ + { + Message: "no", + LockId: "manual", + CreatedBy: &api.Actor{ + Name: "test tester", + Email: "testmail@example.com", + }, + }, + }, + }, + }, + Config: &api.EnvironmentConfig{ + Upstream: &api.EnvironmentConfig_Upstream{ + Environment: &staging, + }, + Argocd: &api.EnvironmentConfig_ArgoCD{ + Destination: &api.EnvironmentConfig_ArgoCD_Destination{}, + }, + EnvironmentGroup: &prod, + }, + DistanceToUpstream: 2, + Priority: api.Priority_PROD, + }, + }, + Priority: api.Priority_PROD, + DistanceToUpstream: 2, + }, + }, + LightweightApps: []*api.OverviewApplication{ + { + Name: "test", + Team: "", + }, + { + Name: "test-with-team", + Team: "test-team", + }, + { + Name: "test-with-incorrect-pr-number", + Team: "", + }, + { + Name: "test-with-only-pr-number", + Team: "", + }, + }, + GitRevision: "0", + }, Test: func(t *testing.T, svc *OverviewServiceServer) { + //TODO: This test suite has some commented out sections. These tests should either be adapted or reimplemented in Ref: SRX-9PBRYS. var ctx = auth.WriteUserToContext(testutil.MakeTestContext(), auth.User{ Email: "test-email@example.com", Name: "overview tester", @@ -169,17 +334,27 @@ func TestOverviewService(t *testing.T) { if len(resp.EnvironmentGroups) != expectedEnvs { t.Errorf("expected %d environmentGroups, got %q", expectedEnvs, resp.EnvironmentGroups) } - testApp := resp.Applications["test"] - releases := testApp.Releases + app, err := svc.GetAppDetails(ctx, &api.GetAppDetailsRequest{AppName: "test"}) + if err != nil { + t.Errorf("Error fetching information for app test: %v", err) + } + testApp := app.Application + + releases := app.Application.Releases if len(releases) != 1 { t.Errorf("Expected one release, but got %#q", len(releases)) } if releases[0].PrNumber != "678" { t.Errorf("Release should have PR number \"678\", but got %q", releases[0].PrNumber) } - testApp = resp.Applications["test-with-team"] + + app, err = svc.GetAppDetails(ctx, &api.GetAppDetailsRequest{AppName: "test-with-team"}) + if err != nil { + t.Errorf("Error fetching information for app test") + } + testApp = app.Application if testApp.SourceRepoUrl != "" { - t.Errorf("Expected \"\", but got %#q", resp.Applications["test"].SourceRepoUrl) + t.Errorf("Expected \"\", but got %#q", testApp.SourceRepoUrl) } releases = testApp.Releases if len(releases) != 1 { @@ -188,7 +363,13 @@ func TestOverviewService(t *testing.T) { if releases[0].PrNumber != "" { t.Errorf("Release should not have PR number") } - testApp = resp.Applications["test-with-incorrect-pr-number"] + + app, err = svc.GetAppDetails(ctx, &api.GetAppDetailsRequest{AppName: "test-with-incorrect-pr-number"}) + if err != nil { + t.Errorf("Error fetching information for app test") + } + testApp = app.Application + releases = testApp.Releases if len(releases) != 1 { t.Errorf("Expected one release, but got %#q", len(releases)) @@ -196,7 +377,11 @@ func TestOverviewService(t *testing.T) { if releases[0].PrNumber != "" { t.Errorf("Release should not have PR number since is an invalid PR number") } - testApp = resp.Applications["test-with-only-pr-number"] + app, err = svc.GetAppDetails(ctx, &api.GetAppDetailsRequest{AppName: "test-with-only-pr-number"}) + if err != nil { + t.Errorf("Error fetching information for app test") + } + testApp = app.Application releases = testApp.Releases if len(releases) != 1 { t.Errorf("Expected one release, but got %#q", len(releases)) @@ -204,8 +389,8 @@ func TestOverviewService(t *testing.T) { if releases[0].PrNumber == "" { t.Errorf("Release should have PR number \"678\", but got %q", releases[0].PrNumber) } - // Check Dev - // Note that EnvironmentGroups are sorted, so it's dev,staging,production (see MapEnvironmentsToGroups for details on sorting) + //Check Dev + //Note that EnvironmentGroups are sorted, so it's dev,staging,production (see MapEnvironmentsToGroups for details on sorting) devGroup := resp.EnvironmentGroups[0] if devGroup.EnvironmentGroupName != "dev" { t.Errorf("dev environmentGroup has wrong name: %q", devGroup.EnvironmentGroupName) @@ -233,32 +418,19 @@ func TestOverviewService(t *testing.T) { } } + if _, ok := dev.TeamLocks["test-team"]; !ok { + t.Errorf("development environment doesn't contain manual-team lock: %#v", dev.TeamLocks) + + } // check team lock - if lck, ok := dev.Applications["test-with-team"].TeamLocks["manual-team-lock"]; !ok { - t.Errorf("development environment doesn't contain manual-team lock: %#v", dev.Locks) + if len(dev.TeamLocks["test-team"].Locks) != 1 { + t.Errorf("development environment doesn't contain manual-team lock: %#v", dev.TeamLocks) } else { + lck := dev.TeamLocks["test-team"].Locks[0] if lck.Message != "team lock message" { t.Errorf("development environment manual lock has wrong message: %q", lck.Message) } } - if len(dev.Applications) != 4 { - t.Errorf("development environment has wrong applications: %#v", dev.Applications) - } - if app, ok := dev.Applications["test"]; !ok { - t.Errorf("development environment has wrong applications: %#v", dev.Applications) - } else { - if app.Version != 1 { - t.Errorf("test application has not version 1 but %d", app.Version) - } - if len(app.Locks) != 0 { - t.Errorf("test application has locks in development: %#v", app.Locks) - } - } - - got := dev.Applications["test"].GetDeploymentMetaData().DeployAuthor - if got != "test tester" { - t.Errorf("development environment deployment did not create deploymentMetaData, got %s", got) - } // Check staging stageGroup := resp.EnvironmentGroups[1] @@ -282,9 +454,6 @@ func TestOverviewService(t *testing.T) { if len(stage.Locks) != 0 { t.Errorf("staging environment has wrong locks: %#v", stage.Locks) } - if len(stage.Applications) != 0 { - t.Errorf("staging environment has wrong applications: %#v", stage.Applications) - } // Check production prodGroup := resp.EnvironmentGroups[2] @@ -308,53 +477,62 @@ func TestOverviewService(t *testing.T) { if len(prod.Locks) != 0 { t.Errorf("production environment has wrong locks: %#v", prod.Locks) } - if len(prod.Applications) != 1 { - t.Errorf("production environment has wrong applications: %#v", prod.Applications) + + //Check applications + if len(resp.LightweightApps) != 4 { + t.Errorf("expected two application, got %#v", resp.LightweightApps) } - if app, ok := prod.Applications["test"]; !ok { - t.Errorf("production environment has wrong applications: %#v", prod.Applications) - } else { - if app.Version != 0 { - t.Errorf("test application has not version 0 but %d", app.Version) - } - if len(app.Locks) != 1 { - t.Errorf("test application has locks in production: %#v", app.Locks) - } + + app, err = svc.GetAppDetails(ctx, &api.GetAppDetailsRequest{AppName: "test"}) + if err != nil { + t.Errorf("Error fetching information for app test") + } + test := app.Application + if _, ok := app.Deployments["development"]; !ok { + t.Errorf("no deployments foubnd on development") } - // Check applications - if len(resp.Applications) != 4 { - t.Errorf("expected two application, got %#v", resp.Applications) + got := app.Deployments["development"].GetDeploymentMetaData().DeployAuthor + if got != "test tester" { + t.Errorf("development environment deployment did not create deploymentMetaData, got %s", got) } - if test, ok := resp.Applications["test"]; !ok { - t.Errorf("test application is missing in %#v", resp.Applications) - } else { - if test.Name != "test" { - t.Errorf("test applications name is not test but %q", test.Name) - } - if len(test.Releases) != 1 { - t.Errorf("expected one release, got %#v", test.Releases) - } - if test.Releases[0].Version != 1 { - t.Errorf("expected test release version to be 1, but got %d", test.Releases[0].Version) - } - if test.Releases[0].SourceAuthor != "example " { - t.Errorf("expected test source author to be \"example \", but got %q", test.Releases[0].SourceAuthor) - } - if test.Releases[0].SourceMessage != "changed something (#678)" { - t.Errorf("expected test source message to be \"changed something\", but got %q", test.Releases[0].SourceMessage) - } - if test.Releases[0].SourceCommitId != "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef" { - t.Errorf("expected test source commit id to be \"deadbeef\", but got %q", test.Releases[0].SourceCommitId) - } + + if app.Deployments["development"].Version != 1 { + t.Errorf("test application has not version 1 but %d", app.Deployments["development"].Version) } - if testWithTeam, ok := resp.Applications["test-with-team"]; !ok { - t.Errorf("test-with-team application is missing in %#v", resp.Applications) - } else { - if testWithTeam.Team != "test-team" { - t.Errorf("application team is not test-team but %q", testWithTeam.Team) - } + if len(dev.AppLocks) != 0 { + t.Errorf("test application has locks in development: %#v", dev.AppLocks) + } + + if test.Name != "test" { + t.Errorf("test applications name is not test but %q", test.Name) + } + if len(test.Releases) != 1 { + t.Errorf("expected one release, got %#v", test.Releases) + } + if test.Releases[0].Version != 1 { + t.Errorf("expected test release version to be 1, but got %d", test.Releases[0].Version) + } + if test.Releases[0].SourceAuthor != "example " { + t.Errorf("expected test source author to be \"example \", but got %q", test.Releases[0].SourceAuthor) + } + if test.Releases[0].SourceMessage != "changed something (#678)" { + t.Errorf("expected test source message to be \"changed something\", but got %q", test.Releases[0].SourceMessage) + } + if test.Releases[0].SourceCommitId != "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef" { + t.Errorf("expected test source commit id to be \"deadbeef\", but got %q", test.Releases[0].SourceCommitId) + } + + app, err = svc.GetAppDetails(ctx, &api.GetAppDetailsRequest{AppName: "test-with-team"}) + if err != nil { + t.Errorf("Error fetching information for app test") } + testWithTeam := app.Application + + if testWithTeam.Team != "test-team" { + t.Errorf("application team is not test-team but %q", testWithTeam.Team) + } + }, }, { @@ -404,14 +582,18 @@ func TestOverviewService(t *testing.T) { if overview1 == nil { t.Fatal("overview is nil") } - v1 := overview1.GetEnvironmentGroups()[0].GetEnvironments()[0].GetApplications()["test"].Version + v1 := overview1.GetEnvironmentGroups()[0].GetEnvironments()[0].GetLocks() // Update a version and see that the version changed - err := svc.Repository.Apply(ctx, &repository.DeployApplicationVersion{ - Application: "test", - Environment: "development", - Version: 2, + err := svc.Repository.Apply(ctx, &repository.CreateEnvironmentLock{ + Environment: "development", + LockId: "ov-test", + Message: "stream overview test", + CiLink: "", + AllowedDomains: []string{}, + TransformerEslVersion: 0, }) + if err != nil { t.Fatal(err) } @@ -421,8 +603,8 @@ func TestOverviewService(t *testing.T) { if overview2 == nil { t.Fatal("overview is nil") } - v2 := overview2.EnvironmentGroups[0].Environments[0].Applications["test"].Version - if v1 == v2 { + v2 := overview2.GetEnvironmentGroups()[0].GetEnvironments()[0].GetLocks() + if diff := cmp.Diff(v1, v2); diff == "" { t.Fatalf("Versions are not different: %q vs %q", v1, v2) } @@ -449,49 +631,12 @@ func TestOverviewService(t *testing.T) { }, EnvironmentGroup: &dev, }, - Applications: map[string]*api.Environment_Application{ - "test": { - Name: "test", - Version: 1, - DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ - DeployAuthor: "testmail@example.com", - DeployTime: "1", - }, - Team: "team-123", - }, - }, Priority: api.Priority_YOLO, }, }, Priority: api.Priority_YOLO, }, }, - Applications: map[string]*api.Application{ - "test": { - Name: "test", - Releases: []*api.Release{ - { - Version: 1, - SourceCommitId: "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef", - SourceAuthor: "example ", - SourceMessage: "changed something (#678)", - PrNumber: "678", - CreatedAt: ×tamppb.Timestamp{Seconds: 1, Nanos: 1}, - }, - { - Version: 2, - SourceCommitId: "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef", - SourceAuthor: "example ", - SourceMessage: "changed something (#678)", - PrNumber: "678", - IsMinor: true, - IsPrepublish: true, - CreatedAt: ×tamppb.Timestamp{Seconds: 1, Nanos: 1}, - }, - }, - Team: "team-123", - }, - }, LightweightApps: []*api.OverviewApplication{ { Name: "test", @@ -524,7 +669,7 @@ func TestOverviewService(t *testing.T) { TransformerEslVersion: 1, Application: "test", Manifests: map[string]string{ - "development": "v1", + "dev": "v1", }, }, &repository.CreateApplicationVersion{ @@ -537,12 +682,11 @@ func TestOverviewService(t *testing.T) { DisplayVersion: "", WriteCommitData: false, PreviousCommit: "", - TransformerEslVersion: 1, + TransformerEslVersion: 2, Application: "test", Manifests: map[string]string{ - "development": "v1", + "dev": "v2", }, - IsPrepublish: true, }, }, Test: func(t *testing.T, svc *OverviewServiceServer) { @@ -579,30 +723,30 @@ func TestOverviewService(t *testing.T) { } // Check applications - if len(resp.Applications) != 1 { - t.Errorf("expected one application, got %#v", resp.Applications) + app, err := svc.GetAppDetails(ctx, &api.GetAppDetailsRequest{AppName: "test"}) + if err != nil { + t.Errorf("got an error fetching app deails") } - if test, ok := resp.Applications["test"]; !ok { - t.Errorf("test application is missing in %#v", resp.Applications) - } else { - if test.Name != "test" { - t.Errorf("test applications name is not test but %q", test.Name) - } - if len(test.Releases) != 2 { - t.Errorf("expected two releases, got %#v", test.Releases) - } - if test.Releases[0].Version != 1 { - t.Errorf("expected test release version to be 1, but got %d", test.Releases[0].Version) - } - if test.Releases[0].SourceAuthor != "example " { - t.Errorf("expected test source author to be \"example \", but got %q", test.Releases[0].SourceAuthor) - } - if test.Releases[0].SourceMessage != "changed something (#678)" { - t.Errorf("expected test source message to be \"changed something\", but got %q", test.Releases[0].SourceMessage) - } - if test.Releases[0].SourceCommitId != "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef" { - t.Errorf("expected test source commit id to be \"deadbeef\", but got %q", test.Releases[0].SourceCommitId) - } + + test := app.Application + + if test.Name != "test" { + t.Errorf("test applications name is not test but %q", test.Name) + } + if len(test.Releases) != 2 { + t.Errorf("expected two releases, got %v", test.Releases) + } + if test.Releases[1].Version != 1 { + t.Errorf("expected test release version to be 1, but got %d", test.Releases[0].Version) + } + if test.Releases[1].SourceAuthor != "example " { + t.Errorf("expected test source author to be \"example \", but got %q", test.Releases[0].SourceAuthor) + } + if test.Releases[1].SourceMessage != "changed something (#678)" { + t.Errorf("expected test source message to be \"changed something\", but got %q", test.Releases[0].SourceMessage) + } + if test.Releases[1].SourceCommitId != "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef" { + t.Errorf("expected test source commit id to be \"deadbeef\", but got %q", test.Releases[0].SourceCommitId) } //Check cache @@ -646,6 +790,7 @@ func TestOverviewService(t *testing.T) { svc := &OverviewServiceServer{ Repository: repo, Shutdown: shutdown, + DBHandler: repo.State().DBHandler, Context: context.Background(), } tc.Test(t, svc) @@ -656,7 +801,7 @@ func TestOverviewService(t *testing.T) { return err } cachedResponse.GitRevision = "0" - if diff := cmp.Diff(tc.ExpectedCachedOverview, cachedResponse, protocmp.Transform(), protocmp.IgnoreFields(&api.Release{}, "created_at"), protocmp.IgnoreFields(&api.Environment_Application_DeploymentMetaData{}, "deploy_time")); diff != "" { + if diff := cmp.Diff(tc.ExpectedCachedOverview, cachedResponse, protocmp.Transform(), protocmp.IgnoreFields(&api.Release{}, "created_at"), protocmp.IgnoreFields(&api.Lock{}, "created_at")); diff != "" { t.Errorf("latest overview cache mismatch (-want +got):\n%s", diff) } return nil @@ -697,10 +842,12 @@ func TestGetApplicationDetails(t *testing.T) { }, Deployments: map[string]*api.Deployment{ env: { - Version: 1, - QueuedVersion: 0, - UndeployVersion: false, - DeploymentMetaData: &api.Deployment_DeploymentMetaData{}, + Version: 1, + QueuedVersion: 0, + UndeployVersion: false, + DeploymentMetaData: &api.Deployment_DeploymentMetaData{ + DeployAuthor: "test tester", + }, }, }, TeamLocks: map[string]*api.Locks{ @@ -848,6 +995,108 @@ func TestGetApplicationDetails(t *testing.T) { } } +func TestDeriveUndeploySummary(t *testing.T) { + var tcs = []struct { + Name string + AppName string + Deployments map[string]*api.Deployment + ExpectedResult api.UndeploySummary + }{ + { + Name: "No Environments", + AppName: "foo", + Deployments: map[string]*api.Deployment{}, + ExpectedResult: api.UndeploySummary_UNDEPLOY, + }, + { + Name: "one Environment but no Application", + AppName: "foo", + Deployments: map[string]*api.Deployment{ + "bar": { // different app + UndeployVersion: true, + Version: 666, + }, + }, + ExpectedResult: api.UndeploySummary_UNDEPLOY, + }, + { + Name: "One Env with undeploy", + AppName: "foo", + Deployments: map[string]*api.Deployment{ + "foo": { + UndeployVersion: true, + Version: 666, + }, + }, + ExpectedResult: api.UndeploySummary_UNDEPLOY, + }, + { + Name: "One Env with normal version", + AppName: "foo", + Deployments: map[string]*api.Deployment{ + "foo": { + UndeployVersion: false, + Version: 666, + }, + }, + ExpectedResult: api.UndeploySummary_NORMAL, + }, + { + Name: "Two Envs all undeploy", + AppName: "foo", + Deployments: map[string]*api.Deployment{ + "foo": { + UndeployVersion: true, + Version: 666, + }, + "bar": { + UndeployVersion: true, + Version: 666, + }, + }, + ExpectedResult: api.UndeploySummary_UNDEPLOY, + }, + { + Name: "Two Envs all normal", + AppName: "foo", + Deployments: map[string]*api.Deployment{ + "foo": { + UndeployVersion: false, + Version: 666, + }, + "bar": { + UndeployVersion: false, + Version: 666, + }, + }, + ExpectedResult: api.UndeploySummary_NORMAL, + }, + { + Name: "Two Envs all different", + AppName: "foo", + Deployments: map[string]*api.Deployment{ + "foo": { + UndeployVersion: false, + Version: 666, + }, + "bar": { + UndeployVersion: true, + Version: 666, + }, + }, + ExpectedResult: api.UndeploySummary_MIXED, + }, + } + for _, tc := range tcs { + t.Run(tc.Name, func(t *testing.T) { + actualResult := deriveUndeploySummary(tc.AppName, tc.Deployments) + if !cmp.Equal(tc.ExpectedResult, actualResult) { + t.Fatal("Output mismatch (-want +got):\n", cmp.Diff(tc.ExpectedResult, actualResult)) + } + }) + } +} + func TestOverviewServiceFromCommit(t *testing.T) { type step struct { Transformer repository.Transformer @@ -1016,3 +1265,344 @@ func TestOverviewServiceFromCommit(t *testing.T) { }) } } + +//TODO: This test suite has some commented out sections. These tests should either be adapted or reimplemented in Ref: SRX-9PBRYS. +//func TestUpdateOverviewDeploymentAttempt(t *testing.T) { +// var dev = "dev" +// var upstreamLatest = true +// var version int64 = 12 +// startingOverview := makeTestStartingOverview() +// tcs := []struct { +// Name string +// NewDeployment *QueuedDeployment +// ExpectedError error +// ExpectedOverview *api.GetOverviewResponse +// }{ +// { +// Name: "Update overview Deployment Attempt", +// NewDeployment: &QueuedDeployment{ +// EslVersion: 1, +// Env: "development", +// App: "test", +// Version: &version, +// Created: time.Date(2024, time.July, 12, 15, 30, 0, 0, time.UTC), +// }, +// ExpectedOverview: &api.GetOverviewResponse{ +// EnvironmentGroups: []*api.EnvironmentGroup{ +// { +// EnvironmentGroupName: "dev", +// Environments: []*api.Environment{ +// { +// Name: "development", +// Config: &api.EnvironmentConfig{ +// Upstream: &api.EnvironmentConfig_Upstream{ +// Latest: &upstreamLatest, +// }, +// Argocd: &api.EnvironmentConfig_ArgoCD{}, +// EnvironmentGroup: &dev, +// }, +// //Applications: map[string]*api.Environment_Application{ +// // "test": { +// // Name: "test", +// // Version: 1, +// // DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ +// // DeployAuthor: "testmail@example.com", +// // DeployTime: "1", +// // }, +// // Team: "team-123", +// // QueuedVersion: 12, +// // }, +// //}, +// Priority: api.Priority_YOLO, +// }, +// }, +// Priority: api.Priority_YOLO, +// }, +// }, +// //Applications: map[string]*api.Application{ +// // "test": { +// // Name: "test", +// // Releases: []*api.Release{ +// // { +// // Version: 1, +// // SourceCommitId: "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef", +// // SourceAuthor: "example ", +// // SourceMessage: "changed something (#678)", +// // PrNumber: "678", +// // CreatedAt: ×tamppb.Timestamp{Seconds: 1, Nanos: 1}, +// // }, +// // }, +// // Team: "team-123", +// // Warnings: []*api.Warning{ +// // { +// // WarningType: &api.Warning_UnusualDeploymentOrder{ +// // UnusualDeploymentOrder: &api.UnusualDeploymentOrder{ +// // UpstreamEnvironment: "staging", +// // ThisVersion: 12, +// // ThisEnvironment: "development", +// // }, +// // }, +// // }, +// // }, +// // }, +// //}, +// LightweightApps: []*api.OverviewApplication{ +// { +// Name: "test", +// Team: "team-123", +// }, +// }, +// GitRevision: "0", +// }, +// }, +// { +// Name: "app does not exists", +// NewDeployment: &QueuedDeployment{ +// EslVersion: 1, +// Env: "development", +// App: "does-not-exists", +// Version: &version, +// Created: time.Date(2024, time.July, 12, 15, 30, 0, 0, time.UTC), +// }, +// ExpectedError: errMatcher{"could not find application 'does-not-exists' in apps table: got no result"}, +// }, +// { +// Name: "env does not exists", +// NewDeployment: &QueuedDeployment{ +// EslVersion: 1, +// Env: "does-not-exists", +// App: "test", +// Version: &version, +// Created: time.Date(2024, time.July, 12, 15, 30, 0, 0, time.UTC), +// }, +// ExpectedError: errMatcher{"could not find environment does-not-exists in overview"}, +// }, +// { +// Name: "nil queued deployment", +// ExpectedOverview: startingOverview, +// }, +// } +// +// for _, tc := range tcs { +// t.Run(tc.Name, func(t *testing.T) { +// ctx := testutil.MakeTestContext() +// dbHandler := setupDB(t) +// +// err := dbHandler.WithTransaction(ctx, false, func(ctx context.Context, transaction *sql.Tx) error { +// err := dbHandler.WriteOverviewCache(ctx, transaction, startingOverview) +// if err != nil { +// return err +// } +// err = dbHandler.UpdateOverviewDeploymentAttempt(ctx, transaction, tc.NewDeployment) +// if err != nil { +// if diff := cmp.Diff(tc.ExpectedError, err, cmpopts.EquateErrors()); diff != "" { +// return fmt.Errorf("mismatch between errors (-want +got):\n%s", diff) +// } +// return nil +// } +// latestOverview, err := dbHandler.ReadLatestOverviewCache(ctx, transaction) +// if err != nil { +// return err +// } +// opts := getOverviewIgnoredTypes() +// if diff := cmp.Diff(tc.ExpectedOverview, latestOverview, opts); diff != "" { +// return fmt.Errorf("mismatch (-want +got):\n%s", diff) +// } +// return nil +// }) +// if err != nil { +// t.Fatal(err) +// } +// }) +// } +//} +// + +//TODO: This test suite has some commented out sections. These tests should either be adapted or reimplemented in Ref: SRX-9PBRYS. +//func TestUpdateOverviewApplicationLock(t *testing.T) { +// var dev = "dev" +// var upstreamLatest = true +// startingOverview := makeTestStartingOverview() +// tcs := []struct { +// Name string +// NewApplicationLock ApplicationLock +// ExcpectedOverview *api.GetOverviewResponse +// ExpectedError error +// }{ +// { +// Name: "Update overview", +// NewApplicationLock: ApplicationLock{ +// Env: "development", +// App: "test", +// LockID: "dev-lock", +// EslVersion: 2, +// Deleted: false, +// Metadata: LockMetadata{ +// Message: "My lock on dev for my-team", +// CreatedByName: "myself", +// CreatedByEmail: "myself@example.com", +// }, +// Created: time.Date(2024, time.July, 12, 15, 30, 0, 0, time.UTC), +// }, +// ExcpectedOverview: &api.GetOverviewResponse{ +// EnvironmentGroups: []*api.EnvironmentGroup{ +// { +// EnvironmentGroupName: "dev", +// Environments: []*api.Environment{ +// { +// Name: "development", +// Config: &api.EnvironmentConfig{ +// Upstream: &api.EnvironmentConfig_Upstream{ +// Latest: &upstreamLatest, +// }, +// Argocd: &api.EnvironmentConfig_ArgoCD{}, +// EnvironmentGroup: &dev, +// }, +// //Applications: map[string]*api.Environment_Application{ +// // "test": { +// // Name: "test", +// // Version: 1, +// // DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ +// // DeployAuthor: "testmail@example.com", +// // DeployTime: "1", +// // }, +// // Team: "team-123", +// // Locks: map[string]*api.Lock{ +// // "dev-lock": { +// // Message: "My lock on dev for my-team", +// // LockId: "dev-lock", +// // CreatedAt: timestamppb.New(time.Date(2024, time.July, 12, 15, 30, 0, 0, time.UTC)), +// // CreatedBy: &api.Actor{ +// // Name: "myself", +// // Email: "myself@example.com", +// // }, +// // }, +// // }, +// // }, +// //}, +// Priority: api.Priority_YOLO, +// }, +// }, +// Priority: api.Priority_YOLO, +// }, +// }, +// //Applications: map[string]*api.Application{ +// // "test": { +// // Name: "test", +// // Releases: []*api.Release{ +// // { +// // Version: 1, +// // SourceCommitId: "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef", +// // SourceAuthor: "example ", +// // SourceMessage: "changed something (#678)", +// // PrNumber: "678", +// // CreatedAt: ×tamppb.Timestamp{Seconds: 1, Nanos: 1}, +// // }, +// // }, +// // Team: "team-123", +// // Warnings: []*api.Warning{ +// // { +// // WarningType: &api.Warning_UnusualDeploymentOrder{ +// // UnusualDeploymentOrder: &api.UnusualDeploymentOrder{ +// // UpstreamEnvironment: "staging", +// // ThisVersion: 12, +// // ThisEnvironment: "development", +// // }, +// // }, +// // }, +// // }, +// // }, +// //}, +// LightweightApps: []*api.OverviewApplication{ +// { +// Name: "test", +// Team: "team-123", +// }, +// }, +// GitRevision: "0", +// }, +// }, +// { +// Name: "env does not exists", +// NewApplicationLock: ApplicationLock{ +// Env: "does-not-exists", +// App: "test", +// LockID: "dev-lock", +// EslVersion: 2, +// Deleted: false, +// Metadata: LockMetadata{ +// Message: "My lock on dev for my-team", +// CreatedByName: "myself", +// CreatedByEmail: "myself@example.com", +// }, +// Created: time.Date(2024, time.July, 12, 15, 30, 0, 0, time.UTC), +// }, +// ExpectedError: errMatcher{"could not find environment does-not-exists in overview"}, +// }, +// { +// Name: "app does not exists", +// NewApplicationLock: ApplicationLock{ +// Env: "development", +// App: "does-not-exists", +// LockID: "dev-lock", +// EslVersion: 2, +// Deleted: false, +// Metadata: LockMetadata{ +// Message: "My lock on dev for my-team", +// CreatedByName: "myself", +// CreatedByEmail: "myself@example.com", +// }, +// Created: time.Date(2024, time.July, 12, 15, 30, 0, 0, time.UTC), +// }, +// ExpectedError: errMatcher{"could not find application 'does-not-exists' in apps table: got no result"}, +// }, +// } +// +// for _, tc := range tcs { +// tc := tc +// t.Run(tc.Name, func(t *testing.T) { +// t.Parallel() +// ctx := testutil.MakeTestContext() +// dbHandler := setupDB(t) +// +// err := dbHandler.WithTransaction(ctx, false, func(ctx context.Context, transaction *sql.Tx) error { +// err := dbHandler.WriteOverviewCache(ctx, transaction, startingOverview) +// if err != nil { +// return err +// } +// err = dbHandler.UpdateOverviewApplicationLock(ctx, transaction, tc.NewApplicationLock, tc.NewApplicationLock.Created) +// if err != nil { +// if diff := cmp.Diff(tc.ExpectedError, err, cmpopts.EquateErrors()); diff != "" { +// return fmt.Errorf("mismatch between errors (-want +got):\n%s", diff) +// } +// return nil +// } +// latestOverview, err := dbHandler.ReadLatestOverviewCache(ctx, transaction) +// if err != nil { +// return err +// } +// opts := getOverviewIgnoredTypes() +// if diff := cmp.Diff(tc.ExcpectedOverview, latestOverview, opts); diff != "" { +// return fmt.Errorf("mismatch (-want +got):\n%s", diff) +// } +// tc.NewApplicationLock.Deleted = true +// err = dbHandler.UpdateOverviewApplicationLock(ctx, transaction, tc.NewApplicationLock, tc.NewApplicationLock.Created) +// if err != nil { +// return err +// } +// latestOverview, err = dbHandler.ReadLatestOverviewCache(ctx, transaction) +// if err != nil { +// return err +// } +// if diff := cmp.Diff(startingOverview, latestOverview, opts); diff != "" { +// return fmt.Errorf("mismatch (-want +got):\n%s", diff) +// } +// return nil +// }) +// +// if err != nil { +// t.Fatal(err) +// } +// }) +// } +//} diff --git a/services/frontend-service/pkg/handler/commit_deployments_test.go b/services/frontend-service/pkg/handler/commit_deployments_test.go index 9ca276235..6c6ee3e12 100644 --- a/services/frontend-service/pkg/handler/commit_deployments_test.go +++ b/services/frontend-service/pkg/handler/commit_deployments_test.go @@ -83,7 +83,7 @@ func TestHandleCommitDeployments(t *testing.T) { inputTail: "123456/", failGrpcCall: false, expectedStatusCode: http.StatusOK, - expectedResponse: "{\"deploymentStatus\":{\"app1\":{\"deploymentStatus\":{\"dev\":\"DEPLOYED\", \"prod\":\"UNKNOWN\", \"stage\":\"PENDING\"}}}}\n", + expectedResponse: "{\"deploymentStatus\":{\"app1\":{\"deploymentStatus\":{\"dev\":\"DEPLOYED\",\"prod\":\"UNKNOWN\",\"stage\":\"PENDING\"}}}}\n", }, } for _, tc := range tcs { diff --git a/services/frontend-service/src/ui/Pages/Environments/EnvironmentsPage.test.tsx b/services/frontend-service/src/ui/Pages/Environments/EnvironmentsPage.test.tsx index 640a47144..ccea1b8a2 100644 --- a/services/frontend-service/src/ui/Pages/Environments/EnvironmentsPage.test.tsx +++ b/services/frontend-service/src/ui/Pages/Environments/EnvironmentsPage.test.tsx @@ -25,14 +25,16 @@ const sampleEnvsA: Environment[] = [ { name: 'foo', locks: {}, - applications: {}, + appLocks: {}, + teamLocks: {}, distanceToUpstream: 0, priority: Priority.YOLO, }, { name: 'moreTest', locks: {}, - applications: {}, + appLocks: {}, + teamLocks: {}, distanceToUpstream: 0, priority: Priority.YOLO, }, @@ -42,14 +44,16 @@ const sampleEnvsB: Environment[] = [ { name: 'fooB', locks: {}, - applications: {}, + appLocks: {}, + teamLocks: {}, distanceToUpstream: 0, priority: Priority.YOLO, }, { name: 'moreTestB', locks: {}, - applications: {}, + appLocks: {}, + teamLocks: {}, distanceToUpstream: 0, priority: Priority.YOLO, }, diff --git a/services/frontend-service/src/ui/Pages/Home/Home.test.tsx b/services/frontend-service/src/ui/Pages/Home/Home.test.tsx index e782fc394..803970bc4 100644 --- a/services/frontend-service/src/ui/Pages/Home/Home.test.tsx +++ b/services/frontend-service/src/ui/Pages/Home/Home.test.tsx @@ -206,7 +206,6 @@ describe('Get teams from application list (useTeamNames)', () => { name: 'right amount of teams - 4 sorted results', overview: { - applications: {}, lightweightApps: [ { name: 'foo', @@ -289,7 +288,6 @@ describe('Get teams from application list (useTeamNames)', () => { { name: "doesn't collect duplicate team names - 2 sorted results", overview: { - applications: {}, lightweightApps: [ { name: 'foo', @@ -355,7 +353,6 @@ describe('Get teams from application list (useTeamNames)', () => { { name: "doesn't collect empty team names and adds option to dropdown - 2 sorted results", overview: { - applications: {}, lightweightApps: [ { name: 'foo', @@ -463,7 +460,6 @@ describe('Get applications from selected teams (useApplicationsFilteredAndSorted name: 'gets filtered apps by team - 2 results', selectedTeams: ['dummy', 'foo'], Overview: { - applications: {}, environmentGroups: [], gitRevision: '', branch: '', @@ -547,7 +543,6 @@ describe('Get applications from selected teams (useApplicationsFilteredAndSorted name: 'shows both applications of the selected team - 2 results', selectedTeams: ['dummy'], Overview: { - applications: {}, environmentGroups: [], gitRevision: '', branch: '', @@ -614,7 +609,6 @@ describe('Get applications from selected teams (useApplicationsFilteredAndSorted name: 'no teams selected (shows every application) - 4 results', selectedTeams: [], Overview: { - applications: {}, environmentGroups: [], gitRevision: '', branch: '', @@ -685,7 +679,6 @@ describe('Get applications from selected teams (useApplicationsFilteredAndSorted name: 'selected team has no assigned applications - 0 results', selectedTeams: ['thisTeamDoesntExist'], Overview: { - applications: {}, environmentGroups: [], gitRevision: '', branch: '', diff --git a/services/frontend-service/src/ui/Pages/Locks/LocksPage.test.tsx b/services/frontend-service/src/ui/Pages/Locks/LocksPage.test.tsx index c8ccd66bc..57a7bf43b 100644 --- a/services/frontend-service/src/ui/Pages/Locks/LocksPage.test.tsx +++ b/services/frontend-service/src/ui/Pages/Locks/LocksPage.test.tsx @@ -23,7 +23,7 @@ import { useFilteredEnvironmentLockIDs, } from '../../utils/store'; import { MemoryRouter } from 'react-router-dom'; -import { Environment, Priority } from '../../../api/api'; +import { Environment, OverviewApplication, Priority } from '../../../api/api'; import { fakeLoadEverything, enableDexAuth } from '../../../setupTests'; describe('LocksPage', () => { @@ -86,7 +86,8 @@ describe('Test env locks', () => { { name: 'integration', locks: { locktest: { message: 'locktest', lockId: 'ui-v2-1337' } }, - applications: {}, + appLocks: {}, + teamLocks: {}, distanceToUpstream: 0, priority: 2, }, @@ -104,7 +105,8 @@ describe('Test env locks', () => { lockfoo: { message: 'lockfoo', lockId: 'ui-v2-123', createdAt: new Date(1995, 11, 16) }, lockbar: { message: 'lockbar', lockId: 'ui-v2-321', createdAt: new Date(1995, 11, 15) }, }, - applications: {}, + appLocks: {}, + teamLocks: {}, distanceToUpstream: 0, priority: 2, }, @@ -122,7 +124,8 @@ describe('Test env locks', () => { lockfoo: { message: 'lockfoo', lockId: 'ui-v2-123', createdAt: new Date(1995, 11, 16) }, locktest: { message: 'locktest', lockId: 'ui-v2-1337', createdAt: new Date(1995, 11, 17) }, }, - applications: {}, + appLocks: {}, + teamLocks: {}, distanceToUpstream: 0, priority: 2, }, @@ -136,6 +139,11 @@ describe('Test env locks', () => { it(testcase.name, () => { // given UpdateOverview.set({ + lighweightApps: [ + { + name: 'app1', + }, + ], environmentGroups: [ { environments: testcase.envs, @@ -172,7 +180,8 @@ describe('Test env locks', () => { { name: 'integration', locks: { locktest: { message: 'locktest', lockId: 'ui-v2-1337' } }, - applications: {}, + appLocks: {}, + teamLocks: {}, distanceToUpstream: 0, priority: 0, }, @@ -188,7 +197,8 @@ describe('Test env locks', () => { locks: { lockfoo: { message: 'lockfoo', lockId: 'ui-v2-123', createdAt: new Date(1995, 11, 16) }, }, - applications: {}, + appLocks: {}, + teamLocks: {}, distanceToUpstream: 0, priority: 0, }, @@ -198,7 +208,8 @@ describe('Test env locks', () => { locktest: { message: 'locktest', lockId: 'ui-v2-1337', createdAt: new Date(1995, 11, 17) }, lockbar: { message: 'lockbar', lockId: 'ui-v2-321', createdAt: new Date(1995, 11, 15) }, }, - applications: {}, + appLocks: {}, + teamLocks: {}, distanceToUpstream: 0, priority: 0, }, @@ -214,7 +225,8 @@ describe('Test env locks', () => { locks: { lockfoo: { message: 'lockfoo', lockId: 'ui-v2-123', createdAt: new Date(1995, 11, 16) }, }, - applications: {}, + appLocks: {}, + teamLocks: {}, distanceToUpstream: 0, priority: 0, }, @@ -224,7 +236,8 @@ describe('Test env locks', () => { lockbar: { message: 'lockbar', lockId: 'ui-v2-321', createdAt: new Date(1995, 11, 15) }, locktest: { message: 'locktest', lockId: 'ui-v2-1337', createdAt: new Date(1995, 11, 17) }, }, - applications: {}, + appLocks: {}, + teamLocks: {}, distanceToUpstream: 0, priority: 0, }, @@ -238,7 +251,6 @@ describe('Test env locks', () => { it(testcase.name, () => { // given UpdateOverview.set({ - // environments: testcase.envs, environmentGroups: [ { distanceToUpstream: 0, @@ -276,7 +288,8 @@ describe('Test env locks', () => { createdBy: { email: 'kuberpult@fdc.com', name: 'kuberpultUser' }, }, }, - applications: {}, + appLocks: {}, + teamLocks: {}, distanceToUpstream: 0, priority: 0, }, @@ -297,7 +310,6 @@ describe('Test env locks', () => { it(testcase.name, () => { // given UpdateOverview.set({ - applications: {}, environmentGroups: [ { environments: testcase.envs, @@ -319,6 +331,7 @@ describe('Test app locks', () => { interface dataAppT { name: string; envs: Environment[]; + OverviewApps: OverviewApplication[]; sortOrder: 'oldestToNewest' | 'newestToOldest'; expectedLockIDs: string[]; } @@ -327,28 +340,30 @@ describe('Test app locks', () => { { name: 'no locks', envs: [], + OverviewApps: [], sortOrder: 'oldestToNewest', expectedLockIDs: [], }, { name: 'get one lock', + OverviewApps: [ + { + name: 'foo', + team: '', + }, + ], envs: [ { name: 'integration', locks: {}, - distanceToUpstream: 0, - priority: 0, - applications: { + appLocks: { foo: { - name: 'foo', - version: 1337, - locks: { locktest: { message: 'locktest', lockId: 'ui-v2-1337' } }, - teamLocks: {}, - team: 'test-team', - queuedVersion: 0, - undeployVersion: true, + locks: [{ message: 'locktest', lockId: 'ui-v2-1337' }], }, }, + teamLocks: {}, + distanceToUpstream: 0, + priority: 0, }, ], sortOrder: 'oldestToNewest', @@ -356,31 +371,40 @@ describe('Test app locks', () => { }, { name: 'get a few locks (sorted, newestToOldest)', + OverviewApps: [ + { + name: 'foo', + team: '', + }, + ], envs: [ { name: 'integration', locks: {}, - distanceToUpstream: 0, - priority: 0, - applications: { + appLocks: { foo: { - name: 'foo', - version: 1337, - locks: { - locktest: { + locks: [ + { message: 'locktest', lockId: 'ui-v2-1337', createdAt: new Date(1995, 11, 17), }, - lockfoo: { message: 'lockfoo', lockId: 'ui-v2-123', createdAt: new Date(1995, 11, 16) }, - lockbar: { message: 'lockbar', lockId: 'ui-v2-321', createdAt: new Date(1995, 11, 15) }, - }, - teamLocks: {}, - team: 'test-team', - queuedVersion: 0, - undeployVersion: true, + { + message: 'lockfoo', + lockId: 'ui-v2-123', + createdAt: new Date(1995, 11, 16), + }, + { + message: 'lockbar', + lockId: 'ui-v2-321', + createdAt: new Date(1995, 11, 15), + }, + ], }, }, + teamLocks: {}, + distanceToUpstream: 0, + priority: 0, }, ], sortOrder: 'newestToOldest', @@ -388,6 +412,16 @@ describe('Test app locks', () => { }, { name: 'get a few locks (sorted, oldestToNewest)', + OverviewApps: [ + { + name: 'foo', + team: 'test-team', + }, + { + name: 'bar', + team: 'test-team', + }, + ], envs: [ { name: 'integration', @@ -396,37 +430,34 @@ describe('Test app locks', () => { locktest: { message: 'locktest', lockId: 'ui-v2-1337', createdAt: new Date(1995, 11, 17) }, lockbar: { message: 'lockbar', lockId: 'ui-v2-321', createdAt: new Date(1995, 11, 15) }, }, - distanceToUpstream: 0, - priority: 0, - applications: { + teamLocks: {}, + appLocks: { foo: { - name: 'foo', - version: 1337, - queuedVersion: 0, - undeployVersion: false, - locks: { - lockbar: { message: 'lockbar', lockId: 'ui-v2-321', createdAt: new Date(1995, 11, 15) }, - }, - teamLocks: {}, - team: 'test-team', + locks: [ + { + message: 'lockbar', + lockId: 'ui-v2-321', + createdAt: new Date(1995, 11, 15), + }, + ], }, bar: { - name: 'bar', - version: 420, - queuedVersion: 0, - undeployVersion: false, - locks: { - lockfoo: { message: 'lockfoo', lockId: 'ui-v2-123', createdAt: new Date(1995, 11, 16) }, - locktest: { + locks: [ + { + message: 'lockfoo', + lockId: 'ui-v2-123', + createdAt: new Date(1995, 11, 16), + }, + { message: 'locktest', lockId: 'ui-v2-1337', createdAt: new Date(1995, 11, 17), }, - }, - teamLocks: {}, - team: 'test-team', + ], }, }, + distanceToUpstream: 0, + priority: 0, }, ], sortOrder: 'oldestToNewest', @@ -439,6 +470,7 @@ describe('Test app locks', () => { // given // UpdateOverview.set({ environmentGroups: testcase.envs }); UpdateOverview.set({ + lightweightApps: testcase.OverviewApps, environmentGroups: [ { environments: testcase.envs, @@ -463,6 +495,7 @@ describe('Test Team locks', () => { envs: Environment[]; sortOrder: 'oldestToNewest' | 'newestToOldest'; expectedLockIDs: string[]; + OverviewApps: OverviewApplication[]; } const sampleAppData: dataAppT[] = [ @@ -471,26 +504,33 @@ describe('Test Team locks', () => { envs: [], sortOrder: 'oldestToNewest', expectedLockIDs: [], + OverviewApps: [], }, { name: 'get one lock', + OverviewApps: [ + { + name: 'bar', + team: 'lock-test', + }, + ], envs: [ { name: 'integration', locks: {}, - distanceToUpstream: 0, - priority: 0, - applications: { - foo: { - name: 'foo', - version: 1337, - locks: {}, - teamLocks: { locktest: { message: 'locktest', lockId: 'ui-v2-1337' } }, - team: 'test-team', - queuedVersion: 0, - undeployVersion: true, + teamLocks: { + 'lock-test': { + locks: [ + { + message: 'locktest', + lockId: 'ui-v2-1337', + }, + ], }, }, + appLocks: {}, + distanceToUpstream: 0, + priority: 0, }, ], sortOrder: 'oldestToNewest', @@ -498,31 +538,40 @@ describe('Test Team locks', () => { }, { name: 'get a few locks (sorted, newestToOldest)', + OverviewApps: [ + { + name: 'bar', + team: 'test-team', + }, + ], envs: [ { name: 'integration', locks: {}, - distanceToUpstream: 0, - priority: 0, - applications: { - foo: { - name: 'foo', - version: 1337, - locks: {}, - teamLocks: { - locktest: { + appLocks: {}, + teamLocks: { + 'test-team': { + locks: [ + { message: 'locktest', lockId: 'ui-v2-1337', createdAt: new Date(1995, 11, 17), }, - lockfoo: { message: 'lockfoo', lockId: 'ui-v2-123', createdAt: new Date(1995, 11, 16) }, - lockbar: { message: 'lockbar', lockId: 'ui-v2-321', createdAt: new Date(1995, 11, 15) }, - }, - team: 'test-team', - queuedVersion: 0, - undeployVersion: true, + { + message: 'lockfoo', + lockId: 'ui-v2-123', + createdAt: new Date(1995, 11, 16), + }, + { + message: 'lockbar', + lockId: 'ui-v2-321', + createdAt: new Date(1995, 11, 16), + }, + ], }, }, + distanceToUpstream: 0, + priority: 0, }, ], sortOrder: 'newestToOldest', @@ -530,6 +579,16 @@ describe('Test Team locks', () => { }, { name: 'get a few locks (sorted, oldestToNewest)', + OverviewApps: [ + { + name: 'foo', + team: 'test-team', + }, + { + name: 'bar', + team: 'test-team', + }, + ], envs: [ { name: 'integration', @@ -540,45 +599,40 @@ describe('Test Team locks', () => { }, distanceToUpstream: 0, priority: 0, - applications: { + appLocks: { foo: { - name: 'foo', - version: 1337, - queuedVersion: 0, - undeployVersion: false, - locks: { - lockbar: { message: 'lockbar', lockId: 'ui-v2-321', createdAt: new Date(1995, 11, 15) }, - }, - teamLocks: { - lockbar: { - message: 'team lock 1', - lockId: 'ui-v2-t-lock-1', + locks: [ + { + message: 'lockbar', + lockId: 'ui-v2-321', createdAt: new Date(1995, 11, 15), }, - }, - team: 'test-team', + ], }, bar: { - name: 'bar', - version: 420, - queuedVersion: 0, - undeployVersion: false, - locks: { - lockfoo: { message: 'lockfoo', lockId: 'ui-v2-123', createdAt: new Date(1995, 11, 16) }, - locktest: { - message: 'locktest', - lockId: 'ui-v2-1337', - createdAt: new Date(1995, 11, 17), + locks: [ + { + message: 'lockfoo', + lockId: 'ui-v2-123', + createdAt: new Date(1995, 11, 16), + }, + ], + }, + }, + teamLocks: { + 'test-team': { + locks: [ + { + message: 'team lock 1', + lockId: 'ui-v2-t-lock-1', + createdAt: new Date(1995, 11, 15), }, - }, - teamLocks: { - lockbar: { + { message: 'team lock 2', lockId: 'ui-v2-t-lock-2', createdAt: new Date(1995, 11, 15), }, - }, - team: 'test-team', + ], }, }, }, @@ -592,6 +646,7 @@ describe('Test Team locks', () => { it(testcase.name, () => { // given UpdateOverview.set({ + lightweightApps: testcase.OverviewApps, environmentGroups: [ { environments: testcase.envs, diff --git a/services/frontend-service/src/ui/Pages/Locks/LocksPage.tsx b/services/frontend-service/src/ui/Pages/Locks/LocksPage.tsx index 8ecd44f46..035912480 100644 --- a/services/frontend-service/src/ui/Pages/Locks/LocksPage.tsx +++ b/services/frontend-service/src/ui/Pages/Locks/LocksPage.tsx @@ -15,7 +15,14 @@ along with kuberpult. If not, see Copyright freiheit.com*/ import React, { useMemo } from 'react'; import { LocksTable } from '../../components/LocksTable/LocksTable'; -import { searchCustomFilter, sortLocks, useEnvironments, useGlobalLoadingState, useTeamLocks } from '../../utils/store'; +import { + searchCustomFilter, + sortLocks, + useApplications, + useEnvironments, + useGlobalLoadingState, + useTeamLocks, +} from '../../utils/store'; import { useSearchParams } from 'react-router-dom'; import { TopAppBar } from '../../components/TopAppBar/TopAppBar'; @@ -37,7 +44,8 @@ export const LocksPage: React.FC = () => { const [params] = useSearchParams(); const appNameParam = params.get('application'); const envs = useEnvironments(); - let teamLocks = useTeamLocks(); + const allApps = useApplications(); + let teamLocks = useTeamLocks(allApps); const envLocks = useMemo( () => sortLocks( @@ -60,22 +68,25 @@ export const LocksPage: React.FC = () => { teamLocks = useMemo(() => sortLocks(teamLocks, 'oldestToNewest'), [teamLocks]); + //Goes through all envs and all apps and checks for locks for each app const appLocks = useMemo( () => sortLocks( Object.values(envs) .map((env) => - Object.values(env.applications) + allApps .map((app) => - Object.values(app.locks).map((lock) => ({ - date: lock.createdAt, - environment: env.name, - application: app.name, - lockId: lock.lockId, - message: lock.message, - authorName: lock.createdBy?.name, - authorEmail: lock.createdBy?.email, - })) + env.appLocks[app.name] + ? env.appLocks[app.name].locks.map((lock) => ({ + date: lock.createdAt, + environment: env.name, + application: app.name, + lockId: lock.lockId, + message: lock.message, + authorName: lock.createdBy?.name, + authorEmail: lock.createdBy?.email, + })) + : [] ) .flat() ) @@ -83,8 +94,9 @@ export const LocksPage: React.FC = () => { .filter((lock) => searchCustomFilter(appNameParam, lock.application)), 'oldestToNewest' ), - [appNameParam, envs] + [appNameParam, envs, allApps] ); + const element = useGlobalLoadingState(); if (element) { return element; diff --git a/services/frontend-service/src/ui/components/ApplicationLockDisplay/ApplicationLockDisplay.test.tsx b/services/frontend-service/src/ui/components/ApplicationLockDisplay/ApplicationLockDisplay.test.tsx index dce35b3d0..a9aa8f3bb 100644 --- a/services/frontend-service/src/ui/components/ApplicationLockDisplay/ApplicationLockDisplay.test.tsx +++ b/services/frontend-service/src/ui/components/ApplicationLockDisplay/ApplicationLockDisplay.test.tsx @@ -15,7 +15,7 @@ along with kuberpult. If not, see Copyright freiheit.com*/ import { fireEvent, render } from '@testing-library/react'; -import { Environment, EnvironmentGroup, Environment_Application, Lock, Priority } from '../../../api/api'; +import { Application, Environment, EnvironmentGroup, Lock, Priority, UndeploySummary } from '../../../api/api'; import { ApplicationLockChip } from './ApplicationLockDisplay'; import { DisplayApplicationLock } from '../../utils/store'; import { Spy } from 'spy4js'; @@ -35,26 +35,27 @@ describe('ApplicationLockDisplay', () => { createdAt: new Date('2022-12-04T12:30:12'), createdBy: { name: 'test', email: 'test' }, }; - const testApp: Environment_Application = { + const testApp: Application = { name: 'test', - version: 1, - locks: { testlockId: testAppLock }, - queuedVersion: 0, - undeployVersion: false, - teamLocks: {}, team: 'test-team', + releases: [], + sourceRepoUrl: 'http://bar.com', + undeploySummary: UndeploySummary.NORMAL, + warnings: [], }; const testEnv1: Environment = { name: 'dev', - applications: { test: testApp }, locks: {}, + appLocks: {}, + teamLocks: {}, distanceToUpstream: 0, priority: Priority.UPSTREAM, }; const testEnv2: Environment = { name: 'staging', - applications: { test: testApp }, locks: {}, + appLocks: {}, + teamLocks: {}, distanceToUpstream: 0, priority: Priority.OTHER, }; @@ -76,7 +77,7 @@ describe('ApplicationLockDisplay', () => { displayLock: { environment: testEnv1, environmentGroup: testEnvGroup1, - application: testApp, + application: testApp.name, lock: { date: testAppLock.createdAt, environment: testEnv1.name, @@ -96,7 +97,7 @@ describe('ApplicationLockDisplay', () => { displayLock: { environment: testEnv2, environmentGroup: testEnvGroup2, - application: testApp, + application: testApp.name, lock: { date: testAppLock.createdAt, environment: testEnv2.name, diff --git a/services/frontend-service/src/ui/components/ApplicationLockDisplay/ApplicationLockDisplay.tsx b/services/frontend-service/src/ui/components/ApplicationLockDisplay/ApplicationLockDisplay.tsx index aad922c7d..117d06911 100644 --- a/services/frontend-service/src/ui/components/ApplicationLockDisplay/ApplicationLockDisplay.tsx +++ b/services/frontend-service/src/ui/components/ApplicationLockDisplay/ApplicationLockDisplay.tsx @@ -71,7 +71,7 @@ export const ApplicationLockChip = (props: DisplayApplicationLock): JSX.Element className="mdc-evolution-chip__cell mdc-evolution-chip__cell--primary mdc-evolution-chip__action--primary" role="gridcell"> - + {' '} {locks} diff --git a/services/frontend-service/src/ui/components/EnvironmentCard/EnvironmentCard.test.tsx b/services/frontend-service/src/ui/components/EnvironmentCard/EnvironmentCard.test.tsx index c743c31a8..d94a33f3c 100644 --- a/services/frontend-service/src/ui/components/EnvironmentCard/EnvironmentCard.test.tsx +++ b/services/frontend-service/src/ui/components/EnvironmentCard/EnvironmentCard.test.tsx @@ -64,7 +64,8 @@ describe('Test Environment Cards', () => { name: 'env1', distanceToUpstream: 2, locks: {}, - applications: {}, + appLocks: {}, + teamLocks: {}, priority: Priority.PRE_PROD, config: {}, }, @@ -87,7 +88,8 @@ describe('Test Environment Cards', () => { name: 'env1', distanceToUpstream: 2, locks: {}, - applications: {}, + appLocks: {}, + teamLocks: {}, priority: Priority.UPSTREAM, config: {}, }, @@ -95,7 +97,8 @@ describe('Test Environment Cards', () => { name: 'env2', distanceToUpstream: 2, locks: {}, - applications: {}, + appLocks: {}, + teamLocks: {}, priority: Priority.UPSTREAM, config: {}, }, diff --git a/services/frontend-service/src/ui/components/ProductVersion/ProductVersion.test.tsx b/services/frontend-service/src/ui/components/ProductVersion/ProductVersion.test.tsx index 4edc6e5d7..221129a77 100644 --- a/services/frontend-service/src/ui/components/ProductVersion/ProductVersion.test.tsx +++ b/services/frontend-service/src/ui/components/ProductVersion/ProductVersion.test.tsx @@ -62,7 +62,8 @@ const sampleEnvsA: Environment[] = [ { name: 'tester', locks: {}, - applications: {}, + appLocks: {}, + teamLocks: {}, distanceToUpstream: 0, priority: Priority.UPSTREAM, }, diff --git a/services/frontend-service/src/ui/components/ReleaseCard/ReleaseCard.test.tsx b/services/frontend-service/src/ui/components/ReleaseCard/ReleaseCard.test.tsx index c5c9ece79..11dc714da 100644 --- a/services/frontend-service/src/ui/components/ReleaseCard/ReleaseCard.test.tsx +++ b/services/frontend-service/src/ui/components/ReleaseCard/ReleaseCard.test.tsx @@ -199,19 +199,10 @@ describe('Release Card', () => { foo: { name: 'foo', locks: {}, + appLocks: {}, + teamLocks: {}, distanceToUpstream: 0, priority: 0, - applications: { - test2: { - version: 2, - queuedVersion: 0, - name: 'test2', - locks: {}, - teamLocks: {}, - team: 'test-team', - undeployVersion: false, - }, - }, }, }, }, @@ -270,19 +261,10 @@ describe('Release Card', () => { undeployed: { name: 'undeployed', locks: {}, + appLocks: {}, + teamLocks: {}, distanceToUpstream: 0, priority: 0, - applications: { - test2: { - version: 3, - queuedVersion: 0, - name: 'test2', - locks: {}, - teamLocks: {}, - team: 'test-team', - undeployVersion: false, - }, - }, }, }, }, @@ -340,21 +322,11 @@ describe('Release Card', () => { environments: { other: { locks: {}, - + appLocks: {}, + teamLocks: {}, distanceToUpstream: 0, priority: 0, name: 'other', - applications: { - test3: { - version: 3, - queuedVersion: 0, - name: 'test2', - locks: {}, - teamLocks: {}, - team: 'test-team', - undeployVersion: false, - }, - }, }, }, }, @@ -419,16 +391,6 @@ describe('Release Card', () => { mock_FormattedDate.FormattedDate.returns(
some formatted date
); // when UpdateOverview.set({ - applications: { - [testcase.props.app]: { - name: testcase.props.app, - releases: testcase.rels, - sourceRepoUrl: 'url', - undeploySummary: UndeploySummary.NORMAL, - team: 'no-team', - warnings: [], - }, - }, environmentGroups: [], }); updateAppDetails.set(testcase.appDetails); @@ -558,35 +520,17 @@ describe('Release Card Rollout Status', () => { environments: [ { name: 'development', - applications: { - test1: { - version: 2, - name: '', - locks: {}, - teamLocks: {}, - team: 'test-team', - queuedVersion: 0, - undeployVersion: false, - }, - }, locks: {}, + appLocks: {}, + teamLocks: {}, distanceToUpstream: 0, priority: Priority.OTHER, }, { name: 'development2', - applications: { - test1: { - version: 2, - name: '', - locks: {}, - teamLocks: {}, - team: 'test-team', - queuedVersion: 0, - undeployVersion: false, - }, - }, locks: {}, + appLocks: {}, + teamLocks: {}, distanceToUpstream: 0, priority: Priority.OTHER, }, @@ -599,18 +543,9 @@ describe('Release Card Rollout Status', () => { environments: [ { name: 'staging', - applications: { - test1: { - version: 2, - name: '', - locks: {}, - teamLocks: {}, - team: 'test-team', - queuedVersion: 0, - undeployVersion: false, - }, - }, locks: {}, + appLocks: {}, + teamLocks: {}, distanceToUpstream: 0, priority: Priority.OTHER, }, @@ -654,16 +589,6 @@ describe('Release Card Rollout Status', () => { mock_FormattedDate.FormattedDate.returns(
some formatted date
); // when UpdateOverview.set({ - applications: { - [testcase.props.app]: { - name: testcase.props.app, - releases: testcase.rels, - sourceRepoUrl: 'url', - undeploySummary: UndeploySummary.NORMAL, - team: 'no-team', - warnings: [], - }, - }, environmentGroups: testcase.environmentGroups, }); updateAppDetails.set(testcase.appDetails); diff --git a/services/frontend-service/src/ui/components/ReleaseCard/ReleaseCard.tsx b/services/frontend-service/src/ui/components/ReleaseCard/ReleaseCard.tsx index f72aaad06..279a974c7 100644 --- a/services/frontend-service/src/ui/components/ReleaseCard/ReleaseCard.tsx +++ b/services/frontend-service/src/ui/components/ReleaseCard/ReleaseCard.tsx @@ -21,6 +21,7 @@ import { useRolloutStatus, EnvironmentGroupExtended, useReleaseOrLog, + useAppDetailsForApp, } from '../../utils/store'; import { Tooltip } from '../tooltip/tooltip'; import { EnvironmentGroupChipList } from '../chip/EnvironmentGroupChip'; @@ -87,11 +88,12 @@ const useDeploymentStatus = ( app: string, deployedAt: EnvironmentGroupExtended[] ): [Array, RolloutStatus?] => { + const appDetails = useAppDetailsForApp(app); const rolloutEnvGroups = useRolloutStatus((getter) => { const groups: { [envGroup: string]: RolloutStatus } = {}; deployedAt.forEach((envGroup) => { const status = envGroup.environments.reduce((cur: RolloutStatus | undefined, env) => { - const appVersion: number | undefined = env.applications[app]?.version; + const appVersion = appDetails.deployments[env.name].version; const status = getter.getAppStatus(app, appVersion, env.name); if (cur === undefined) { return status; diff --git a/services/frontend-service/src/ui/components/ReleaseCardMini/ReleaseCardMini.test.tsx b/services/frontend-service/src/ui/components/ReleaseCardMini/ReleaseCardMini.test.tsx index da3d48107..ef6b255de 100644 --- a/services/frontend-service/src/ui/components/ReleaseCardMini/ReleaseCardMini.test.tsx +++ b/services/frontend-service/src/ui/components/ReleaseCardMini/ReleaseCardMini.test.tsx @@ -75,19 +75,10 @@ describe('Release Card Mini', () => { { name: 'other', locks: {}, + appLocks: {}, + teamLocks: {}, distanceToUpstream: 0, priority: 0, - applications: { - test2: { - version: 2, - queuedVersion: 0, - name: 'test2', - locks: {}, - teamLocks: {}, - team: 'test-team', - undeployVersion: false, - }, - }, }, ], expectedMessage: 'test2', @@ -101,19 +92,10 @@ describe('Release Card Mini', () => { { name: 'other', locks: {}, + appLocks: {}, + teamLocks: {}, distanceToUpstream: 0, priority: 0, - applications: { - test2: { - version: 2, - queuedVersion: 0, - name: 'test2', - locks: {}, - teamLocks: {}, - team: 'test-team', - undeployVersion: false, - }, - }, }, ], expectedMessage: 'Undeploy Version', @@ -127,16 +109,12 @@ describe('Release Card Mini', () => { mock_FormattedDate.FormattedDate.returns(
some formatted date
); // when UpdateOverview.set({ - applications: { - [testcase.props.app]: { - name: testcase.props.app, - releases: testcase.rels, - sourceRepoUrl: 'url', - undeploySummary: UndeploySummary.NORMAL, - team: 'no-team', - warnings: [], + lightweightApps: [ + { + name: 'test2', + team: 'example', }, - }, + ], environmentGroups: [ { environments: testcase.environments, @@ -158,7 +136,7 @@ describe('Release Card Mini', () => { warnings: [], }, deployments: { - test2: { + other: { version: 2, queuedVersion: 0, undeployVersion: false, diff --git a/services/frontend-service/src/ui/components/ReleaseDialog/ReleaseDialog.test.tsx b/services/frontend-service/src/ui/components/ReleaseDialog/ReleaseDialog.test.tsx index eb6ddef9b..46ddeac8a 100644 --- a/services/frontend-service/src/ui/components/ReleaseDialog/ReleaseDialog.test.tsx +++ b/services/frontend-service/src/ui/components/ReleaseDialog/ReleaseDialog.test.tsx @@ -94,17 +94,8 @@ describe('Release Dialog', () => { { name: 'prod', locks: {}, - applications: { - test1: { - name: 'test1', - version: 2, - locks: {}, - teamLocks: {}, - team: 'test-team', - queuedVersion: 0, - undeployVersion: false, - }, - }, + appLocks: {}, + teamLocks: {}, distanceToUpstream: 0, priority: Priority.UPSTREAM, }, @@ -148,17 +139,8 @@ describe('Release Dialog', () => { { name: 'prod', locks: {}, - applications: { - test1: { - name: 'test1', - version: 2, - locks: {}, - teamLocks: {}, - team: 'test-team', - queuedVersion: 0, - undeployVersion: false, - }, - }, + appLocks: {}, + teamLocks: {}, distanceToUpstream: 0, priority: Priority.UPSTREAM, }, @@ -241,17 +223,12 @@ describe('Release Dialog', () => { { name: 'prod', locks: { envLock: { message: 'envLock', lockId: 'ui-envlock' } }, - applications: { + appLocks: { test1: { - name: 'test1', - version: 2, - locks: { applock: { message: 'appLock', lockId: 'ui-applock' } }, - teamLocks: {}, - team: 'test-team', - queuedVersion: 0, - undeployVersion: false, + locks: [{ message: 'appLock', lockId: 'ui-applock' }], }, }, + teamLocks: {}, distanceToUpstream: 0, priority: Priority.UPSTREAM, }, @@ -332,18 +309,10 @@ describe('Release Dialog', () => { { name: 'prod', locks: { envLock: { message: 'envLock', lockId: 'ui-envlock' } }, - applications: { - test1: { - name: 'test1', - version: 2, - locks: { applock: { message: 'appLock', lockId: 'ui-applock' } }, - teamLocks: {}, - team: 'test-team', - queuedVersion: 0, - undeployVersion: false, - deploymentMetaData: { deployAuthor: 'test', deployTime: '1688467491' }, - }, + appLocks: { + test1: { locks: [{ message: 'appLock', lockId: 'ui-applock' }] }, }, + teamLocks: {}, distanceToUpstream: 0, priority: Priority.UPSTREAM, }, @@ -434,32 +403,26 @@ describe('Release Dialog', () => { { name: 'prod', locks: { envLock: { message: 'envLock', lockId: 'ui-envlock' } }, - applications: { + appLocks: { test1: { - name: 'test1', - version: 2, - locks: { applock: { message: 'appLock', lockId: 'ui-applock' } }, - teamLocks: {}, - team: 'test-team', - queuedVersion: 0, - undeployVersion: false, + locks: [{ message: 'appLock', lockId: 'ui-applock' }], }, }, + teamLocks: {}, distanceToUpstream: 0, priority: Priority.UPSTREAM, }, { name: 'dev', locks: { envLock: { message: 'envLock', lockId: 'ui-envlock' } }, - applications: { + appLocks: { test1: { - name: 'test1', - version: 3, - locks: { applock: { message: 'appLock', lockId: 'ui-applock' } }, - teamLocks: { teamLock: { message: 'teamLock', lockId: 'ui-teamlock' } }, - team: 'test-team', - queuedVersion: 666, - undeployVersion: false, + locks: [{ message: 'appLock', lockId: 'ui-applock' }], + }, + }, + teamLocks: { + test1: { + locks: [{ message: 'teamLock', lockId: 'ui-teamlock' }], }, }, distanceToUpstream: 0, @@ -583,16 +546,6 @@ describe('Release Dialog', () => { asMap[obj.name] = obj; }); UpdateOverview.set({ - applications: { - [testcase.props.app]: { - name: testcase.props.app, - releases: testcase.rels, - team: testcase.teamName, - sourceRepoUrl: 'url', - undeploySummary: UndeploySummary.NORMAL, - warnings: [], - }, - }, environmentGroups: [ { environmentGroupName: 'dev', diff --git a/services/frontend-service/src/ui/components/ReleaseDialog/ReleaseDialog.tsx b/services/frontend-service/src/ui/components/ReleaseDialog/ReleaseDialog.tsx index 2027b786e..eaa925556 100644 --- a/services/frontend-service/src/ui/components/ReleaseDialog/ReleaseDialog.tsx +++ b/services/frontend-service/src/ui/components/ReleaseDialog/ReleaseDialog.tsx @@ -15,11 +15,13 @@ along with kuberpult. If not, see Copyright freiheit.com*/ import classNames from 'classnames'; import React, { ReactElement, useCallback } from 'react'; -import { Environment, Environment_Application, EnvironmentGroup, Lock, LockBehavior, Release } from '../../../api/api'; +import { Deployment, Environment, EnvironmentGroup, Lock, LockBehavior, Release } from '../../../api/api'; import { addAction, getPriorityClassName, useAppDetailsForApp, + useApplications, + useAppLocks, useCloseReleaseDialog, useCurrentlyDeployedAtGroup, useEnvironmentGroups, @@ -27,6 +29,7 @@ import { useReleaseOptional, useRolloutStatus, useTeamFromApplication, + useTeamLocks, } from '../../utils/store'; import { Button } from '../button'; import { Close, Locks, SortAscending, SortDescending } from '../../../images'; @@ -105,14 +108,14 @@ export type EnvironmentListItemProps = { }; type CommitIdProps = { - application: Environment_Application; + deployment: Deployment; app: string; env: Environment; otherRelease?: Release; }; -const DeployedVersion: React.FC = ({ application, app, env, otherRelease }): ReactElement => { - if (!application || !otherRelease) { +const DeployedVersion: React.FC = ({ deployment, app, env, otherRelease }): ReactElement => { + if (!deployment || !otherRelease) { return ( "{app}" has no version deployed on "{env.name}" @@ -187,58 +190,31 @@ export const EnvironmentListItem: React.FC = ({ ); const otherRelease = useReleaseOptional(app, env); - const application = env.applications[app]; + const appDetails = useAppDetailsForApp(app); + const deployment = appDetails.deployments[env.name]; + const getDeploymentMetadata = (): [String, JSX.Element] => { - if (!application) { + if (!deployment) { return ['', <>]; } - if (application.deploymentMetaData === null) { + if (deployment.deploymentMetaData === null) { return ['', <>]; } - const deployedBy = application.deploymentMetaData?.deployAuthor ?? 'unknown'; - const deployedUNIX = application.deploymentMetaData?.deployTime ?? ''; + const deployedBy = deployment.deploymentMetaData?.deployAuthor ?? 'unknown'; + const deployedUNIX = deployment.deploymentMetaData?.deployTime ?? ''; if (deployedUNIX === '') { return ['Deployed by  ' + deployedBy, <>]; } - const deployedDate = new Date(+deployedUNIX * 1000); + const deployedDate = new Date(deployedUNIX); const returnString = 'Deployed by ' + deployedBy + ' '; const time = ; return [returnString, time]; }; - const appRolloutStatus = useRolloutStatus((getter) => getter.getAppStatus(app, application?.version, env.name)); - - const teamLocks = Object.values(env.applications) - .filter((application) => application.name === app) - .filter((app) => app.team === team) - .map((app) => - Object.values(app.teamLocks).map((lock) => ({ - date: lock.createdAt, - environment: env.name, - team: app.team, - lockId: lock.lockId, - message: lock.message, - authorName: lock.createdBy?.name, - authorEmail: lock.createdBy?.email, - })) - ) - .flat() - .filter((value, index, self) => index === self.findIndex((t) => t.lockId === value.lockId)); - - const appLocks = Object.values(env.applications) - .filter((application) => application.name === app) - .map((app) => - Object.values(app.locks).map((lock) => ({ - date: lock.createdAt, - environment: env.name, - team: app.team, - lockId: lock.lockId, - message: lock.message, - authorName: lock.createdBy?.name, - authorEmail: lock.createdBy?.email, - })) - ) - .flat(); + const appRolloutStatus = useRolloutStatus((getter) => getter.getAppStatus(app, deployment?.version, env.name)); + const apps = useApplications().filter((application) => application.name === app); + const teamLocks = useTeamLocks(apps); + const appLocks = useAppLocks(apps); const allowDeployment: boolean = ((): boolean => { if (release.isPrepublish) { @@ -251,7 +227,7 @@ export const EnvironmentListItem: React.FC = ({ })(); const releaseDifference = useReleaseDifference(release.version, app, env.name); const getReleaseDiffContent = (): JSX.Element => { - if (!otherRelease || !application) { + if (!otherRelease || !deployment) { return
; } if (releaseDifference > 0) { @@ -314,7 +290,7 @@ export const EnvironmentListItem: React.FC = ({ '. ' + (release.undeployVersion ? undeployTooltipExplanation : '') }> - + {queueInfo}
@@ -454,6 +430,7 @@ export const EnvironmentGroupLane: React.FC<{ const priorityClassName = getPriorityClassName(environmentGroup); const [isCollapsed, setIsCollapsed] = React.useState(false); const [allowGroupDeployment, setAllowGroupDeployment] = React.useState(true); + const appDetails = useAppDetailsForApp(app); const collapse = useCallback(() => { setIsCollapsed(!isCollapsed); }, [isCollapsed]); @@ -580,7 +557,9 @@ export const EnvironmentGroupLane: React.FC<{ release={release} team={team} className={priorityClassName} - queuedVersion={env.applications[app] ? env.applications[app].queuedVersion : 0} + queuedVersion={ + appDetails.deployments[env.name] ? appDetails.deployments[env.name].queuedVersion : 0 + } /> ))}
diff --git a/services/frontend-service/src/ui/components/ReleaseDialog/__snapshots__/ReleaseDialog.test.tsx.snap b/services/frontend-service/src/ui/components/ReleaseDialog/__snapshots__/ReleaseDialog.test.tsx.snap index 678ad7703..d58f09ee6 100644 --- a/services/frontend-service/src/ui/components/ReleaseDialog/__snapshots__/ReleaseDialog.test.tsx.snap +++ b/services/frontend-service/src/ui/components/ReleaseDialog/__snapshots__/ReleaseDialog.test.tsx.snap @@ -121,7 +121,6 @@ exports[`Release Dialog Renders the environment locks normal release 1`] = ` - - @@ -263,7 +239,6 @@ exports[`Release Dialog Renders the environment locks normal release 1`] = ` class="env-card-data" >
- Deployed by  unknown  
@@ -458,7 +433,6 @@ exports[`Release Dialog Renders the environment locks normal release with deploy -
- @@ -600,13 +551,9 @@ exports[`Release Dialog Renders the environment locks normal release with deploy class="env-card-data" >
- Deployed by test  
-
- some formatted date -
 
@@ -887,28 +834,6 @@ exports[`Release Dialog Renders the environment locks two envs release 1`] = `
-
- App: -
- -
-
@@ -1063,28 +988,6 @@ exports[`Release Dialog Renders the environment locks two envs release 1`] = `
-
- App: -
- -
-
diff --git a/services/frontend-service/src/ui/components/Releases/Releases.test.tsx b/services/frontend-service/src/ui/components/Releases/Releases.test.tsx index d06f8e8dd..f2a404fdd 100644 --- a/services/frontend-service/src/ui/components/Releases/Releases.test.tsx +++ b/services/frontend-service/src/ui/components/Releases/Releases.test.tsx @@ -19,12 +19,12 @@ import { updateAppDetails, UpdateOverview } from '../../utils/store'; import { Environment, EnvironmentGroup, - Environment_Application, Lock, Priority, Release, UndeploySummary, GetAppDetailsResponse, + OverviewApplication, } from '../../../api/api'; import { MemoryRouter } from 'react-router-dom'; @@ -33,6 +33,7 @@ describe('Release Dialog', () => { name: string; dates: number; releases: Release[]; + OverviewApps: OverviewApplication[]; envGroups: EnvironmentGroup[]; expectedAppLocksLength: number; appDetails: { [p: string]: GetAppDetailsResponse }; @@ -120,45 +121,30 @@ describe('Release Dialog', () => { }, }; - const testApp1: Environment_Application = { - name: 'test', - version: 1, - locks: { testlockId: testAppLock }, - queuedVersion: 0, - undeployVersion: false, - teamLocks: {}, - team: 'test-team', - }; - const testApp2: Environment_Application = { - name: 'test2', - version: 1, - locks: { testlockId2: testAppLock2 }, - queuedVersion: 0, - undeployVersion: false, - teamLocks: {}, - team: 'test-team', - }; - - const testApp3: Environment_Application = { - name: 'test', - version: 2, - locks: { testlockId3: testApplock3 }, - queuedVersion: 0, - undeployVersion: false, - teamLocks: {}, - team: 'test-team', - }; const testEnv1: Environment = { name: 'dev', - applications: { test: testApp1, test2: testApp2 }, locks: {}, + appLocks: { + test: { + locks: [testAppLock], + }, + test2: { + locks: [testAppLock2], + }, + }, + teamLocks: {}, distanceToUpstream: 0, priority: Priority.UPSTREAM, }; const testEnv2: Environment = { name: 'staging', - applications: { test: testApp3 }, locks: {}, + appLocks: { + test: { + locks: [testApplock3], + }, + }, + teamLocks: {}, distanceToUpstream: 0, priority: Priority.PROD, }; @@ -180,6 +166,12 @@ describe('Release Dialog', () => { appDetails: { test: app1Details, }, + OverviewApps: [ + { + name: app1Details.application?.name || '', + team: app1Details.application?.team || '', + }, + ], name: '3 releases in 3 days', dates: 3, releases: [ @@ -226,6 +218,12 @@ describe('Release Dialog', () => { { name: '3 releases in 2 days', dates: 2, + OverviewApps: [ + { + name: 'test', + team: 'example', + }, + ], appDetails: { test: { application: { @@ -322,6 +320,12 @@ describe('Release Dialog', () => { { name: 'two application locks without any release', dates: 0, + OverviewApps: [ + { + name: 'test', + team: 'example', + }, + ], appDetails: { test: { application: { @@ -347,7 +351,7 @@ describe('Release Dialog', () => { it(testcase.name, () => { // when UpdateOverview.set({ - applications: {}, + lightweightApps: testcase.OverviewApps, environmentGroups: testcase.envGroups, }); updateAppDetails.set(testcase.appDetails); diff --git a/services/frontend-service/src/ui/components/Releases/Releases.tsx b/services/frontend-service/src/ui/components/Releases/Releases.tsx index 4cfaccd9a..413ade1c4 100644 --- a/services/frontend-service/src/ui/components/Releases/Releases.tsx +++ b/services/frontend-service/src/ui/components/Releases/Releases.tsx @@ -53,6 +53,7 @@ export const Releases: React.FC = (props) => { const { app, className } = props; const releases = useAppDetailsForApp(app).application?.releases; const displayAppLocks = useDisplayApplicationLocks(app); + const rel = getReleasesForAppGroupByDate(releases); return (
diff --git a/services/frontend-service/src/ui/components/ServiceLane/ServiceLane.test.tsx b/services/frontend-service/src/ui/components/ServiceLane/ServiceLane.test.tsx index 0af9ae17b..c4cb3e66c 100644 --- a/services/frontend-service/src/ui/components/ServiceLane/ServiceLane.test.tsx +++ b/services/frontend-service/src/ui/components/ServiceLane/ServiceLane.test.tsx @@ -67,11 +67,7 @@ describe('Service Lane', () => { name: 'test2', team: '', }; - UpdateOverview.set({ - applications: { - test2: sampleApp, - }, - }); + UpdateOverview.set({}); updateAppDetails.set({ test2: { application: { @@ -140,37 +136,19 @@ const data: TestDataDiff[] = [ envs: [ { name: 'foo', - applications: { - test2: { - version: 1, - name: '', - locks: {}, - teamLocks: {}, - team: 'test-team', - queuedVersion: 0, - undeployVersion: false, - }, - }, distanceToUpstream: 0, priority: Priority.UPSTREAM, locks: {}, + appLocks: {}, + teamLocks: {}, }, { name: 'foo2', - applications: { - test2: { - version: 1, - name: '', - locks: {}, - teamLocks: {}, - team: 'test-team', - queuedVersion: 0, - undeployVersion: false, - }, - }, distanceToUpstream: 0, priority: Priority.UPSTREAM, locks: {}, + appLocks: {}, + teamLocks: {}, }, ], }, @@ -205,37 +183,19 @@ const data: TestDataDiff[] = [ envs: [ { name: 'foo', - applications: { - test2: { - version: 1, - name: '', - locks: {}, - teamLocks: {}, - team: 'test-team', - queuedVersion: 0, - undeployVersion: false, - }, - }, distanceToUpstream: 0, priority: Priority.UPSTREAM, locks: {}, + appLocks: {}, + teamLocks: {}, }, { name: 'foo2', - applications: { - test2: { - version: 2, - name: '', - locks: {}, - teamLocks: {}, - team: 'test-team', - queuedVersion: 0, - undeployVersion: false, - }, - }, distanceToUpstream: 0, priority: Priority.UPSTREAM, locks: {}, + appLocks: {}, + teamLocks: {}, }, ], }, @@ -270,35 +230,17 @@ const data: TestDataDiff[] = [ envs: [ { name: 'foo', - applications: { - test2: { - name: 'test2', - version: 1, - locks: {}, - teamLocks: {}, - team: 'test-team', - queuedVersion: 0, - undeployVersion: false, - }, - }, locks: {}, + appLocks: {}, + teamLocks: {}, distanceToUpstream: 0, priority: Priority.UPSTREAM, }, { name: 'foo2', - applications: { - test2: { - name: 'test2', - version: 4, - locks: {}, - teamLocks: {}, - team: 'test-team', - queuedVersion: 0, - undeployVersion: false, - }, - }, locks: {}, + appLocks: {}, + teamLocks: {}, distanceToUpstream: 0, priority: Priority.UPSTREAM, }, @@ -335,37 +277,19 @@ const data: TestDataDiff[] = [ envs: [ { name: 'foo', - applications: { - test2: { - version: 2, - name: '', - locks: {}, - teamLocks: {}, - team: 'test-team', - queuedVersion: 0, - undeployVersion: false, - }, - }, distanceToUpstream: 0, priority: Priority.UPSTREAM, locks: {}, + appLocks: {}, + teamLocks: {}, }, { name: 'foo2', - applications: { - test2: { - version: 5, - name: '', - locks: {}, - teamLocks: {}, - team: 'test-team', - queuedVersion: 0, - undeployVersion: false, - }, - }, distanceToUpstream: 0, priority: Priority.UPSTREAM, locks: {}, + appLocks: {}, + teamLocks: {}, }, ], }, @@ -381,7 +305,6 @@ describe('Service Lane Diff', () => { describe.each(data)('Service Lane diff number', (testcase) => { it(testcase.name, () => { UpdateOverview.set({ - applications: {}, environmentGroups: [ { environments: testcase.envs, @@ -504,28 +427,16 @@ describe('Service Lane Important Releases', () => { team: 'test2', }; UpdateOverview.set({ - applications: { - test2: sampleApp, - }, environmentGroups: [ { environments: [ { name: 'foo', - applications: { - test2: { - name: 'test2', - version: testcase.currentlyDeployedVersion, - locks: {}, - teamLocks: {}, - team: 'test-team', - undeployVersion: false, - queuedVersion: 0, - }, - }, distanceToUpstream: 0, priority: Priority.UPSTREAM, locks: {}, + appLocks: {}, + teamLocks: {}, }, ], environmentGroupName: 'group1', @@ -601,10 +512,11 @@ const dataUndeploy: TestDataUndeploy[] = (() => { envs: [ { name: 'foo2', - applications: {}, distanceToUpstream: 0, priority: Priority.UPSTREAM, locks: {}, + appLocks: {}, + teamLocks: {}, }, ], expectedUndeployButton: '⋮', @@ -628,10 +540,11 @@ const dataUndeploy: TestDataUndeploy[] = (() => { envs: [ { name: 'foo2', - applications: {}, distanceToUpstream: 0, priority: Priority.UPSTREAM, locks: {}, + appLocks: {}, + teamLocks: {}, }, ], expectedUndeployButton: '⋮', @@ -658,9 +571,6 @@ describe('Service Lane ⋮ menu', () => { mock_addAction.addAction.returns(undefined); UpdateOverview.set({ - applications: { - test1: testcase.renderedApp, - }, environmentGroups: [ { environments: testcase.envs, @@ -804,7 +714,6 @@ const dataAppLockSummary: TestDataAppLockSummary[] = (() => { envs: [ { name: 'foo2', - applications: {}, distanceToUpstream: 0, priority: Priority.UPSTREAM, locks: { @@ -813,6 +722,8 @@ const dataAppLockSummary: TestDataAppLockSummary[] = (() => { lockId: '487329463874223', }, }, + appLocks: {}, + teamLocks: {}, }, ], expected: undefined, @@ -823,10 +734,11 @@ const dataAppLockSummary: TestDataAppLockSummary[] = (() => { envs: [ { name: 'foo2', - applications: {}, distanceToUpstream: 0, priority: Priority.UPSTREAM, locks: {}, + appLocks: {}, + teamLocks: {}, }, ], expected: '"test1" has 1 lock. Click on a tile to see details.', @@ -837,10 +749,11 @@ const dataAppLockSummary: TestDataAppLockSummary[] = (() => { envs: [ { name: 'foo2', - applications: {}, distanceToUpstream: 0, priority: Priority.UPSTREAM, locks: {}, + appLocks: {}, + teamLocks: {}, }, ], expected: '"test1" has 2 locks. Click on a tile to see details.', @@ -851,10 +764,11 @@ const dataAppLockSummary: TestDataAppLockSummary[] = (() => { envs: [ { name: 'foo2', - applications: {}, distanceToUpstream: 0, priority: Priority.UPSTREAM, locks: {}, + appLocks: {}, + teamLocks: {}, }, ], expected: '"test1" has 1 lock. Click on a tile to see details.', @@ -865,10 +779,11 @@ const dataAppLockSummary: TestDataAppLockSummary[] = (() => { envs: [ { name: 'foo2', - applications: {}, distanceToUpstream: 0, priority: Priority.UPSTREAM, locks: {}, + appLocks: {}, + teamLocks: {}, }, ], expected: '"test1" has 2 locks. Click on a tile to see details.', diff --git a/services/frontend-service/src/ui/components/chip/EnvironmentGroupChip.test.tsx b/services/frontend-service/src/ui/components/chip/EnvironmentGroupChip.test.tsx index 50f9b83ff..eb3b9fecd 100644 --- a/services/frontend-service/src/ui/components/chip/EnvironmentGroupChip.test.tsx +++ b/services/frontend-service/src/ui/components/chip/EnvironmentGroupChip.test.tsx @@ -32,7 +32,8 @@ describe('EnvironmentChip', () => { distanceToUpstream: 0, priority: Priority.PROD, locks: {}, - applications: {}, + appLocks: {}, + teamLocks: {}, }; const envGroup: EnvironmentGroup = { distanceToUpstream: 0, @@ -150,9 +151,10 @@ const envGroupPairFromPrios = ( envGroupPrio: Priority ): { env: Environment; envGroup: EnvironmentGroup } => { const env: Environment = { - applications: {}, distanceToUpstream: -1, // shouldn't matter, if this value is used an error will be thrown locks: {}, + appLocks: {}, + teamLocks: {}, name: 'Test me', priority: envPrio, }; @@ -236,7 +238,8 @@ const envFromPrio = (prio: Priority): Environment => ({ distanceToUpstream: 0, priority: prio, locks: {}, - applications: {}, + appLocks: {}, + teamLocks: {}, }); const envGroupChipData: Array = [ diff --git a/services/frontend-service/src/ui/utils/store.test.tsx b/services/frontend-service/src/ui/utils/store.test.tsx index 56cc86314..08aacb6d5 100644 --- a/services/frontend-service/src/ui/utils/store.test.tsx +++ b/services/frontend-service/src/ui/utils/store.test.tsx @@ -40,6 +40,7 @@ import { GetAppDetailsResponse, GetOverviewResponse, LockBehavior, + OverviewApplication, Priority, ReleaseTrainRequest_TargetType, RolloutStatus, @@ -56,6 +57,7 @@ describe('Test useLocksSimilarTo', () => { inputEnvGroups: EnvironmentGroup[]; // this just defines what locks generally exist inputAction: BatchAction; // the action we are rendering currently in the sidebar expectedLocks: AllLocks; + OverviewApps: OverviewApplication[]; }; const testdata: TestDataStore[] = [ @@ -70,6 +72,7 @@ describe('Test useLocksSimilarTo', () => { }, }, }, + OverviewApps: [], inputEnvGroups: [], expectedLocks: { appLocks: [], @@ -88,6 +91,7 @@ describe('Test useLocksSimilarTo', () => { }, }, }, + OverviewApps: [], inputEnvGroups: [ { environments: [ @@ -98,7 +102,8 @@ describe('Test useLocksSimilarTo', () => { locks: { l1: makeLock({ lockId: 'l1' }), }, - applications: {}, + appLocks: {}, + teamLocks: {}, }, ], environmentGroupName: 'group1', @@ -123,6 +128,7 @@ describe('Test useLocksSimilarTo', () => { }, }, }, + OverviewApps: [], inputEnvGroups: [ { environments: [ @@ -133,7 +139,8 @@ describe('Test useLocksSimilarTo', () => { locks: { l1: makeLock({ lockId: 'l1' }), }, - applications: {}, + appLocks: {}, + teamLocks: {}, }, { name: 'staging', @@ -142,7 +149,8 @@ describe('Test useLocksSimilarTo', () => { locks: { l1: makeLock({ lockId: 'l1' }), }, - applications: {}, + appLocks: {}, + teamLocks: {}, }, ], environmentGroupName: 'group1', @@ -172,6 +180,7 @@ describe('Test useLocksSimilarTo', () => { }, }, }, + OverviewApps: [{ name: 'betty', team: '' }], inputEnvGroups: [ { environments: [ @@ -182,18 +191,10 @@ describe('Test useLocksSimilarTo', () => { locks: { l1: makeLock({ lockId: 'l1' }), }, - applications: { - app1: { - name: 'betty', - locks: { - l1: makeLock({ lockId: 'l1' }), - }, - version: 666, - teamLocks: {}, - team: 'test-team', - undeployVersion: false, - queuedVersion: 0, - argoCd: undefined, + teamLocks: {}, + appLocks: { + betty: { + locks: [makeLock({ lockId: 'l1' })], }, }, }, @@ -228,6 +229,12 @@ describe('Test useLocksSimilarTo', () => { }, }, }, + OverviewApps: [ + { + name: 'betty', + team: 'test-team', + }, + ], inputEnvGroups: [ { environments: [ @@ -238,18 +245,14 @@ describe('Test useLocksSimilarTo', () => { locks: { l1: makeLock({ lockId: 'l1' }), }, - applications: { - app1: { - name: 'betty', - locks: { - l1: makeLock({ lockId: 'l1' }), - }, - teamLocks: { l1: makeLock({ lockId: 'l1' }) }, - team: 'test-team', - version: 666, - undeployVersion: false, - queuedVersion: 0, - argoCd: undefined, + appLocks: { + betty: { + locks: [makeLock({ lockId: 'l1' })], + }, + }, + teamLocks: { + 'test-team': { + locks: [makeLock({ lockId: 'l1' })], }, }, }, @@ -260,7 +263,8 @@ describe('Test useLocksSimilarTo', () => { locks: { l1: makeLock({ lockId: 'l1' }), }, - applications: {}, + appLocks: {}, + teamLocks: {}, }, ], environmentGroupName: 'group1', @@ -306,7 +310,7 @@ describe('Test useLocksSimilarTo', () => { // given updateActions([]); UpdateOverview.set({ - applications: {}, + lightweightApps: testcase.OverviewApps, environmentGroups: testcase.inputEnvGroups, }); // when @@ -797,6 +801,7 @@ describe('Test useLocksConflictingWithActions', () => { expectedAppLocks: DisplayLock[]; expectedEnvLocks: DisplayLock[]; environments: Environment[]; + OverviewApps: OverviewApplication[]; }; const testdata: TestDataStore[] = [ @@ -806,6 +811,7 @@ describe('Test useLocksConflictingWithActions', () => { expectedAppLocks: [], expectedEnvLocks: [], environments: [], + OverviewApps: [], }, { name: 'deploy action and related app lock and env lock', @@ -823,6 +829,12 @@ describe('Test useLocksConflictingWithActions', () => { }, }, ], + OverviewApps: [ + { + name: 'app1', + team: '', + }, + ], environments: [ { name: 'dev', @@ -832,22 +844,17 @@ describe('Test useLocksConflictingWithActions', () => { lockId: 'my-env-lock1', }), }, - applications: { - echo: { - name: 'app1', - version: 0, - locks: { - applock: makeLock({ + appLocks: { + app1: { + locks: [ + makeLock({ lockId: 'app-lock-id', message: 'i do not like this app', }), - }, - queuedVersion: 0, - undeployVersion: false, - teamLocks: {}, - team: 'test-team', + ], }, }, + teamLocks: {}, distanceToUpstream: 0, priority: 0, }, @@ -870,6 +877,12 @@ describe('Test useLocksConflictingWithActions', () => { }, { name: 'deploy action and unrelated locks', + OverviewApps: [ + { + name: 'anotherapp', + team: '', + }, + ], actions: [ { action: { @@ -893,22 +906,17 @@ describe('Test useLocksConflictingWithActions', () => { lockId: 'my-env-lock1', }), }, - applications: { - echo: { - name: 'anotherapp', // this lock differs by app - version: 0, - locks: { - applock: makeLock({ + appLocks: { + anotherapp: { + locks: [ + makeLock({ lockId: 'app-lock-id', message: 'i do not like this app', }), - }, - teamLocks: {}, - team: 'test-team', - queuedVersion: 0, - undeployVersion: false, + ], }, }, + teamLocks: {}, distanceToUpstream: 0, priority: 0, }, @@ -923,7 +931,7 @@ describe('Test useLocksConflictingWithActions', () => { // given updateActions(testcase.actions); UpdateOverview.set({ - applications: {}, + lightweightApps: testcase.OverviewApps, environmentGroups: [ { environmentGroupName: 'g1', @@ -1101,7 +1109,6 @@ describe('Test Calculate Release Difference', () => { name: 'app does not exist in the app Details', inputAppDetails: {}, inputOverview: { - applications: {}, environmentGroups: [ { environmentGroupName: 'test', @@ -1109,7 +1116,8 @@ describe('Test Calculate Release Difference', () => { { name: envName, locks: {}, - applications: {}, + teamLocks: {}, + appLocks: {}, distanceToUpstream: 0, priority: Priority.PROD, }, @@ -1183,7 +1191,6 @@ describe('Test Calculate Release Difference', () => { }, }, inputOverview: { - applications: {}, environmentGroups: [ { environmentGroupName: 'test', @@ -1191,17 +1198,8 @@ describe('Test Calculate Release Difference', () => { { name: 'exampleEnv', locks: {}, - applications: { - exampleApp: { - name: appName, - version: 12, - locks: {}, - queuedVersion: 0, - undeployVersion: false, - teamLocks: {}, - team: '', - }, - }, + teamLocks: {}, + appLocks: {}, distanceToUpstream: 0, priority: Priority.PROD, }, @@ -1281,7 +1279,6 @@ describe('Test Calculate Release Difference', () => { }, }, inputOverview: { - applications: {}, environmentGroups: [ { environmentGroupName: 'test', @@ -1289,17 +1286,8 @@ describe('Test Calculate Release Difference', () => { { name: envName, locks: {}, - applications: { - [appName]: { - name: appName, - version: 10, - locks: {}, - queuedVersion: 0, - undeployVersion: false, - teamLocks: {}, - team: '', - }, - }, + teamLocks: {}, + appLocks: {}, distanceToUpstream: 0, priority: Priority.PROD, }, @@ -1370,7 +1358,6 @@ describe('Test Calculate Release Difference', () => { }, }, inputOverview: { - applications: {}, environmentGroups: [ { environmentGroupName: 'test', @@ -1378,17 +1365,8 @@ describe('Test Calculate Release Difference', () => { { name: envName, locks: {}, - applications: { - [appName]: { - name: appName, - version: 12, - locks: {}, - queuedVersion: 0, - undeployVersion: false, - teamLocks: {}, - team: '', - }, - }, + teamLocks: {}, + appLocks: {}, distanceToUpstream: 0, priority: Priority.PROD, }, @@ -1457,7 +1435,6 @@ describe('Test Calculate Release Difference', () => { }, }, inputOverview: { - applications: {}, environmentGroups: [ { environmentGroupName: 'test', @@ -1465,17 +1442,8 @@ describe('Test Calculate Release Difference', () => { { name: envName, locks: {}, - applications: { - [appName]: { - name: appName, - version: 11, - locks: {}, - queuedVersion: 0, - undeployVersion: false, - teamLocks: {}, - team: '', - }, - }, + appLocks: {}, + teamLocks: {}, distanceToUpstream: 0, priority: Priority.PROD, }, diff --git a/services/frontend-service/src/ui/utils/store.tsx b/services/frontend-service/src/ui/utils/store.tsx index b84b02075..58b17e968 100644 --- a/services/frontend-service/src/ui/utils/store.tsx +++ b/services/frontend-service/src/ui/utils/store.tsx @@ -31,7 +31,6 @@ import { GetEnvironmentConfigResponse, GetReleaseTrainPrognosisResponse, GetFailedEslsResponse, - Environment_Application, GetAppDetailsResponse, OverviewApplication, } from '../../api/api'; @@ -59,12 +58,16 @@ export interface DisplayLock { } export const displayLockUniqueId = (displayLock: DisplayLock): string => - 'dl-' + displayLock.lockId + '-' + displayLock.environment + '-' + displayLock.application; + 'dl-' + + displayLock.lockId + + '-' + + displayLock.environment + + '-' + + (displayLock.application ? displayLock.application : displayLock.team); type EnhancedOverview = GetOverviewResponse & { [key: string]: unknown; loaded: boolean }; const emptyOverview: EnhancedOverview = { - applications: {}, lightweightApps: [], environmentGroups: [], gitRevision: '', @@ -469,6 +472,7 @@ export const deleteAction = (action: BatchAction): void => { UpdateAction.set(({ actions }) => ({ // create comparison function actions: actions.filter((act) => JSON.stringify(act).localeCompare(JSON.stringify(action))), + //actions: [], })); }; // returns all application names @@ -521,20 +525,22 @@ export const useEnvironments = (): Environment[] => */ export const useEnvironmentNames = (): string[] => useEnvironments().map((env) => env.name); -export const useTeamLocks = (): DisplayLock[] => +export const useTeamLocks = (allApps: OverviewApplication[]): DisplayLock[] => Object.values(useEnvironments()) .map((env) => - Object.values(env.applications) + allApps .map((app) => - Object.values(app.teamLocks).map((lock) => ({ - date: lock.createdAt, - environment: env.name, - team: app.team, - lockId: lock.lockId, - message: lock.message, - authorName: lock.createdBy?.name, - authorEmail: lock.createdBy?.email, - })) + env.teamLocks[app.team] + ? env.teamLocks[app.team].locks.map((lock) => ({ + date: lock.createdAt, + environment: env.name, + team: app.team, + lockId: lock.lockId, + message: lock.message, + authorName: lock.createdBy?.name, + authorEmail: lock.createdBy?.email, + })) + : [] ) .flat() ) @@ -547,6 +553,27 @@ export const useTeamLocks = (): DisplayLock[] => t.lockId === value.lockId && t.team === value.team && t.environment === value.environment ) ); + +export const useAppLocks = (allApps: OverviewApplication[]): DisplayLock[] => + Object.values(useEnvironments()) + .map((env) => + allApps + .map((app) => + env.appLocks[app.name] + ? env.appLocks[app.name].locks.map((lock) => ({ + date: lock.createdAt, + environment: env.name, + application: app.name, + lockId: lock.lockId, + message: lock.message, + authorName: lock.createdBy?.name, + authorEmail: lock.createdBy?.email, + })) + : [] + ) + .flat() + ) + .flat(); /** * returns the classname according to the priority of an environment, used to color environments */ @@ -596,66 +623,38 @@ export const useApplicationsFilteredAndSorted = ( return applicationsSortedByTeam(allMatchingTeamAndWarningsAndName); }; -// return all applications locks -export const useFilteredApplicationLocks = (appNameParam: string | null): DisplayLock[] => { - const finalLocks: DisplayLock[] = []; - Object.values(useEnvironments()) - .map((environment) => ({ envName: environment.name, apps: environment.applications })) - .forEach((app) => { - Object.values(app.apps) - .map((myApp) => ({ environment: app.envName, appName: myApp.name, locks: myApp.locks })) - .forEach((lock) => { - Object.values(lock.locks).forEach((cena) => - finalLocks.push({ - date: cena.createdAt, - application: lock.appName, - environment: lock.environment, - lockId: cena.lockId, - message: cena.message, - authorName: cena.createdBy?.name, - authorEmail: cena.createdBy?.email, - }) - ); - }); - }); - const filteredLocks = finalLocks.filter((val) => appNameParam === val.application); - return sortLocks(filteredLocks, 'newestToOldest'); -}; - export interface DisplayApplicationLock { lock: DisplayLock; - application: Environment_Application; + application: string; environment: Environment; environmentGroup: EnvironmentGroup; } -export const useDisplayApplicationLocks = (appName: string | null): DisplayApplicationLock[] => { +export const useDisplayApplicationLocks = (appName: string): DisplayApplicationLock[] => { const envGroups = useEnvironmentGroups(); const finalLocks = useMemo(() => { const finalLocks: DisplayApplicationLock[] = []; Object.values(envGroups).forEach((envGroup) => { - Object.values(envGroup.environments).forEach((env) => { - Object.values(env.applications).forEach((app) => { - if (appName && appName === app.name) { - Object.values(app.locks).forEach((lock) => - finalLocks.push({ - lock: { - date: lock.createdAt, - application: app.name, - environment: env.name, - lockId: lock.lockId, - message: lock.message, - authorName: lock.createdBy?.name, - authorEmail: lock.createdBy?.email, - }, - application: app, - environment: env, - environmentGroup: envGroup, - }) - ); - } - }); - }); + Object.values(envGroup.environments).forEach((env) => + env.appLocks[appName] + ? Object.values(env.appLocks[appName].locks).forEach((lock) => { + finalLocks.push({ + lock: { + date: lock.createdAt, + application: appName, + environment: env.name, + lockId: lock.lockId, + message: lock.message, + authorName: lock.createdBy?.name, + authorEmail: lock.createdBy?.email, + }, + application: appName, + environment: env, + environmentGroup: envGroup, + }); + }) + : [] + ); }); finalLocks.sort((a: DisplayApplicationLock, b: DisplayApplicationLock) => { if ((a.lock.date ?? new Date(0)) < (b.lock.date ?? new Date(0))) return 1; @@ -764,46 +763,12 @@ export type AllLocks = { teamLocks: DisplayLock[]; }; -export const useTeamLocksFilterByTeam = (team: string): DisplayLock[] => { - const envs = useEnvironments(); - const teamLocks: DisplayLock[] = []; - envs.forEach((env: Environment) => { - for (const applicationsKey in env.applications) { - const app = env.applications[applicationsKey]; - if (team === app.team) { - for (const locksKey in app.teamLocks) { - const lock = app.teamLocks[locksKey]; - const displayLock: DisplayLock = { - lockId: lock.lockId, - team: app.team, - date: lock.createdAt, - environment: env.name, - message: lock.message, - authorName: lock.createdBy?.name, - authorEmail: lock.createdBy?.email, - }; - if ( - !teamLocks.some( - (e) => - e.lockId === displayLock.lockId && - e.team === displayLock.team && - e.environment === displayLock.environment - ) - ) { - teamLocks.push(displayLock); - } - } - } - } - }); - return teamLocks; -}; - export const useAllLocks = (): AllLocks => { const envs = useEnvironments(); + const allApps = useApplications(); + const teamLocks = useTeamLocks(allApps); const environmentLocks: DisplayLock[] = []; - const appLocks: DisplayLock[] = []; - const teamLocks: DisplayLock[] = []; + const appLocks = useAppLocks(allApps); envs.forEach((env: Environment) => { for (const locksKey in env.locks) { const lock = env.locks[locksKey]; @@ -817,44 +782,6 @@ export const useAllLocks = (): AllLocks => { }; environmentLocks.push(displayLock); } - for (const applicationsKey in env.applications) { - const app = env.applications[applicationsKey]; - for (const locksKey in app.locks) { - const lock = app.locks[locksKey]; - const displayLock: DisplayLock = { - lockId: lock.lockId, - application: app.name, - date: lock.createdAt, - environment: env.name, - message: lock.message, - authorName: lock.createdBy?.name, - authorEmail: lock.createdBy?.email, - }; - appLocks.push(displayLock); - } - for (const locksKey in app.teamLocks) { - const lock = app.teamLocks[locksKey]; - const displayLock: DisplayLock = { - lockId: lock.lockId, - team: app.team, - date: lock.createdAt, - environment: env.name, - message: lock.message, - authorName: lock.createdBy?.name, - authorEmail: lock.createdBy?.email, - }; - if ( - !teamLocks.some( - (l) => - l.lockId === displayLock.lockId && - l.environment === displayLock.environment && - l.team === displayLock.team - ) // 2 Team locks that don't have the same environment or team might, in theory, have the same lock ID, so the lock id does not uniquely identify a lock, but the combination of env + team + ID should. - ) { - teamLocks.push(displayLock); - } - } - } }); return { environmentLocks, @@ -903,7 +830,6 @@ const extractDeleteActionData = (batchAction: BatchAction): DeleteActionData | u export const useLocksSimilarTo = (cartItemAction: BatchAction | undefined): AllLocks => { const allLocks = useAllLocks(); const actions = useActions(); - if (!cartItemAction) { return { appLocks: [], environmentLocks: [], teamLocks: [] }; } @@ -1013,18 +939,6 @@ export const useReleaseOptional = (application: string, env: Environment): Relea return appDetails.application?.releases.find((r) => r.version === deployment.version); }; -// returns the release versions that are currently deployed to at least one environment -export const useDeployedReleases = (application: string): number[] => - [ - ...new Set( - Object.values(useEnvironments()) - .filter((env) => env.applications[application]) - .map((env) => env.applications[application].version) - ), - ] - .sort((a, b) => b - a) - .filter((version) => version !== 0); // 0 means "not deployed", so we filter those out - export type EnvironmentGroupExtended = EnvironmentGroup & { numberOfEnvsInGroup: number }; /** @@ -1032,11 +946,15 @@ export type EnvironmentGroupExtended = EnvironmentGroup & { numberOfEnvsInGroup: */ export const useCurrentlyDeployedAtGroup = (application: string, version: number): EnvironmentGroupExtended[] => { const environmentGroups: EnvironmentGroup[] = useEnvironmentGroups(); + const appDetails = useAppDetailsForApp(application); return useMemo(() => { const envGroups: EnvironmentGroupExtended[] = []; environmentGroups.forEach((group: EnvironmentGroup) => { const envs = group.environments.filter( - (env) => env.applications[application] && env.applications[application].version === version + (env) => + appDetails && + appDetails.deployments[env.name] && + appDetails.deployments[env.name].version === version ); if (envs.length > 0) { // we need to make a copy of the group here, because we want to remove some envs. @@ -1052,7 +970,7 @@ export const useCurrentlyDeployedAtGroup = (application: string, version: number } }); return envGroups; - }, [environmentGroups, application, version]); + }, [environmentGroups, version, appDetails]); }; /** @@ -1060,10 +978,11 @@ export const useCurrentlyDeployedAtGroup = (application: string, version: number */ export const useCurrentlyExistsAtGroup = (application: string): EnvironmentGroupExtended[] => { const environmentGroups: EnvironmentGroup[] = useEnvironmentGroups(); + const appDetails = useAppDetailsForApp(application); return useMemo(() => { const envGroups: EnvironmentGroupExtended[] = []; environmentGroups.forEach((group: EnvironmentGroup) => { - const envs = group.environments.filter((env) => env.applications[application]); + const envs = group.environments.filter((env) => appDetails.deployments[env.name]); if (envs.length > 0) { // we need to make a copy of the group here, because we want to remove some envs. // but that should not have any effect on the group saved in the store. @@ -1078,7 +997,7 @@ export const useCurrentlyExistsAtGroup = (application: string): EnvironmentGroup } }); return envGroups; - }, [environmentGroups, application]); + }, [environmentGroups, appDetails]); }; // Get all releases for an app diff --git a/services/rollout-service/pkg/argo/argo.go b/services/rollout-service/pkg/argo/argo.go index f89f3bad0..36ccdcc5d 100644 --- a/services/rollout-service/pkg/argo/argo.go +++ b/services/rollout-service/pkg/argo/argo.go @@ -73,13 +73,6 @@ func New(appClient application.ApplicationServiceClient, manageArgoApplicationEn } } -type Key struct { - AppName string - EnvName string - Application *api.Environment_Application - Environment *api.Environment -} - func (a *ArgoAppProcessor) GetManageArgoAppsFilter() []string { return a.ManageArgoAppsFilter } diff --git a/services/rollout-service/pkg/argo/argo_test.go b/services/rollout-service/pkg/argo/argo_test.go index 7fc30552a..51378562b 100644 --- a/services/rollout-service/pkg/argo/argo_test.go +++ b/services/rollout-service/pkg/argo/argo_test.go @@ -307,26 +307,14 @@ func TestArgoConsume(t *testing.T) { }, }, Overview: &api.GetOverviewResponse{ - Applications: map[string]*api.Application{ - "foo": { - Releases: []*api.Release{ - { - Version: 1, - SourceCommitId: "00001", - }, - }, - Team: "footeam", - }, - }, EnvironmentGroups: []*api.EnvironmentGroup{ { EnvironmentGroupName: "staging-group", Environments: []*api.Environment{ { - Name: "staging", - Applications: map[string]*api.Environment_Application{}, - Priority: api.Priority_UPSTREAM, + Name: "staging", + Priority: api.Priority_UPSTREAM, Config: &api.EnvironmentConfig{ Argocd: &api.EnvironmentConfig_ArgoCD{ Destination: &api.EnvironmentConfig_ArgoCD_Destination{ @@ -335,6 +323,8 @@ func TestArgoConsume(t *testing.T) { }, }, }, + TeamLocks: make(map[string]*api.Locks), + AppLocks: make(map[string]*api.Locks), }, }, }, @@ -378,26 +368,14 @@ func TestArgoConsume(t *testing.T) { }, }, Overview: &api.GetOverviewResponse{ - Applications: map[string]*api.Application{ - "foo": { - Releases: []*api.Release{ - { - Version: 1, - SourceCommitId: "00001", - }, - }, - Team: "footeam", - }, - }, EnvironmentGroups: []*api.EnvironmentGroup{ { EnvironmentGroupName: "staging-group", Environments: []*api.Environment{ { - Name: "staging", - Applications: map[string]*api.Environment_Application{}, - Priority: api.Priority_UPSTREAM, + Name: "staging", + Priority: api.Priority_UPSTREAM, Config: &api.EnvironmentConfig{ Argocd: &api.EnvironmentConfig_ArgoCD{ Destination: &api.EnvironmentConfig_ArgoCD_Destination{ @@ -406,6 +384,8 @@ func TestArgoConsume(t *testing.T) { }, }, }, + TeamLocks: make(map[string]*api.Locks), + AppLocks: make(map[string]*api.Locks), }, }, }, @@ -466,26 +446,14 @@ func TestArgoConsume(t *testing.T) { }, }, Overview: &api.GetOverviewResponse{ - Applications: map[string]*api.Application{ - "foo": { - Releases: []*api.Release{ - { - Version: 1, - SourceCommitId: "00001", - }, - }, - Team: "footeam", - }, - }, EnvironmentGroups: []*api.EnvironmentGroup{ { EnvironmentGroupName: "staging-group", Environments: []*api.Environment{ { - Name: "staging", - Applications: map[string]*api.Environment_Application{}, - Priority: api.Priority_UPSTREAM, + Name: "staging", + Priority: api.Priority_UPSTREAM, Config: &api.EnvironmentConfig{ Argocd: &api.EnvironmentConfig_ArgoCD{ Destination: &api.EnvironmentConfig_ArgoCD_Destination{ @@ -494,6 +462,8 @@ func TestArgoConsume(t *testing.T) { }, }, }, + TeamLocks: make(map[string]*api.Locks), + AppLocks: make(map[string]*api.Locks), }, }, }, @@ -555,26 +525,14 @@ func TestArgoConsume(t *testing.T) { }, }, Overview: &api.GetOverviewResponse{ - Applications: map[string]*api.Application{ - "foo": { - Releases: []*api.Release{ - { - Version: 1, - SourceCommitId: "00001", - }, - }, - Team: "footeam", - }, - }, EnvironmentGroups: []*api.EnvironmentGroup{ { EnvironmentGroupName: "staging-group", Environments: []*api.Environment{ { - Name: "staging", - Applications: map[string]*api.Environment_Application{}, - Priority: api.Priority_UPSTREAM, + Name: "staging", + Priority: api.Priority_UPSTREAM, Config: &api.EnvironmentConfig{ Argocd: &api.EnvironmentConfig_ArgoCD{ Destination: &api.EnvironmentConfig_ArgoCD_Destination{ @@ -583,6 +541,8 @@ func TestArgoConsume(t *testing.T) { }, }, }, + TeamLocks: make(map[string]*api.Locks), + AppLocks: make(map[string]*api.Locks), }, }, }, @@ -663,35 +623,14 @@ func TestArgoConsume(t *testing.T) { }, }, Overview: &api.GetOverviewResponse{ - Applications: map[string]*api.Application{ - "foo": { - Releases: []*api.Release{ - { - Version: 1, - SourceCommitId: "00001", - }, - }, - Team: "footeam", - }, - "foo2": { - Releases: []*api.Release{ - { - Version: 1, - SourceCommitId: "00012", - }, - }, - Team: "footeam", - }, - }, EnvironmentGroups: []*api.EnvironmentGroup{ { EnvironmentGroupName: "staging-group", Environments: []*api.Environment{ { - Name: "staging", - Applications: map[string]*api.Environment_Application{}, - Priority: api.Priority_UPSTREAM, + Name: "staging", + Priority: api.Priority_UPSTREAM, Config: &api.EnvironmentConfig{ Argocd: &api.EnvironmentConfig_ArgoCD{ Destination: &api.EnvironmentConfig_ArgoCD_Destination{ @@ -700,6 +639,8 @@ func TestArgoConsume(t *testing.T) { }, }, }, + TeamLocks: make(map[string]*api.Locks), + AppLocks: make(map[string]*api.Locks), }, }, }, @@ -762,35 +703,14 @@ func TestArgoConsume(t *testing.T) { }, }, Overview: &api.GetOverviewResponse{ - Applications: map[string]*api.Application{ - "foo": { - Releases: []*api.Release{ - { - Version: 1, - SourceCommitId: "00001", - }, - }, - Team: "footeam", - }, - "foo2": { - Releases: []*api.Release{ - { - Version: 1, - SourceCommitId: "00012", - }, - }, - Team: "footeam", - }, - }, EnvironmentGroups: []*api.EnvironmentGroup{ { EnvironmentGroupName: "staging-group", Environments: []*api.Environment{ { - Name: "staging", - Applications: map[string]*api.Environment_Application{}, - Priority: api.Priority_UPSTREAM, + Name: "staging", + Priority: api.Priority_UPSTREAM, Config: &api.EnvironmentConfig{ Argocd: &api.EnvironmentConfig_ArgoCD{ Destination: &api.EnvironmentConfig_ArgoCD_Destination{ @@ -799,6 +719,8 @@ func TestArgoConsume(t *testing.T) { }, }, }, + TeamLocks: make(map[string]*api.Locks), + AppLocks: make(map[string]*api.Locks), }, }, }, @@ -820,26 +742,14 @@ func TestArgoConsume(t *testing.T) { }, ArgoOverview: &ArgoOverview{ Overview: &api.GetOverviewResponse{ - Applications: map[string]*api.Application{ - "foo": { - Releases: []*api.Release{ - { - Version: 1, - SourceCommitId: "00001", - }, - }, - Team: "footeam", - }, - }, EnvironmentGroups: []*api.EnvironmentGroup{ { EnvironmentGroupName: "staging-group", Environments: []*api.Environment{ { - Name: "staging", - Applications: map[string]*api.Environment_Application{}, - Priority: api.Priority_UPSTREAM, + Name: "staging", + Priority: api.Priority_UPSTREAM, Config: &api.EnvironmentConfig{ Argocd: &api.EnvironmentConfig_ArgoCD{ Destination: &api.EnvironmentConfig_ArgoCD_Destination{ @@ -929,26 +839,25 @@ func TestCreateOrUpdateArgoApp(t *testing.T) { tcs := []struct { Name string Overview *ArgoOverview - Application *api.Environment_Application Environment *api.Environment AppsKnownToArgo map[string]*v1alpha1.Application ArgoManageEnabled bool ArgoManageFilter []string ExpectedOutput bool ExpectedError string + Application api.OverviewApplication }{ { Name: "when filter has `*` and a team name", + Application: api.OverviewApplication{ + Name: "foo", + Team: "footeam", + }, Overview: &ArgoOverview{ Overview: &api.GetOverviewResponse{ - Applications: map[string]*api.Application{ - "foo": { - Releases: []*api.Release{ - { - Version: 1, - SourceCommitId: "00001", - }, - }, + LightweightApps: []*api.OverviewApplication{ + { + Name: "foo", Team: "footeam", }, }, @@ -957,16 +866,7 @@ func TestCreateOrUpdateArgoApp(t *testing.T) { EnvironmentGroupName: "staging-group", Environments: []*api.Environment{ { - Name: "staging", - Applications: map[string]*api.Environment_Application{ - "foo": { - Name: "foo", - Version: 1, - DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ - DeployTime: "123456789", - }, - }, - }, + Name: "staging", Priority: api.Priority_UPSTREAM, Config: &api.EnvironmentConfig{ Argocd: &api.EnvironmentConfig_ArgoCD{ @@ -983,24 +883,8 @@ func TestCreateOrUpdateArgoApp(t *testing.T) { GitRevision: "1234", }, }, - Application: &api.Environment_Application{ - Name: "foo", - Version: 1, - DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ - DeployTime: "123456789", - }, - }, Environment: &api.Environment{ - Name: "development", - Applications: map[string]*api.Environment_Application{ - "foo": { - Name: "foo", - Version: 1, - DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ - DeployTime: "123456789", - }, - }, - }, + Name: "development", Priority: api.Priority_UPSTREAM, Config: &api.EnvironmentConfig{ Argocd: &api.EnvironmentConfig_ArgoCD{ @@ -1020,26 +904,15 @@ func TestCreateOrUpdateArgoApp(t *testing.T) { Name: "when filter has `*`", Overview: &ArgoOverview{ Overview: &api.GetOverviewResponse{ - Applications: map[string]*api.Application{ - "foo": { - Releases: []*api.Release{ - { - Version: 1, - SourceCommitId: "00001", - }, - }, - Team: "footeam", - }, - }, EnvironmentGroups: []*api.EnvironmentGroup{ { EnvironmentGroupName: "staging-group", Environments: []*api.Environment{ { - Name: "staging", - Applications: map[string]*api.Environment_Application{}, - Priority: api.Priority_UPSTREAM, + Name: "staging", + //Applications: map[string]*api.Environment_Application{}, + Priority: api.Priority_UPSTREAM, Config: &api.EnvironmentConfig{ Argocd: &api.EnvironmentConfig_ArgoCD{ Destination: &api.EnvironmentConfig_ArgoCD_Destination{ @@ -1055,24 +928,8 @@ func TestCreateOrUpdateArgoApp(t *testing.T) { GitRevision: "1234", }, }, - Application: &api.Environment_Application{ - Name: "foo", - Version: 1, - DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ - DeployTime: "123456789", - }, - }, Environment: &api.Environment{ - Name: "development", - Applications: map[string]*api.Environment_Application{ - "foo": { - Name: "foo", - Version: 1, - DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ - DeployTime: "123456789", - }, - }, - }, + Name: "development", Priority: api.Priority_UPSTREAM, Config: &api.EnvironmentConfig{ Argocd: &api.EnvironmentConfig_ArgoCD{ @@ -1108,7 +965,7 @@ func TestCreateOrUpdateArgoApp(t *testing.T) { } hlth.BackOffFactory = func() backoff.BackOff { return backoff.NewConstantBackOff(0) } - isActive, err := IsSelfManagedFilterActive(team(tc.Overview.Overview, tc.Application.Name), argoProcessor) + isActive, err := IsSelfManagedFilterActive(tc.Application.Name, argoProcessor) if tc.ExpectedError != "" { if err.Error() != tc.ExpectedError { t.Fatalf("expected error to be %s but got %s", tc.ExpectedError, err.Error()) @@ -1208,11 +1065,3 @@ type ArgoEvent struct { HealthStatusCode health.HealthStatusCode OperationState *v1alpha1.OperationState } - -func team(overview *api.GetOverviewResponse, app string) string { - a := overview.Applications[app] - if a == nil { - return "" - } - return a.Team -} diff --git a/services/rollout-service/pkg/versions/versions.go b/services/rollout-service/pkg/versions/versions.go index 7c0d4af40..ee7953fe6 100644 --- a/services/rollout-service/pkg/versions/versions.go +++ b/services/rollout-service/pkg/versions/versions.go @@ -78,7 +78,7 @@ var ZeroVersion VersionInfo // GetVersion implements VersionClient func (v *versionClient) GetVersion(ctx context.Context, revision, environment, application string) (*VersionInfo, error) { ctx = auth.WriteUserToGrpcContext(ctx, RolloutServiceUser) - tr, err := v.tryGetVersion(ctx, revision, environment, application) + tr, err := v.tryGetVersion(environment, application) if err == nil { return tr, nil } @@ -98,27 +98,21 @@ func (v *versionClient) GetVersion(ctx context.Context, revision, environment, a } // Tries getting the version from cache -func (v *versionClient) tryGetVersion(ctx context.Context, revision, environment, application string) (*VersionInfo, error) { - var overview *api.GetOverviewResponse - entry, ok := v.cache.Get(revision) +func (v *versionClient) tryGetVersion(environment, application string) (*VersionInfo, error) { + var appDetails *api.GetAppDetailsResponse + entry, ok := v.cache.Get(application) if !ok { return nil, ErrNotFound } - overview = entry.(*api.GetOverviewResponse) - for _, group := range overview.GetEnvironmentGroups() { - for _, env := range group.GetEnvironments() { - if env.Name == environment { - app := env.Applications[application] - if app == nil { - return &ZeroVersion, nil - } - return &VersionInfo{ - Version: app.Version, - SourceCommitId: sourceCommitIdFromOverview(overview, app), - DeployedAt: deployedAtFromApp(app), - }, nil - } - } + appDetails = entry.(*api.GetAppDetailsResponse) + + if deployment, exists := appDetails.Deployments[environment]; exists { + deployedVersion := deployment.Version + return &VersionInfo{ + Version: deployedVersion, + SourceCommitId: sourceCommitId(appDetails.Application.Releases, deployment), + DeployedAt: deployedAtFromApp(deployment), + }, nil } return &ZeroVersion, nil } @@ -138,11 +132,11 @@ func deployedAt(deployment *api.Deployment) time.Time { return time.Time{} } -func deployedAtFromApp(app *api.Environment_Application) time.Time { - if app.DeploymentMetaData == nil { +func deployedAtFromApp(deployment *api.Deployment) time.Time { + if deployment.DeploymentMetaData == nil { return time.Time{} } - deployTime := app.DeploymentMetaData.DeployTime + deployTime := deployment.DeploymentMetaData.DeployTime if deployTime != "" { dt, err := strconv.ParseInt(deployTime, 10, 64) if err != nil { @@ -161,19 +155,6 @@ func sourceCommitId(appReleases []*api.Release, deployment *api.Deployment) stri } return "" } -func sourceCommitIdFromOverview(overview *api.GetOverviewResponse, app *api.Environment_Application) string { - a := overview.Applications[app.Name] - if a == nil { - return "" - } - for _, rel := range a.Releases { - if rel.Version == app.Version { - - return rel.SourceCommitId - } - } - return "" -} type KuberpultEvent struct { Environment string @@ -235,7 +216,7 @@ func (v *versionClient) ConsumeEvents(ctx context.Context, processor VersionEven return fmt.Errorf("overviewClient.GetOverview: %w", err) } l := logger.FromContext(ctx) - v.cache.Add(ov.GitRevision, ov) + l.Info("overview.get") seen := make(map[key]uint64, len(versions)) @@ -246,6 +227,7 @@ func (v *versionClient) ConsumeEvents(ctx context.Context, processor VersionEven for _, appDetailsResponse := range changedApps.ChangedApps { appName := appDetailsResponse.Application.Name overview.AppDetails[appName] = appDetailsResponse + v.cache.Add(appName, appDetailsResponse) // Update cache of app details app := appDetailsResponse.Application //Go through every deployment and check if we have seen it. If not, Add it to the pool of events diff --git a/services/rollout-service/pkg/versions/versions_test.go b/services/rollout-service/pkg/versions/versions_test.go index 588ae8bbe..8fdbac49a 100644 --- a/services/rollout-service/pkg/versions/versions_test.go +++ b/services/rollout-service/pkg/versions/versions_test.go @@ -163,17 +163,6 @@ func (m *mockVersionEventProcessor) ProcessKuberpultEvent(ctx context.Context, e func TestVersionClientStream(t *testing.T) { t.Parallel() testOverview := &api.GetOverviewResponse{ - Applications: map[string]*api.Application{ - "foo": { - Releases: []*api.Release{ - { - Version: 1, - SourceCommitId: "00001", - }, - }, - Team: "footeam", - }, - }, EnvironmentGroups: []*api.EnvironmentGroup{ { @@ -182,15 +171,6 @@ func TestVersionClientStream(t *testing.T) { Environments: []*api.Environment{ { Name: "staging", - Applications: map[string]*api.Environment_Application{ - "foo": { - Name: "foo", - Version: 1, - DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ - DeployTime: "123456789", - }, - }, - }, }, }, }, @@ -199,16 +179,6 @@ func TestVersionClientStream(t *testing.T) { } testOverviewWithDifferentEnvgroup := &api.GetOverviewResponse{ - Applications: map[string]*api.Application{ - "foo": { - Releases: []*api.Release{ - { - Version: 2, - SourceCommitId: "00002", - }, - }, - }, - }, EnvironmentGroups: []*api.EnvironmentGroup{ { @@ -217,15 +187,6 @@ func TestVersionClientStream(t *testing.T) { Environments: []*api.Environment{ { Name: "staging", - Applications: map[string]*api.Environment_Application{ - "foo": { - Name: "foo", - Version: 2, - DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ - DeployTime: "123456789", - }, - }, - }, }, }, }, @@ -233,17 +194,6 @@ func TestVersionClientStream(t *testing.T) { GitRevision: "1234", } testOverviewWithProdEnvs := &api.GetOverviewResponse{ - Applications: map[string]*api.Application{ - "foo": { - Team: "footeam", - Releases: []*api.Release{ - { - Version: 2, - SourceCommitId: "00002", - }, - }, - }, - }, EnvironmentGroups: []*api.EnvironmentGroup{ { EnvironmentGroupName: "production", @@ -251,15 +201,6 @@ func TestVersionClientStream(t *testing.T) { Environments: []*api.Environment{ { Name: "production", - Applications: map[string]*api.Environment_Application{ - "foo": { - Name: "foo", - Version: 2, - DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ - DeployTime: "123456789", - }, - }, - }, }, }, }, @@ -269,15 +210,6 @@ func TestVersionClientStream(t *testing.T) { Environments: []*api.Environment{ { Name: "canary", - Applications: map[string]*api.Environment_Application{ - "foo": { - Name: "foo", - Version: 2, - DeploymentMetaData: &api.Environment_Application_DeploymentMetaData{ - DeployTime: "123456789", - }, - }, - }, }, }, },