diff --git a/event_reporter/reporter/app_revision.go b/event_reporter/reporter/app_revision.go new file mode 100644 index 00000000000000..845814fc6d47ca --- /dev/null +++ b/event_reporter/reporter/app_revision.go @@ -0,0 +1,17 @@ +package reporter + +import ( + "context" + "github.com/argoproj/argo-cd/v2/pkg/apiclient/application" + appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" +) + +func (s *applicationEventReporter) getApplicationRevisionDetails(ctx context.Context, a *appv1.Application, revision string) (*appv1.RevisionMetadata, error) { + project := a.Spec.GetProject() + return s.applicationServiceClient.RevisionMetadata(ctx, &application.RevisionMetadataQuery{ + Name: &a.Name, + AppNamespace: &a.Namespace, + Revision: &revision, + Project: &project, + }) +} diff --git a/event_reporter/reporter/app_revision_helpers.go b/event_reporter/reporter/app_revision_helpers.go deleted file mode 100644 index 3e66c1161ac76e..00000000000000 --- a/event_reporter/reporter/app_revision_helpers.go +++ /dev/null @@ -1,84 +0,0 @@ -package reporter - -import ( - "context" - "github.com/argoproj/argo-cd/v2/pkg/apiclient/application" - appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" -) - -type AppSyncRevisionsMetadata struct { - revisions []*appv1.RevisionMetadata - syncRevisions []*appv1.RevisionMetadata -} - -func getLatestAppHistoryId(a *appv1.Application) int64 { - var id int64 - lastHistory := getLatestAppHistoryItem(a) - - if lastHistory != nil { - id = lastHistory.ID - } - - return id -} - -func getLatestAppHistoryItem(a *appv1.Application) *appv1.RevisionHistory { - if a.Status.History != nil && len(a.Status.History) > 0 { - return &a.Status.History[len(a.Status.History)-1] - } - - return nil -} - -func getApplicationLatestRevision(a *appv1.Application) string { - revision := a.Status.Sync.Revision - lastHistory := getLatestAppHistoryItem(a) - - if lastHistory != nil { - revision = lastHistory.Revision - } - - return revision -} - -func getOperationRevision(a *appv1.Application) string { - var revision string - if a != nil { - // this value will be used in case if application hasnt 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 != "" { - revision = a.Status.OperationState.Operation.Sync.Revision - } else if a.Operation != nil && a.Operation.Sync != nil && a.Operation.Sync.Revision != "" { - revision = a.Operation.Sync.Revision - } - } - - return revision -} - -func (s *applicationEventReporter) getApplicationRevisionDetails(ctx context.Context, a *appv1.Application, revision string) (*appv1.RevisionMetadata, error) { - project := a.Spec.GetProject() - return s.applicationServiceClient.RevisionMetadata(ctx, &application.RevisionMetadataQuery{ - Name: &a.Name, - AppNamespace: &a.Namespace, - Revision: &revision, - Project: &project, - }) -} - -func addCommitDetailsToLabels(u *unstructured.Unstructured, revisionMetadata *appv1.RevisionMetadata) *unstructured.Unstructured { - if revisionMetadata == nil || u == nil { - return u - } - - if field, _, _ := unstructured.NestedFieldCopy(u.Object, "metadata", "labels"); field == nil { - _ = unstructured.SetNestedStringMap(u.Object, map[string]string{}, "metadata", "labels") - } - - _ = unstructured.SetNestedField(u.Object, revisionMetadata.Date.Format("2006-01-02T15:04:05.000Z"), "metadata", "labels", "app.meta.commit-date") - _ = unstructured.SetNestedField(u.Object, revisionMetadata.Author, "metadata", "labels", "app.meta.commit-author") - _ = unstructured.SetNestedField(u.Object, revisionMetadata.Message, "metadata", "labels", "app.meta.commit-message") - - return u -} diff --git a/event_reporter/reporter/application_event_reporter.go b/event_reporter/reporter/application_event_reporter.go index 2001ebde202072..52db839a6435ae 100644 --- a/event_reporter/reporter/application_event_reporter.go +++ b/event_reporter/reporter/application_event_reporter.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "github.com/argoproj/argo-cd/v2/event_reporter/utils" "math" "reflect" "strings" @@ -63,13 +64,13 @@ func NewApplicationEventReporter(cache *servercache.Cache, applicationServiceCli } func (s *applicationEventReporter) shouldSendResourceEvent(a *appv1.Application, rs appv1.ResourceStatus) bool { - logCtx := logWithResourceStatus(log.WithFields(log.Fields{ + logCtx := utils.LogWithResourceStatus(log.WithFields(log.Fields{ "app": a.Name, "gvk": fmt.Sprintf("%s/%s/%s", rs.Group, rs.Version, rs.Kind), "resource": fmt.Sprintf("%s/%s", rs.Namespace, rs.Name), }), rs) - cachedRes, err := s.cache.GetLastResourceEvent(a, rs, getApplicationLatestRevision(a)) + cachedRes, err := s.cache.GetLastResourceEvent(a, rs, utils.GetApplicationLatestRevision(a)) if err != nil { logCtx.Debug("resource not in cache") return true @@ -144,19 +145,19 @@ func (s *applicationEventReporter) StreamApplicationEvents( logCtx.Info("getting parent application name") - parentAppIdentity := getParentAppIdentity(a, appInstanceLabelKey, trackingMethod) + parentAppIdentity := utils.GetParentAppIdentity(a, appInstanceLabelKey, trackingMethod) - if isChildApp(parentAppIdentity) { + if utils.IsChildApp(parentAppIdentity) { logCtx.Info("processing as child application") parentApplicationEntity, err := s.applicationServiceClient.Get(ctx, &application.ApplicationQuery{ - Name: &parentAppIdentity.name, - AppNamespace: &parentAppIdentity.namespace, + Name: &parentAppIdentity.Name, + AppNamespace: &parentAppIdentity.Namespace, }) if err != nil { return fmt.Errorf("failed to get parent application entity: %w", err) } - rs := getAppAsResource(a) + rs := utils.GetAppAsResource(a) parentDesiredManifests, err, manifestGenErr := s.getDesiredManifests(ctx, parentApplicationEntity, logCtx) if err != nil { @@ -165,13 +166,13 @@ func (s *applicationEventReporter) StreamApplicationEvents( // helm app hasnt revision // TODO: add check if it helm application - parentOperationRevision := getOperationRevision(parentApplicationEntity) + parentOperationRevision := utils.GetOperationRevision(parentApplicationEntity) parentRevisionMetadata, err := s.getApplicationRevisionDetails(ctx, parentApplicationEntity, parentOperationRevision) if err != nil { logCtx.WithError(err).Warn("failed to get parent application's revision metadata, resuming") } - setHealthStatusIfMissing(rs) + utils.SetHealthStatusIfMissing(rs) err = s.processResource(ctx, *rs, parentApplicationEntity, logCtx, ts, parentDesiredManifests, appTree, manifestGenErr, a, parentRevisionMetadata, appInstanceLabelKey, trackingMethod, desiredManifests.ApplicationVersions) if err != nil { s.metricsServer.IncErroredEventsCounter(metrics.MetricChildAppEventType, metrics.MetricEventUnknownErrorType, a.Name) @@ -193,7 +194,7 @@ func (s *applicationEventReporter) StreamApplicationEvents( return nil } - logWithAppStatus(a, logCtx, ts).Info("sending root application event") + utils.LogWithAppStatus(a, logCtx, ts).Info("sending root application event") if err := s.codefreshClient.SendEvent(ctx, a.Name, appEvent); err != nil { s.metricsServer.IncErroredEventsCounter(metrics.MetricParentAppEventType, metrics.MetricEventDeliveryErrorType, a.Name) return fmt.Errorf("failed to send event for root application %s/%s: %w", a.Namespace, a.Name, err) @@ -202,14 +203,14 @@ func (s *applicationEventReporter) StreamApplicationEvents( s.metricsServer.ObserveEventProcessingDurationHistogramDuration(a.Name, metrics.MetricParentAppEventType, reconcileDuration) } - revisionMetadata, _ := s.getApplicationRevisionDetails(ctx, a, getOperationRevision(a)) + revisionMetadata, _ := s.getApplicationRevisionDetails(ctx, a, utils.GetOperationRevision(a)) // for each resource in the application get desired and actual state, // then stream the event for _, rs := range a.Status.Resources { - if isApp(rs) { + if utils.IsApp(rs) { continue } - setHealthStatusIfMissing(&rs) + utils.SetHealthStatusIfMissing(&rs) if !ignoreResourceCache && !s.shouldSendResourceEvent(a, rs) { s.metricsServer.IncCachedIgnoredEventsCounter(metrics.MetricResourceEventType, a.Name) continue @@ -239,7 +240,7 @@ func (s *applicationEventReporter) getAppForResourceReporting( return a, revisionMetadata } - revisionMetadataToReport, err := s.getApplicationRevisionDetails(ctx, latestAppStatus, getOperationRevision(latestAppStatus)) + revisionMetadataToReport, err := s.getApplicationRevisionDetails(ctx, latestAppStatus, utils.GetOperationRevision(latestAppStatus)) if err != nil { return a, revisionMetadata @@ -264,7 +265,7 @@ func (s *applicationEventReporter) processResource( applicationVersions *apiclient.ApplicationVersions, ) error { metricsEventType := metrics.MetricResourceEventType - if isApp(rs) { + if utils.IsApp(rs) { metricsEventType = metrics.MetricChildAppEventType } @@ -290,7 +291,7 @@ func (s *applicationEventReporter) processResource( var originalAppRevisionMetadata *appv1.RevisionMetadata = nil if originalApplication != nil { - originalAppRevisionMetadata, _ = s.getApplicationRevisionDetails(ctx, originalApplication, getOperationRevision(originalApplication)) + originalAppRevisionMetadata, _ = s.getApplicationRevisionDetails(ctx, originalApplication, utils.GetOperationRevision(originalApplication)) } ev, err := getResourceEventPayload(parentApplicationToReport, &rs, actualState, desiredState, appTree, manifestGenErr, ts, originalApplication, revisionMetadataToReport, originalAppRevisionMetadata, appInstanceLabelKey, trackingMethod, applicationVersions) @@ -302,11 +303,11 @@ func (s *applicationEventReporter) processResource( appRes := appv1.Application{} appName := "" - if isApp(rs) && actualState.Manifest != nil && json.Unmarshal([]byte(*actualState.Manifest), &appRes) == nil { - logWithAppStatus(&appRes, logCtx, ts).Info("streaming resource event") + if utils.IsApp(rs) && actualState.Manifest != nil && json.Unmarshal([]byte(*actualState.Manifest), &appRes) == nil { + utils.LogWithAppStatus(&appRes, logCtx, ts).Info("streaming resource event") appName = appRes.Name } else { - logWithResourceStatus(logCtx, rs).Info("streaming resource event") + utils.LogWithResourceStatus(logCtx, rs).Info("streaming resource event") appName = rs.Name } @@ -320,7 +321,7 @@ func (s *applicationEventReporter) processResource( return nil } - if err := s.cache.SetLastResourceEvent(parentApplicationToReport, rs, resourceEventCacheExpiration, getApplicationLatestRevision(parentApplicationToReport)); err != nil { + if err := s.cache.SetLastResourceEvent(parentApplicationToReport, rs, resourceEventCacheExpiration, utils.GetApplicationLatestRevision(parentApplicationToReport)); err != nil { logCtx.WithError(err).Warn("failed to cache resource event") } @@ -328,7 +329,7 @@ func (s *applicationEventReporter) processResource( } func (s *applicationEventReporter) getResourceActualState(ctx context.Context, logCtx *log.Entry, metricsEventType metrics.MetricEventType, rs appv1.ResourceStatus, parentApplication *appv1.Application, childApplication *appv1.Application) (*application.ApplicationResourceResponse, error) { - if isApp(rs) { + if utils.IsApp(rs) { if childApplication.IsEmptyTypeMeta() { // make sure there is type meta on object childApplication.SetDefaultTypeMeta() diff --git a/event_reporter/reporter/application_event_reporter_test.go b/event_reporter/reporter/application_event_reporter_test.go index fc8575df00d8bf..0863cb1ffb8dde 100644 --- a/event_reporter/reporter/application_event_reporter_test.go +++ b/event_reporter/reporter/application_event_reporter_test.go @@ -20,11 +20,6 @@ import ( "time" "github.com/argoproj/argo-cd/v2/event_reporter/metrics" - "github.com/argoproj/gitops-engine/pkg/health" - - "github.com/ghodss/yaml" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "google.golang.org/grpc" "k8s.io/apimachinery/pkg/runtime" @@ -45,7 +40,6 @@ import ( "github.com/argoproj/argo-cd/v2/pkg/apiclient/events" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "github.com/argoproj/argo-cd/v2/pkg/codefresh" - repoApiclient "github.com/argoproj/argo-cd/v2/reposerver/apiclient" "github.com/argoproj/argo-cd/v2/util/argo" ) @@ -53,166 +47,6 @@ const ( testNamespace = "default" ) -func TestGetResourceEventPayload(t *testing.T) { - t.Run("Deleting timestamp is empty", func(t *testing.T) { - - app := v1alpha1.Application{} - rs := v1alpha1.ResourceStatus{} - - man := "{ \"key\" : \"manifest\" }" - - actualState := application.ApplicationResourceResponse{ - Manifest: &man, - } - desiredState := repoApiclient.Manifest{ - CompiledManifest: "{ \"key\" : \"manifest\" }", - } - appTree := v1alpha1.ApplicationTree{} - revisionMetadata := v1alpha1.RevisionMetadata{ - Author: "demo usert", - Date: metav1.Time{}, - Message: "some message", - } - - event, err := getResourceEventPayload(&app, &rs, &actualState, &desiredState, &appTree, true, "", nil, &revisionMetadata, nil, common.LabelKeyAppInstance, argo.TrackingMethodLabel, &repoApiclient.ApplicationVersions{}) - assert.NoError(t, err) - - var eventPayload events.EventPayload - - err = json.Unmarshal(event.Payload, &eventPayload) - assert.NoError(t, err) - - assert.Equal(t, "{ \"key\" : \"manifest\" }", eventPayload.Source.DesiredManifest) - assert.Equal(t, "{ \"key\" : \"manifest\" }", eventPayload.Source.ActualManifest) - }) - - t.Run("Deleting timestamp is empty", func(t *testing.T) { - - app := v1alpha1.Application{ - ObjectMeta: metav1.ObjectMeta{ - DeletionTimestamp: &metav1.Time{}, - }, - Status: v1alpha1.ApplicationStatus{}, - } - rs := v1alpha1.ResourceStatus{} - man := "{ \"key\" : \"manifest\" }" - actualState := application.ApplicationResourceResponse{ - Manifest: &man, - } - desiredState := repoApiclient.Manifest{ - CompiledManifest: "{ \"key\" : \"manifest\" }", - } - appTree := v1alpha1.ApplicationTree{} - revisionMetadata := v1alpha1.RevisionMetadata{ - Author: "demo usert", - Date: metav1.Time{}, - Message: "some message", - } - - event, err := getResourceEventPayload(&app, &rs, &actualState, &desiredState, &appTree, true, "", nil, &revisionMetadata, nil, common.LabelKeyAppInstance, argo.TrackingMethodLabel, &repoApiclient.ApplicationVersions{}) - assert.NoError(t, err) - - var eventPayload events.EventPayload - - err = json.Unmarshal(event.Payload, &eventPayload) - assert.NoError(t, err) - - assert.Equal(t, "", eventPayload.Source.DesiredManifest) - assert.Equal(t, "", eventPayload.Source.ActualManifest) - }) -} - -func TestGetApplicationLatestRevision(t *testing.T) { - appRevision := "a-revision" - history1Revision := "history-revision-1" - history2Revision := "history-revision-2" - - t.Run("resource revision should be taken from sync.revision", func(t *testing.T) { - noStatusHistoryAppMock := v1alpha1.Application{ - Status: v1alpha1.ApplicationStatus{ - Sync: v1alpha1.SyncStatus{ - Revision: appRevision, - }, - }, - } - - revisionResult := getApplicationLatestRevision(&noStatusHistoryAppMock) - assert.Equal(t, revisionResult, appRevision) - - emptyStatusHistoryAppMock := v1alpha1.Application{ - Status: v1alpha1.ApplicationStatus{ - Sync: v1alpha1.SyncStatus{ - Revision: appRevision, - }, - History: []v1alpha1.RevisionHistory{}, - }, - } - - revision2Result := getApplicationLatestRevision(&emptyStatusHistoryAppMock) - assert.Equal(t, revision2Result, appRevision) - }) - - t.Run("resource revision should be taken from latest history.revision", func(t *testing.T) { - appMock := v1alpha1.Application{ - Status: v1alpha1.ApplicationStatus{ - Sync: v1alpha1.SyncStatus{ - Revision: appRevision, - }, - History: []v1alpha1.RevisionHistory{ - { - Revision: history1Revision, - }, - { - Revision: history2Revision, - }, - }, - }, - } - - revisionResult := getApplicationLatestRevision(&appMock) - assert.Equal(t, revisionResult, history2Revision) - }) -} - -func TestGetLatestAppHistoryId(t *testing.T) { - history1Id := int64(1) - history2Id := int64(2) - - t.Run("resource revision should be 0", func(t *testing.T) { - noStatusHistoryAppMock := v1alpha1.Application{} - - idResult := getLatestAppHistoryId(&noStatusHistoryAppMock) - assert.Equal(t, idResult, int64(0)) - - emptyStatusHistoryAppMock := v1alpha1.Application{ - Status: v1alpha1.ApplicationStatus{ - History: []v1alpha1.RevisionHistory{}, - }, - } - - id2Result := getLatestAppHistoryId(&emptyStatusHistoryAppMock) - assert.Equal(t, id2Result, int64(0)) - }) - - t.Run("resource revision should be taken from latest history.Id", func(t *testing.T) { - appMock := v1alpha1.Application{ - Status: v1alpha1.ApplicationStatus{ - History: []v1alpha1.RevisionHistory{ - { - ID: history1Id, - }, - { - ID: history2Id, - }, - }, - }, - } - - revisionResult := getLatestAppHistoryId(&appMock) - assert.Equal(t, revisionResult, history2Id) - }) -} - func newAppLister(objects ...runtime.Object) applisters.ApplicationLister { fakeAppsClientset := fakeapps.NewSimpleClientset(objects...) factory := appinformer.NewSharedInformerFactoryWithOptions(fakeAppsClientset, 0, appinformer.WithNamespace(""), appinformer.WithTweakListOptions(func(options *metav1.ListOptions) {})) @@ -395,163 +229,6 @@ func TestStreamApplicationEvent(t *testing.T) { } -func TestGetResourceEventPayloadWithoutRevision(t *testing.T) { - app := v1alpha1.Application{} - rs := v1alpha1.ResourceStatus{} - - mf := "{ \"key\" : \"manifest\" }" - - actualState := application.ApplicationResourceResponse{ - Manifest: &mf, - } - desiredState := repoApiclient.Manifest{ - CompiledManifest: "{ \"key\" : \"manifest\" }", - } - appTree := v1alpha1.ApplicationTree{} - - _, err := getResourceEventPayload(&app, &rs, &actualState, &desiredState, &appTree, true, "", nil, nil, nil, common.LabelKeyAppInstance, argo.TrackingMethodLabel, &repoApiclient.ApplicationVersions{}) - assert.NoError(t, err) - -} - -func StrToUnstructured(jsonStr string) *unstructured.Unstructured { - obj := make(map[string]interface{}) - err := yaml.Unmarshal([]byte(jsonStr), &obj) - if err != nil { - panic(err) - } - return &unstructured.Unstructured{Object: obj} -} - -func TestAddCommitDetailsToLabels(t *testing.T) { - revisionMetadata := v1alpha1.RevisionMetadata{ - Author: "demo usert", - Date: metav1.Time{}, - Message: "some message", - } - - t.Run("set labels when lable object missing", func(t *testing.T) { - resource := StrToUnstructured(` - apiVersion: v1 - kind: Service - metadata: - name: helm-guestbook - namespace: default - resourceVersion: "123" - uid: "4" - spec: - selector: - app: guestbook - type: LoadBalancer - status: - loadBalancer: - ingress: - - hostname: localhost`, - ) - - result := addCommitDetailsToLabels(resource, &revisionMetadata) - labels := result.GetLabels() - assert.Equal(t, revisionMetadata.Author, labels["app.meta.commit-author"]) - assert.Equal(t, revisionMetadata.Message, labels["app.meta.commit-message"]) - }) - - t.Run("set labels when labels present", func(t *testing.T) { - resource := StrToUnstructured(` - apiVersion: v1 - kind: Service - metadata: - name: helm-guestbook - namespace: default - labels: - link: http://my-grafana.com/pre-generated-link - spec: - selector: - app: guestbook - type: LoadBalancer - status: - loadBalancer: - ingress: - - hostname: localhost`, - ) - - result := addCommitDetailsToLabels(resource, &revisionMetadata) - labels := result.GetLabels() - assert.Equal(t, revisionMetadata.Author, labels["app.meta.commit-author"]) - assert.Equal(t, revisionMetadata.Message, labels["app.meta.commit-message"]) - assert.Equal(t, "http://my-grafana.com/pre-generated-link", result.GetLabels()["link"]) - }) -} - -func TestSetHealthStatusIfMissing(t *testing.T) { - resource := appsv1.ResourceStatus{Status: appsv1.SyncStatusCodeSynced} - setHealthStatusIfMissing(&resource) - assert.Equal(t, resource.Health.Status, health.HealthStatusHealthy) -} - -func TestGetParentAppIdentityWithinNonControllerNs(t *testing.T) { - resourceTracking := argo.NewResourceTracking() - annotations := make(map[string]string) - constrollerNs := "runtime" - expectedName := "guestbook" - expectedNamespace := "test-apps" - - guestbookApp := appsv1.Application{ - TypeMeta: metav1.TypeMeta{ - Kind: "Application", - APIVersion: "argoproj.io/v1alpha1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: expectedName, - Namespace: expectedNamespace, - }, - } - annotations[common.AnnotationKeyAppInstance] = resourceTracking.BuildAppInstanceValue(argo.AppInstanceValue{ - Name: "test", - ApplicationName: guestbookApp.InstanceName(constrollerNs), - Group: "group", - Kind: "Rollout", - Namespace: "test-resources", - }) - guestbookApp.Annotations = annotations - - res := getParentAppIdentity(&guestbookApp, common.LabelKeyAppInstance, "annotation") - - assert.Equal(t, expectedName, res.name) - assert.Equal(t, expectedNamespace, res.namespace) -} - -func TestGetParentAppIdentityWithinControllerNs(t *testing.T) { - resourceTracking := argo.NewResourceTracking() - annotations := make(map[string]string) - constrollerNs := "runtime" - expectedName := "guestbook" - expectedNamespace := "" - - guestbookApp := appsv1.Application{ - TypeMeta: metav1.TypeMeta{ - Kind: "Application", - APIVersion: "argoproj.io/v1alpha1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: expectedName, - Namespace: constrollerNs, - }, - } - annotations[common.AnnotationKeyAppInstance] = resourceTracking.BuildAppInstanceValue(argo.AppInstanceValue{ - Name: "test", - ApplicationName: guestbookApp.InstanceName(constrollerNs), - Group: "group", - Kind: "Rollout", - Namespace: "test-resources", - }) - guestbookApp.Annotations = annotations - - res := getParentAppIdentity(&guestbookApp, common.LabelKeyAppInstance, "annotation") - - assert.Equal(t, expectedName, res.name) - assert.Equal(t, expectedNamespace, res.namespace) -} - func TestShouldSendApplicationEvent(t *testing.T) { eventReporter := fakeReporter(fakeAppServiceClient()) diff --git a/event_reporter/reporter/event_payload.go b/event_reporter/reporter/event_payload.go index 19c3521c9f8cfc..36a47ea8a7471f 100644 --- a/event_reporter/reporter/event_payload.go +++ b/event_reporter/reporter/event_payload.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "github.com/argoproj/argo-cd/v2/event_reporter/utils" "github.com/argoproj/argo-cd/v2/pkg/apiclient/application" "github.com/argoproj/argo-cd/v2/pkg/apiclient/events" appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" @@ -51,7 +52,7 @@ func getResourceEventPayload( actualObject, err := appv1.UnmarshalToUnstructured(*actualState.Manifest) if err == nil { - actualObject = addCommitDetailsToLabels(actualObject, originalAppRevisionMetadata) + actualObject = utils.AddCommitDetailsToLabels(actualObject, originalAppRevisionMetadata) object, err = actualObject.MarshalJSON() if err != nil { return nil, fmt.Errorf("failed to marshal unstructured object: %w", err) @@ -72,7 +73,7 @@ func getResourceEventPayload( u.SetName(rs.Name) u.SetNamespace(rs.Namespace) if originalAppRevisionMetadata != nil { - u = addCommitDetailsToLabels(u, originalAppRevisionMetadata) + u = utils.AddCommitDetailsToLabels(u, originalAppRevisionMetadata) } object, err = u.MarshalJSON() @@ -81,12 +82,12 @@ func getResourceEventPayload( } } else { // no actual state, use desired state as event object - unstructuredWithNamespace, err := addDestNamespaceToManifest([]byte(desiredState.CompiledManifest), rs) + unstructuredWithNamespace, err := utils.AddDestNamespaceToManifest([]byte(desiredState.CompiledManifest), rs) if err != nil { return nil, fmt.Errorf("failed to add destination namespace to manifest: %w", err) } if originalAppRevisionMetadata != nil { - unstructuredWithNamespace = addCommitDetailsToLabels(unstructuredWithNamespace, originalAppRevisionMetadata) + unstructuredWithNamespace = utils.AddCommitDetailsToLabels(unstructuredWithNamespace, originalAppRevisionMetadata) } object, _ = unstructuredWithNamespace.MarshalJSON() @@ -133,7 +134,7 @@ func getResourceEventPayload( } } - applicationVersionsEvents, err := repoAppVersionsToEvent(applicationVersions) + applicationVersionsEvents, err := utils.RepoAppVersionsToEvent(applicationVersions) if err != nil { logCtx.Errorf("failed to convert appVersions: %v", err) } @@ -144,9 +145,9 @@ func getResourceEventPayload( GitManifest: desiredState.RawManifest, RepoURL: parentApplication.Status.Sync.ComparedTo.Source.RepoURL, Path: desiredState.Path, - Revision: getApplicationLatestRevision(parentApplication), - OperationSyncRevision: getOperationRevision(parentApplication), - HistoryId: getLatestAppHistoryId(parentApplication), + Revision: utils.GetApplicationLatestRevision(parentApplication), + OperationSyncRevision: utils.GetOperationRevision(parentApplication), + HistoryId: utils.GetLatestAppHistoryId(parentApplication), AppName: parentApplication.Name, AppNamespace: parentApplication.Namespace, AppUID: string(parentApplication.ObjectMeta.UID), @@ -181,7 +182,7 @@ func getResourceEventPayload( AppVersions: applicationVersionsEvents, } - logCtx.Infof("AppVersion before encoding: %v", safeString(payload.AppVersions.AppVersion)) + logCtx.Infof("AppVersion before encoding: %v", utils.SafeString(payload.AppVersions.AppVersion)) payloadBytes, err := json.Marshal(&payload) if err != nil { @@ -218,7 +219,7 @@ func (s *applicationEventReporter) getApplicationEventPayload( applicationSource := a.Spec.GetSource() if !applicationSource.IsHelm() && (a.Status.Sync.Revision != "" || (a.Status.History != nil && len(a.Status.History) > 0)) { - revisionMetadata, err := s.getApplicationRevisionDetails(ctx, a, getOperationRevision(a)) + revisionMetadata, err := s.getApplicationRevisionDetails(ctx, a, utils.GetOperationRevision(a)) if err != nil { if !strings.Contains(err.Error(), "not found") { @@ -248,7 +249,7 @@ func (s *applicationEventReporter) getApplicationEventPayload( logCtx.Info("reporting application deletion event") } - applicationVersionsEvents, err := repoAppVersionsToEvent(applicationVersions) + applicationVersionsEvents, err := utils.RepoAppVersionsToEvent(applicationVersions) if err != nil { logCtx.Errorf("failed to convert appVersions: %v", err) } @@ -286,7 +287,7 @@ func (s *applicationEventReporter) getApplicationEventPayload( AppVersions: applicationVersionsEvents, } - logCtx.Infof("AppVersion before encoding: %v", safeString(payload.AppVersions.AppVersion)) + logCtx.Infof("AppVersion before encoding: %v", utils.SafeString(payload.AppVersions.AppVersion)) payloadBytes, err := json.Marshal(&payload) if err != nil { diff --git a/event_reporter/reporter/event_payload_test.go b/event_reporter/reporter/event_payload_test.go new file mode 100644 index 00000000000000..23fa7d8a974629 --- /dev/null +++ b/event_reporter/reporter/event_payload_test.go @@ -0,0 +1,102 @@ +package reporter + +import ( + "encoding/json" + "github.com/argoproj/argo-cd/v2/common" + "github.com/argoproj/argo-cd/v2/pkg/apiclient/application" + "github.com/argoproj/argo-cd/v2/pkg/apiclient/events" + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + repoApiclient "github.com/argoproj/argo-cd/v2/reposerver/apiclient" + "github.com/argoproj/argo-cd/v2/util/argo" + "github.com/stretchr/testify/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "testing" +) + +func TestGetResourceEventPayload(t *testing.T) { + t.Run("Deleting timestamp is empty", func(t *testing.T) { + + app := v1alpha1.Application{} + rs := v1alpha1.ResourceStatus{} + + man := "{ \"key\" : \"manifest\" }" + + actualState := application.ApplicationResourceResponse{ + Manifest: &man, + } + desiredState := repoApiclient.Manifest{ + CompiledManifest: "{ \"key\" : \"manifest\" }", + } + appTree := v1alpha1.ApplicationTree{} + revisionMetadata := v1alpha1.RevisionMetadata{ + Author: "demo usert", + Date: metav1.Time{}, + Message: "some message", + } + + event, err := getResourceEventPayload(&app, &rs, &actualState, &desiredState, &appTree, true, "", nil, &revisionMetadata, nil, common.LabelKeyAppInstance, argo.TrackingMethodLabel, &repoApiclient.ApplicationVersions{}) + assert.NoError(t, err) + + var eventPayload events.EventPayload + + err = json.Unmarshal(event.Payload, &eventPayload) + assert.NoError(t, err) + + assert.Equal(t, "{ \"key\" : \"manifest\" }", eventPayload.Source.DesiredManifest) + assert.Equal(t, "{ \"key\" : \"manifest\" }", eventPayload.Source.ActualManifest) + }) + + t.Run("Deleting timestamp is empty", func(t *testing.T) { + + app := v1alpha1.Application{ + ObjectMeta: metav1.ObjectMeta{ + DeletionTimestamp: &metav1.Time{}, + }, + Status: v1alpha1.ApplicationStatus{}, + } + rs := v1alpha1.ResourceStatus{} + man := "{ \"key\" : \"manifest\" }" + actualState := application.ApplicationResourceResponse{ + Manifest: &man, + } + desiredState := repoApiclient.Manifest{ + CompiledManifest: "{ \"key\" : \"manifest\" }", + } + appTree := v1alpha1.ApplicationTree{} + revisionMetadata := v1alpha1.RevisionMetadata{ + Author: "demo usert", + Date: metav1.Time{}, + Message: "some message", + } + + event, err := getResourceEventPayload(&app, &rs, &actualState, &desiredState, &appTree, true, "", nil, &revisionMetadata, nil, common.LabelKeyAppInstance, argo.TrackingMethodLabel, &repoApiclient.ApplicationVersions{}) + assert.NoError(t, err) + + var eventPayload events.EventPayload + + err = json.Unmarshal(event.Payload, &eventPayload) + assert.NoError(t, err) + + assert.Equal(t, "", eventPayload.Source.DesiredManifest) + assert.Equal(t, "", eventPayload.Source.ActualManifest) + }) +} + +func TestGetResourceEventPayloadWithoutRevision(t *testing.T) { + app := v1alpha1.Application{} + rs := v1alpha1.ResourceStatus{} + + mf := "{ \"key\" : \"manifest\" }" + + actualState := application.ApplicationResourceResponse{ + Manifest: &mf, + } + desiredState := repoApiclient.Manifest{ + CompiledManifest: "{ \"key\" : \"manifest\" }", + } + appTree := v1alpha1.ApplicationTree{} + + _, err := getResourceEventPayload(&app, &rs, &actualState, &desiredState, &appTree, true, "", nil, nil, nil, common.LabelKeyAppInstance, argo.TrackingMethodLabel, &repoApiclient.ApplicationVersions{}) + assert.NoError(t, err) + +} diff --git a/event_reporter/reporter/app_instance_helpers.go b/event_reporter/utils/app_instance.go similarity index 67% rename from event_reporter/reporter/app_instance_helpers.go rename to event_reporter/utils/app_instance.go index da5591af7ff797..3239f9c32f7647 100644 --- a/event_reporter/reporter/app_instance_helpers.go +++ b/event_reporter/utils/app_instance.go @@ -1,4 +1,4 @@ -package reporter +package utils import ( appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" @@ -10,8 +10,8 @@ import ( const appInstanceNameDelimeter = "_" type AppIdentity struct { - name string - namespace string + Name string + Namespace string } // logic connected to /argo-cd/pkg/apis/application/v1alpha1/types.go - InstanceName @@ -20,18 +20,18 @@ func instanceNameIncludesNs(instanceName string) bool { } // logic connected to /argo-cd/pkg/apis/application/v1alpha1/types.go - InstanceName -func parseInstanceName(appNameString string) AppIdentity { +func parseInstanceName(appNameString string) *AppIdentity { parts := strings.Split(appNameString, appInstanceNameDelimeter) namespace := parts[0] app := parts[1] - return AppIdentity{ - name: app, - namespace: namespace, + return &AppIdentity{ + Name: app, + Namespace: namespace, } } -func getParentAppIdentity(a *appv1.Application, appInstanceLabelKey string, trackingMethod appv1.TrackingMethod) AppIdentity { +func GetParentAppIdentity(a *appv1.Application, appInstanceLabelKey string, trackingMethod appv1.TrackingMethod) *AppIdentity { resourceTracking := argo.NewResourceTracking() unApp := kube.MustToUnstructured(&a) @@ -39,14 +39,14 @@ func getParentAppIdentity(a *appv1.Application, appInstanceLabelKey string, trac if instanceNameIncludesNs(instanceName) { return parseInstanceName(instanceName) - } else { - return AppIdentity{ - name: instanceName, - namespace: "", - } + } + + return &AppIdentity{ + Name: instanceName, + Namespace: "", } } -func isChildApp(parentApp AppIdentity) bool { - return parentApp.name != "" +func IsChildApp(parentApp *AppIdentity) bool { + return parentApp.Name != "" } diff --git a/event_reporter/utils/app_instance_test.go b/event_reporter/utils/app_instance_test.go new file mode 100644 index 00000000000000..6cddf9282fc29d --- /dev/null +++ b/event_reporter/utils/app_instance_test.go @@ -0,0 +1,74 @@ +package utils + +import ( + "github.com/argoproj/argo-cd/v2/common" + appsv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + "github.com/argoproj/argo-cd/v2/util/argo" + "github.com/stretchr/testify/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "testing" +) + +func TestGetParentAppIdentityWithinNonControllerNs(t *testing.T) { + resourceTracking := argo.NewResourceTracking() + annotations := make(map[string]string) + constrollerNs := "runtime" + expectedName := "guestbook" + expectedNamespace := "test-apps" + + guestbookApp := appsv1.Application{ + TypeMeta: metav1.TypeMeta{ + Kind: "Application", + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: expectedName, + Namespace: expectedNamespace, + }, + } + annotations[common.AnnotationKeyAppInstance] = resourceTracking.BuildAppInstanceValue(argo.AppInstanceValue{ + Name: "test", + ApplicationName: guestbookApp.InstanceName(constrollerNs), + Group: "group", + Kind: "Rollout", + Namespace: "test-resources", + }) + guestbookApp.Annotations = annotations + + res := GetParentAppIdentity(&guestbookApp, common.LabelKeyAppInstance, "annotation") + + assert.Equal(t, expectedName, res.Name) + assert.Equal(t, expectedNamespace, res.Namespace) +} + +func TestGetParentAppIdentityWithinControllerNs(t *testing.T) { + resourceTracking := argo.NewResourceTracking() + annotations := make(map[string]string) + constrollerNs := "runtime" + expectedName := "guestbook" + expectedNamespace := "" + + guestbookApp := appsv1.Application{ + TypeMeta: metav1.TypeMeta{ + Kind: "Application", + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: expectedName, + Namespace: constrollerNs, + }, + } + annotations[common.AnnotationKeyAppInstance] = resourceTracking.BuildAppInstanceValue(argo.AppInstanceValue{ + Name: "test", + ApplicationName: guestbookApp.InstanceName(constrollerNs), + Group: "group", + Kind: "Rollout", + Namespace: "test-resources", + }) + guestbookApp.Annotations = annotations + + res := GetParentAppIdentity(&guestbookApp, common.LabelKeyAppInstance, "annotation") + + assert.Equal(t, expectedName, res.Name) + assert.Equal(t, expectedNamespace, res.Namespace) +} diff --git a/event_reporter/utils/app_revision.go b/event_reporter/utils/app_revision.go new file mode 100644 index 00000000000000..7c1f2dfebec654 --- /dev/null +++ b/event_reporter/utils/app_revision.go @@ -0,0 +1,67 @@ +package utils + +import ( + appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +type AppSyncRevisionsMetadata struct { + revisions []*appv1.RevisionMetadata + syncRevisions []*appv1.RevisionMetadata +} + +func GetLatestAppHistoryId(a *appv1.Application) int64 { + if lastHistory := getLatestAppHistoryItem(a); lastHistory != nil { + return lastHistory.ID + } + + return 0 +} + +func getLatestAppHistoryItem(a *appv1.Application) *appv1.RevisionHistory { + if a.Status.History != nil && len(a.Status.History) > 0 { + return &a.Status.History[len(a.Status.History)-1] + } + + return nil +} + +func GetApplicationLatestRevision(a *appv1.Application) string { + if lastHistory := getLatestAppHistoryItem(a); lastHistory != nil { + return lastHistory.Revision + } + + return a.Status.Sync.Revision +} + +func GetOperationRevision(a *appv1.Application) string { + if a == nil { + return "" + } + + // 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 != "" { + revision = a.Status.OperationState.Operation.Sync.Revision + } else if a.Operation != nil && a.Operation.Sync != nil && a.Operation.Sync.Revision != "" { + revision = a.Operation.Sync.Revision + } + + return revision +} + +func AddCommitDetailsToLabels(u *unstructured.Unstructured, revisionMetadata *appv1.RevisionMetadata) *unstructured.Unstructured { + if revisionMetadata == nil || u == nil { + return u + } + + if field, _, _ := unstructured.NestedFieldCopy(u.Object, "metadata", "labels"); field == nil { + _ = unstructured.SetNestedStringMap(u.Object, map[string]string{}, "metadata", "labels") + } + + _ = unstructured.SetNestedField(u.Object, revisionMetadata.Date.Format("2006-01-02T15:04:05.000Z"), "metadata", "labels", "app.meta.commit-date") + _ = unstructured.SetNestedField(u.Object, revisionMetadata.Author, "metadata", "labels", "app.meta.commit-author") + _ = unstructured.SetNestedField(u.Object, revisionMetadata.Message, "metadata", "labels", "app.meta.commit-message") + + return u +} diff --git a/event_reporter/utils/app_revision_test.go b/event_reporter/utils/app_revision_test.go new file mode 100644 index 00000000000000..39d2d8fea19d7e --- /dev/null +++ b/event_reporter/utils/app_revision_test.go @@ -0,0 +1,169 @@ +package utils + +import ( + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + "github.com/stretchr/testify/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "sigs.k8s.io/yaml" + "testing" +) + +func TestGetLatestAppHistoryId(t *testing.T) { + history1Id := int64(1) + history2Id := int64(2) + + t.Run("resource revision should be 0", func(t *testing.T) { + noStatusHistoryAppMock := v1alpha1.Application{} + + idResult := GetLatestAppHistoryId(&noStatusHistoryAppMock) + assert.Equal(t, idResult, int64(0)) + + emptyStatusHistoryAppMock := v1alpha1.Application{ + Status: v1alpha1.ApplicationStatus{ + History: []v1alpha1.RevisionHistory{}, + }, + } + + id2Result := GetLatestAppHistoryId(&emptyStatusHistoryAppMock) + assert.Equal(t, id2Result, int64(0)) + }) + + t.Run("resource revision should be taken from latest history.Id", func(t *testing.T) { + appMock := v1alpha1.Application{ + Status: v1alpha1.ApplicationStatus{ + History: []v1alpha1.RevisionHistory{ + { + ID: history1Id, + }, + { + ID: history2Id, + }, + }, + }, + } + + revisionResult := GetLatestAppHistoryId(&appMock) + assert.Equal(t, revisionResult, history2Id) + }) +} + +func TestGetApplicationLatestRevision(t *testing.T) { + appRevision := "a-revision" + history1Revision := "history-revision-1" + history2Revision := "history-revision-2" + + t.Run("resource revision should be taken from sync.revision", func(t *testing.T) { + noStatusHistoryAppMock := v1alpha1.Application{ + Status: v1alpha1.ApplicationStatus{ + Sync: v1alpha1.SyncStatus{ + Revision: appRevision, + }, + }, + } + + revisionResult := GetApplicationLatestRevision(&noStatusHistoryAppMock) + assert.Equal(t, revisionResult, appRevision) + + emptyStatusHistoryAppMock := v1alpha1.Application{ + Status: v1alpha1.ApplicationStatus{ + Sync: v1alpha1.SyncStatus{ + Revision: appRevision, + }, + History: []v1alpha1.RevisionHistory{}, + }, + } + + revision2Result := GetApplicationLatestRevision(&emptyStatusHistoryAppMock) + assert.Equal(t, revision2Result, appRevision) + }) + + t.Run("resource revision should be taken from latest history.revision", func(t *testing.T) { + appMock := v1alpha1.Application{ + Status: v1alpha1.ApplicationStatus{ + Sync: v1alpha1.SyncStatus{ + Revision: appRevision, + }, + History: []v1alpha1.RevisionHistory{ + { + Revision: history1Revision, + }, + { + Revision: history2Revision, + }, + }, + }, + } + + revisionResult := GetApplicationLatestRevision(&appMock) + assert.Equal(t, revisionResult, history2Revision) + }) +} + +func StrToUnstructured(jsonStr string) *unstructured.Unstructured { + obj := make(map[string]interface{}) + err := yaml.Unmarshal([]byte(jsonStr), &obj) + if err != nil { + panic(err) + } + return &unstructured.Unstructured{Object: obj} +} + +func TestAddCommitDetailsToLabels(t *testing.T) { + revisionMetadata := v1alpha1.RevisionMetadata{ + Author: "demo usert", + Date: metav1.Time{}, + Message: "some message", + } + + t.Run("set labels when lable object missing", func(t *testing.T) { + resource := StrToUnstructured(` + apiVersion: v1 + kind: Service + metadata: + name: helm-guestbook + namespace: default + resourceVersion: "123" + uid: "4" + spec: + selector: + app: guestbook + type: LoadBalancer + status: + loadBalancer: + ingress: + - hostname: localhost`, + ) + + result := AddCommitDetailsToLabels(resource, &revisionMetadata) + labels := result.GetLabels() + assert.Equal(t, revisionMetadata.Author, labels["app.meta.commit-author"]) + assert.Equal(t, revisionMetadata.Message, labels["app.meta.commit-message"]) + }) + + t.Run("set labels when labels present", func(t *testing.T) { + resource := StrToUnstructured(` + apiVersion: v1 + kind: Service + metadata: + name: helm-guestbook + namespace: default + labels: + link: http://my-grafana.com/pre-generated-link + spec: + selector: + app: guestbook + type: LoadBalancer + status: + loadBalancer: + ingress: + - hostname: localhost`, + ) + + result := AddCommitDetailsToLabels(resource, &revisionMetadata) + labels := result.GetLabels() + assert.Equal(t, revisionMetadata.Author, labels["app.meta.commit-author"]) + assert.Equal(t, revisionMetadata.Message, labels["app.meta.commit-message"]) + assert.Equal(t, "http://my-grafana.com/pre-generated-link", result.GetLabels()["link"]) + }) +} diff --git a/event_reporter/reporter/app_version_helpers.go b/event_reporter/utils/app_version.go similarity index 85% rename from event_reporter/reporter/app_version_helpers.go rename to event_reporter/utils/app_version.go index c83cd10151a69a..60669881518774 100644 --- a/event_reporter/reporter/app_version_helpers.go +++ b/event_reporter/utils/app_version.go @@ -1,4 +1,4 @@ -package reporter +package utils import ( "encoding/json" @@ -6,7 +6,7 @@ import ( "github.com/argoproj/argo-cd/v2/reposerver/apiclient" ) -func repoAppVersionsToEvent(applicationVersions *apiclient.ApplicationVersions) (*events.ApplicationVersions, error) { +func RepoAppVersionsToEvent(applicationVersions *apiclient.ApplicationVersions) (*events.ApplicationVersions, error) { applicationVersionsEvents := &events.ApplicationVersions{} applicationVersionsData, _ := json.Marshal(applicationVersions) err := json.Unmarshal(applicationVersionsData, applicationVersionsEvents) diff --git a/event_reporter/reporter/helpers.go b/event_reporter/utils/utils.go similarity index 81% rename from event_reporter/reporter/helpers.go rename to event_reporter/utils/utils.go index 6d2a174332c87e..7c824e4e5be91d 100644 --- a/event_reporter/reporter/helpers.go +++ b/event_reporter/utils/utils.go @@ -1,4 +1,4 @@ -package reporter +package utils import ( "fmt" @@ -8,7 +8,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) -func setHealthStatusIfMissing(rs *appv1.ResourceStatus) { +func SetHealthStatusIfMissing(rs *appv1.ResourceStatus) { if rs.Health == nil && rs.Status == appv1.SyncStatusCodeSynced { // for resources without health status we need to add 'Healthy' status // when they are synced because we might have sent an event with 'Missing' @@ -19,11 +19,11 @@ func setHealthStatusIfMissing(rs *appv1.ResourceStatus) { } } -func isApp(rs appv1.ResourceStatus) bool { +func IsApp(rs appv1.ResourceStatus) bool { return rs.GroupVersionKind().String() == appv1.ApplicationSchemaGroupVersionKind.String() } -func logWithAppStatus(a *appv1.Application, logCtx *log.Entry, ts string) *log.Entry { +func LogWithAppStatus(a *appv1.Application, logCtx *log.Entry, ts string) *log.Entry { return logCtx.WithFields(log.Fields{ "sync": a.Status.Sync.Status, "health": a.Status.Health.Status, @@ -32,7 +32,7 @@ func logWithAppStatus(a *appv1.Application, logCtx *log.Entry, ts string) *log.E }) } -func logWithResourceStatus(logCtx *log.Entry, rs appv1.ResourceStatus) *log.Entry { +func LogWithResourceStatus(logCtx *log.Entry, rs appv1.ResourceStatus) *log.Entry { logCtx = logCtx.WithField("sync", rs.Status) if rs.Health != nil { logCtx = logCtx.WithField("health", rs.Health.Status) @@ -41,14 +41,14 @@ func logWithResourceStatus(logCtx *log.Entry, rs appv1.ResourceStatus) *log.Entr return logCtx } -func safeString(s *string) string { +func SafeString(s *string) string { if s == nil { return "" } return *s } -func getAppAsResource(a *appv1.Application) *appv1.ResourceStatus { +func GetAppAsResource(a *appv1.Application) *appv1.ResourceStatus { return &appv1.ResourceStatus{ Name: a.Name, Namespace: a.Namespace, @@ -61,7 +61,7 @@ func getAppAsResource(a *appv1.Application) *appv1.ResourceStatus { } } -func addDestNamespaceToManifest(resourceManifest []byte, rs *appv1.ResourceStatus) (*unstructured.Unstructured, error) { +func AddDestNamespaceToManifest(resourceManifest []byte, rs *appv1.ResourceStatus) (*unstructured.Unstructured, error) { u, err := appv1.UnmarshalToUnstructured(string(resourceManifest)) if err != nil { return nil, fmt.Errorf("failed to unmarshal manifest: %w", err) diff --git a/event_reporter/utils/utils_test.go b/event_reporter/utils/utils_test.go new file mode 100644 index 00000000000000..ad6ee4e5221ff9 --- /dev/null +++ b/event_reporter/utils/utils_test.go @@ -0,0 +1,14 @@ +package utils + +import ( + appsv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + "github.com/argoproj/gitops-engine/pkg/health" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestSetHealthStatusIfMissing(t *testing.T) { + resource := appsv1.ResourceStatus{Status: appsv1.SyncStatusCodeSynced} + SetHealthStatusIfMissing(&resource) + assert.Equal(t, resource.Health.Status, health.HealthStatusHealthy) +}