From 4b88afa873d8d41a2b8c6dd33a89ea31d44bf204 Mon Sep 17 00:00:00 2001 From: oleksandr-codefresh Date: Wed, 13 Nov 2024 17:19:04 +0200 Subject: [PATCH] event-reporter: changes after pr review --- .../application_event_reporter_test.go | 1 + event_reporter/reporter/event_payload.go | 8 +- event_reporter/reporter/types.go | 4 + event_reporter/utils/app.go | 63 +++++ event_reporter/utils/app_revision.go | 37 +-- event_reporter/utils/app_revision_test.go | 221 ++++++++++++++++++ 6 files changed, 316 insertions(+), 18 deletions(-) create mode 100644 event_reporter/utils/app.go diff --git a/event_reporter/reporter/application_event_reporter_test.go b/event_reporter/reporter/application_event_reporter_test.go index acda38922bc65f..b2414628c4cdd6 100644 --- a/event_reporter/reporter/application_event_reporter_test.go +++ b/event_reporter/reporter/application_event_reporter_test.go @@ -10,6 +10,7 @@ import ( "github.com/argoproj/argo-cd/v2/util/db" "github.com/argoproj/argo-cd/v2/util/settings" + "k8s.io/client-go/kubernetes/fake" "github.com/aws/smithy-go/ptr" diff --git a/event_reporter/reporter/event_payload.go b/event_reporter/reporter/event_payload.go index 81c140e7ac1797..22bb9efdc067b2 100644 --- a/event_reporter/reporter/event_payload.go +++ b/event_reporter/reporter/event_payload.go @@ -165,10 +165,14 @@ func getResourceSourceRepoUrl( specCopy.Source = reportedEntityParentApp.app.Status.Sync.ComparedTo.Source.DeepCopy() if specCopy.HasMultipleSources() { - if rr.appSourceIdx == -1 { + if !rr.appSourceIdxDetected() { return "" } - return specCopy.GetSourcePtrByIndex(int(rr.appSourceIdx)).RepoURL + source := specCopy.GetSourcePtrByIndex(int(rr.appSourceIdx)) + if source == nil { + return "" + } + return source.RepoURL } return specCopy.Source.RepoURL diff --git a/event_reporter/reporter/types.go b/event_reporter/reporter/types.go index dddfba5ebd03ca..810f26253ec1a7 100644 --- a/event_reporter/reporter/types.go +++ b/event_reporter/reporter/types.go @@ -43,3 +43,7 @@ func (rr *ReportedResource) GetApiVersion() string { return apiVersion } + +func (rr *ReportedResource) appSourceIdxDetected() bool { + return rr.appSourceIdx >= 0 +} diff --git a/event_reporter/utils/app.go b/event_reporter/utils/app.go new file mode 100644 index 00000000000000..84dbe01d15b27c --- /dev/null +++ b/event_reporter/utils/app.go @@ -0,0 +1,63 @@ +package utils + +import appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + +type AppRevisionsFieldNames string + +var AppRevisionFieldName AppRevisionsFieldNames = "Revision" +var AppRevisionsFieldName AppRevisionsFieldNames = "Revisions" + +type AppUtils struct { + App *appv1.Application +} + +func (au *AppUtils) operationStateSyncExists(fieldToCheck *AppRevisionsFieldNames) bool { + result := au.App != nil && au.App.Status.OperationState != nil && au.App.Status.OperationState.Operation.Sync != nil + if !result { + return false + } + + return revisionsToCheck(RevisionsData{ + Revision: au.App.Status.OperationState.Operation.Sync.Revision, + Revisions: au.App.Status.OperationState.Operation.Sync.Revisions, + }, fieldToCheck) +} + +func (au *AppUtils) operationSyncExists(fieldToCheck *AppRevisionsFieldNames) bool { + result := au.App != nil && au.App.Operation != nil && au.App.Operation.Sync != nil + if !result { + return false + } + + return revisionsToCheck(RevisionsData{ + Revision: au.App.Operation.Sync.Revision, + Revisions: au.App.Operation.Sync.Revisions, + }, fieldToCheck) +} + +func (au *AppUtils) operationSyncResultExists(fieldToCheck *AppRevisionsFieldNames) bool { + result := au.App != nil && au.App.Status.OperationState != nil && au.App.Status.OperationState.SyncResult != nil + if !result { + return false + } + + return revisionsToCheck(RevisionsData{ + Revision: au.App.Status.OperationState.SyncResult.Revision, + Revisions: au.App.Status.OperationState.SyncResult.Revisions, + }, fieldToCheck) +} + +// expected to return true if fieldToCheck == nil +func revisionsToCheck(obj RevisionsData, fieldToCheck *AppRevisionsFieldNames) bool { + if fieldToCheck == nil { + return true + } + if *fieldToCheck == AppRevisionFieldName { + return obj.Revision != "" + } + + if *fieldToCheck == AppRevisionsFieldName { + return obj.Revisions != nil && len(obj.Revisions) > 0 + } + return true +} diff --git a/event_reporter/utils/app_revision.go b/event_reporter/utils/app_revision.go index 29ea5f789e7162..10ab89e0f8dd69 100644 --- a/event_reporter/utils/app_revision.go +++ b/event_reporter/utils/app_revision.go @@ -26,7 +26,7 @@ type RevisionsData struct { const annotationRevisionKey = "app.meta.revisions-metadata" func (asrm *AppSyncRevisionsMetadata) GetSyncRevisionAt(idx int) *RevisionWithMetadata { - if asrm == nil || asrm.SyncRevisions == nil { + if asrm == nil || asrm.SyncRevisions == nil || idx < 0 || idx >= len(asrm.SyncRevisions) { return nil } return asrm.SyncRevisions[idx] @@ -60,12 +60,12 @@ func GetOperationRevision(a *appv1.Application) string { if a == nil { return "" } - + au := &AppUtils{App: a} // this value will be used in case if application hasn't resources , like gitsource revision := a.Status.Sync.Revision - if a.Status.OperationState != nil && a.Status.OperationState.Operation.Sync != nil && a.Status.OperationState.Operation.Sync.Revision != "" { + if au.operationStateSyncExists(&AppRevisionFieldName) { revision = a.Status.OperationState.Operation.Sync.Revision - } else if a.Operation != nil && a.Operation.Sync != nil && a.Operation.Sync.Revision != "" { + } else if au.operationSyncExists(&AppRevisionFieldName) { revision = a.Operation.Sync.Revision } @@ -76,12 +76,13 @@ func GetOperationRevisions(a *appv1.Application) []string { if a == nil { return nil } + au := &AppUtils{App: a} // this value will be used in case if application hasn't resources , like gitsource revisions := a.Status.Sync.Revisions - if a.Status.OperationState != nil && a.Status.OperationState.Operation.Sync != nil && a.Status.OperationState.Operation.Sync.Revisions != nil && len(a.Status.OperationState.Operation.Sync.Revisions) > 0 { + if au.operationStateSyncExists(&AppRevisionsFieldName) { revisions = a.Status.OperationState.Operation.Sync.Revisions - } else if a.Operation != nil && a.Operation.Sync != nil && a.Operation.Sync.Revisions != nil && len(a.Operation.Sync.Revisions) > 0 { + } else if au.operationSyncExists(&AppRevisionsFieldName) { revisions = a.Operation.Sync.Revisions } @@ -89,25 +90,28 @@ func GetOperationRevisions(a *appv1.Application) []string { } func GetOperationSyncResultRevision(a *appv1.Application) *string { - if a == nil || a.Status.OperationState == nil || a.Status.OperationState.SyncResult == nil { - return nil + au := &AppUtils{App: a} + if au.operationSyncResultExists(nil) { + return &a.Status.OperationState.SyncResult.Revision } - return &a.Status.OperationState.SyncResult.Revision + return nil } func GetOperationSyncResultRevisions(a *appv1.Application) *[]string { - if a == nil || a.Status.OperationState == nil || a.Status.OperationState.SyncResult == nil { - return nil + au := &AppUtils{App: a} + if au.operationSyncResultExists(nil) { + return &a.Status.OperationState.SyncResult.Revisions } - return &a.Status.OperationState.SyncResult.Revisions + return nil } func GetOperationSyncRevisions(a *appv1.Application) []string { if a == nil { return []string{} } + au := &AppUtils{App: a} // this value will be used in case if application hasn't resources, like empty gitsource revisions := getRevisions(RevisionsData{ @@ -115,12 +119,12 @@ func GetOperationSyncRevisions(a *appv1.Application) []string { Revisions: a.Status.Sync.Revisions, }) - if a.Status.OperationState != nil && a.Status.OperationState.Operation.Sync != nil { + if au.operationStateSyncExists(nil) { revisions = getRevisions(RevisionsData{ Revision: a.Status.OperationState.Operation.Sync.Revision, Revisions: a.Status.OperationState.Operation.Sync.Revisions, }) - } else if a.Operation != nil && a.Operation.Sync != nil { + } else if au.operationSyncExists(nil) { revisions = getRevisions(RevisionsData{ Revision: a.Operation.Sync.Revision, Revisions: a.Operation.Sync.Revisions, @@ -137,16 +141,17 @@ func GetOperationChangeRevisions(a *appv1.Application) []string { if a == nil { return revisions } + au := &AppUtils{App: a} // this value will be used in case if application hasn't resources, like empty gitsource - if a.Status.OperationState != nil && a.Status.OperationState.Operation.Sync != nil { + if au.operationStateSyncExists(nil) { if a.Status.OperationState.Operation.Sync.ChangeRevision != "" || a.Status.OperationState.Operation.Sync.ChangeRevisions != nil { revisions = getRevisions(RevisionsData{ Revision: a.Status.OperationState.Operation.Sync.ChangeRevision, Revisions: a.Status.OperationState.Operation.Sync.ChangeRevisions, }) } - } else if a.Operation != nil && a.Operation.Sync != nil { + } else if au.operationSyncExists(nil) { if a.Operation.Sync.ChangeRevision != "" || a.Operation.Sync.ChangeRevisions != nil { revisions = getRevisions(RevisionsData{ Revision: a.Operation.Sync.ChangeRevision, diff --git a/event_reporter/utils/app_revision_test.go b/event_reporter/utils/app_revision_test.go index 370edecdb2d5a4..f3e5ab35d93cab 100644 --- a/event_reporter/utils/app_revision_test.go +++ b/event_reporter/utils/app_revision_test.go @@ -441,3 +441,224 @@ func TestAddCommitDetailsToLabels(t *testing.T) { assert.Equal(t, "http://my-grafana.com/pre-generated-link", result.GetLabels()["link"]) }) } + +func TestGetSyncRevisionAt(t *testing.T) { + t.Run("should return nil once idx out of range", func(t *testing.T) { + revisionMetadata := AppSyncRevisionsMetadata{ + SyncRevisions: []*RevisionWithMetadata{{ + Metadata: &v1alpha1.RevisionMetadata{ + Author: "demo usert", + Date: metav1.Time{}, + Message: "some message", + }, + }}, + } + + assert.Nil(t, revisionMetadata.GetSyncRevisionAt(-1)) + assert.Nil(t, revisionMetadata.GetSyncRevisionAt(1)) + }) + t.Run("should return nil if data missing", func(t *testing.T) { + revisionMetadata := AppSyncRevisionsMetadata{} + + assert.Nil(t, revisionMetadata.GetSyncRevisionAt(1)) + }) + t.Run("should return correct idx", func(t *testing.T) { + revisionMetadata := AppSyncRevisionsMetadata{ + SyncRevisions: []*RevisionWithMetadata{ + { + Metadata: &v1alpha1.RevisionMetadata{ + Author: "demo usert", + Date: metav1.Time{}, + Message: "some message", + }, + }, + { + Metadata: &v1alpha1.RevisionMetadata{ + Author: "demo user2", + Date: metav1.Time{}, + Message: "some message 2", + }, + }, + }, + } + + syncRev := revisionMetadata.GetSyncRevisionAt(1) + assert.NotNil(t, syncRev) + assert.Equal(t, "demo user2", syncRev.Metadata.Author) + assert.Equal(t, "some message 2", syncRev.Metadata.Message) + }) +} + +func TestGetOperationRevision(t *testing.T) { + t.Run("should return empty strint once app in nil", func(t *testing.T) { + assert.Equal(t, "", GetOperationRevision(nil)) + }) + t.Run("should return Status.Sync.Revision as fallback", func(t *testing.T) { + assert.Equal(t, "Status.Sync.Revision", GetOperationRevision(&v1alpha1.Application{ + Status: v1alpha1.ApplicationStatus{ + Sync: v1alpha1.SyncStatus{ + Revision: "Status.Sync.Revision", + }, + }, + })) + }) + t.Run("should return Status.OperationState.Operation.Sync.Revision", func(t *testing.T) { + assert.Equal(t, "Status.OperationState.Operation.Sync.Revision", GetOperationRevision(&v1alpha1.Application{ + Status: v1alpha1.ApplicationStatus{ + Sync: v1alpha1.SyncStatus{ + Revision: "Status.Sync.Revision", + }, + OperationState: &v1alpha1.OperationState{ + Operation: v1alpha1.Operation{ + Sync: &v1alpha1.SyncOperation{ + Revision: "Status.OperationState.Operation.Sync.Revision", + }, + }, + }, + }, + Operation: &v1alpha1.Operation{ + Sync: &v1alpha1.SyncOperation{ + Revision: "Operation.Sync.Revision", + }, + }, + })) + }) + t.Run("should return Status-Sync-Revision", func(t *testing.T) { + assert.Equal(t, "Operation.Sync.Revision", GetOperationRevision(&v1alpha1.Application{ + Status: v1alpha1.ApplicationStatus{ + Sync: v1alpha1.SyncStatus{ + Revision: "Status-Sync-Revision", + }, + }, + Operation: &v1alpha1.Operation{ + Sync: &v1alpha1.SyncOperation{ + Revision: "Operation.Sync.Revision", + }, + }, + })) + }) +} + +func TestGetOperationRevisions(t *testing.T) { + t.Run("should return empty strint once app in nil", func(t *testing.T) { + assert.Nil(t, GetOperationRevisions(nil)) + }) + t.Run("should return Status.Sync.Revisions as fallback", func(t *testing.T) { + res := GetOperationRevisions(&v1alpha1.Application{ + Status: v1alpha1.ApplicationStatus{ + Sync: v1alpha1.SyncStatus{ + Revision: "Status.Sync.Revision", + Revisions: []string{"Status.Sync.Revisions"}, + }, + }, + }) + assert.Len(t, res, 1) + assert.Equal(t, res[0], "Status.Sync.Revisions") + }) + t.Run("should return Status.OperationState.Operation.Sync.Revisions", func(t *testing.T) { + res := GetOperationRevisions(&v1alpha1.Application{ + Status: v1alpha1.ApplicationStatus{ + Sync: v1alpha1.SyncStatus{ + Revision: "Status.Sync.Revision", + Revisions: []string{"Status.Sync.Revisions"}, + }, + OperationState: &v1alpha1.OperationState{ + Operation: v1alpha1.Operation{ + Sync: &v1alpha1.SyncOperation{ + Revision: "Status.OperationState.Operation.Sync.Revision", + Revisions: []string{"Status.OperationState.Operation.Sync.Revisions"}, + }, + }, + }, + }, + Operation: &v1alpha1.Operation{ + Sync: &v1alpha1.SyncOperation{ + Revision: "Operation.Sync.Revision", + Revisions: []string{"Operation.Sync.Revisions"}, + }, + }, + }) + assert.Len(t, res, 1) + assert.Equal(t, "Status.OperationState.Operation.Sync.Revisions", res[0]) + }) + t.Run("should return Status-Sync-Revisions", func(t *testing.T) { + res := GetOperationRevisions(&v1alpha1.Application{ + Status: v1alpha1.ApplicationStatus{ + Sync: v1alpha1.SyncStatus{ + Revision: "Status-Sync-Revision", + }, + }, + Operation: &v1alpha1.Operation{ + Sync: &v1alpha1.SyncOperation{ + Revision: "Operation.Sync.Revision", + Revisions: []string{"Operation.Sync.Revisions"}, + }, + }, + }) + assert.Len(t, res, 1) + assert.Equal(t, res[0], "Operation.Sync.Revisions") + }) +} + +func TestGetOperationSyncResultRevision(t *testing.T) { + t.Run("should return nil", func(t *testing.T) { + assert.Nil(t, GetOperationSyncResultRevision(nil)) + assert.Nil(t, GetOperationSyncResultRevision(&v1alpha1.Application{ + Status: v1alpha1.ApplicationStatus{ + OperationState: &v1alpha1.OperationState{ + Operation: v1alpha1.Operation{ + Sync: &v1alpha1.SyncOperation{ + Revision: "Status.OperationState.Operation.Sync.Revision", + Revisions: []string{"Status.OperationState.Operation.Sync.Revisions"}, + }, + }, + }, + }, + })) + }) + t.Run("should return revision", func(t *testing.T) { + res := GetOperationSyncResultRevision(&v1alpha1.Application{ + Status: v1alpha1.ApplicationStatus{ + OperationState: &v1alpha1.OperationState{ + SyncResult: &v1alpha1.SyncOperationResult{ + Revision: "Status.OperationState.SyncResult.Revision", + Revisions: []string{"Status.OperationState.SyncResult.Revisions"}, + }, + }, + }, + }) + assert.Equal(t, "Status.OperationState.SyncResult.Revision", *res) + }) +} + +func TestGetOperationSyncResultRevisions(t *testing.T) { + t.Run("should return nil", func(t *testing.T) { + assert.Nil(t, GetOperationSyncResultRevisions(nil)) + assert.Nil(t, GetOperationSyncResultRevisions(&v1alpha1.Application{ + Status: v1alpha1.ApplicationStatus{ + OperationState: &v1alpha1.OperationState{ + Operation: v1alpha1.Operation{ + Sync: &v1alpha1.SyncOperation{ + Revision: "Status.OperationState.Operation.Sync.Revision", + Revisions: []string{"Status.OperationState.Operation.Sync.Revisions"}, + }, + }, + }, + }, + })) + }) + t.Run("should return revisions", func(t *testing.T) { + res := GetOperationSyncResultRevisions(&v1alpha1.Application{ + Status: v1alpha1.ApplicationStatus{ + OperationState: &v1alpha1.OperationState{ + SyncResult: &v1alpha1.SyncOperationResult{ + Revision: "Status.OperationState.SyncResult.Revision", + Revisions: []string{"Status.OperationState.SyncResult.Revisions"}, + }, + }, + }, + }) + assert.NotNil(t, res) + assert.Len(t, *res, 1) + }) +}