From 5af7ecc3072364a8a3487bd455972e4477a59649 Mon Sep 17 00:00:00 2001 From: Oleksandr Saulyak Date: Thu, 7 Nov 2024 17:39:24 +0200 Subject: [PATCH] chore(event-reporter): refactoring, variables grouping, code splitting (#351) * event-reporter: created dedicated func resolveApplicationVersions * event-reporter: added new types for variables grouping * event-reporter: removed redundant code comment * event-reporter(refactoring): params grouping, added new type ArgoTrackingMetadata to pass data between methods * event-reporter(refactoring): params grouping, added new type ReportedEntityParentApp to pass data between methods * event-reporter(refactoring): params grouping, added new type ReportedResource to pass data between methods, created new methods to group logic inside getResourceEventPayload * event-reporter(refactoring): fixed nil pointer issues after refactoring * event-reporter(refactoring): fixed lint issue * event-reporter(refactoring): fixed lint issue * event-reporter: changes after pr review --- event_reporter/controller/controller.go | 5 +- .../reporter/application_event_reporter.go | 87 ++++-- .../application_event_reporter_test.go | 4 +- event_reporter/reporter/event_payload.go | 282 ++++++++++-------- event_reporter/reporter/event_payload_test.go | 45 ++- event_reporter/reporter/types.go | 42 +++ 6 files changed, 299 insertions(+), 166 deletions(-) create mode 100644 event_reporter/reporter/types.go diff --git a/event_reporter/controller/controller.go b/event_reporter/controller/controller.go index 7ce4f100b4411..85aba07fb7b7f 100644 --- a/event_reporter/controller/controller.go +++ b/event_reporter/controller/controller.go @@ -76,7 +76,10 @@ func (c *eventReporterController) Run(ctx context.Context) { } trackingMethod := argoutil.GetTrackingMethod(c.settingsMgr) - err = c.applicationEventReporter.StreamApplicationEvents(ctx, &a, eventProcessingStartedAt, ignoreResourceCache, appInstanceLabelKey, trackingMethod) + err = c.applicationEventReporter.StreamApplicationEvents(ctx, &a, eventProcessingStartedAt, ignoreResourceCache, &reporter.ArgoTrackingMetadata{ + AppInstanceLabelKey: &appInstanceLabelKey, + TrackingMethod: &trackingMethod, + }) if err != nil { return err } diff --git a/event_reporter/reporter/application_event_reporter.go b/event_reporter/reporter/application_event_reporter.go index a64cd1c0a1f82..242784a557111 100644 --- a/event_reporter/reporter/application_event_reporter.go +++ b/event_reporter/reporter/application_event_reporter.go @@ -48,8 +48,7 @@ type ApplicationEventReporter interface { a *appv1.Application, eventProcessingStartedAt string, ignoreResourceCache bool, - appInstanceLabelKey string, - trackingMethod appv1.TrackingMethod, + argoTrackingMetadata *ArgoTrackingMetadata, ) error ShouldSendApplicationEvent(ae *appv1.ApplicationWatchEvent) (shouldSend bool, syncStatusChanged bool) } @@ -113,8 +112,7 @@ func (s *applicationEventReporter) StreamApplicationEvents( a *appv1.Application, eventProcessingStartedAt string, ignoreResourceCache bool, - appInstanceLabelKey string, - trackingMethod appv1.TrackingMethod, + argoTrackingMetadata *ArgoTrackingMetadata, ) error { metricTimer := metricsUtils.NewMetricTimer() @@ -141,18 +139,11 @@ func (s *applicationEventReporter) StreamApplicationEvents( desiredManifests, manifestGenErr := s.getDesiredManifests(ctx, a, nil, logCtx) - syncRevision := utils.GetOperationStateRevision(a) - var applicationVersions *apiclient.ApplicationVersions - if syncRevision != nil { - syncManifests, _ := s.getDesiredManifests(ctx, a, syncRevision, logCtx) - applicationVersions = syncManifests.GetApplicationVersions() - } else { - applicationVersions = nil - } + applicationVersions := s.resolveApplicationVersions(ctx, a, logCtx) logCtx.Info("getting parent application name") - parentAppIdentity := utils.GetParentAppIdentity(a, appInstanceLabelKey, trackingMethod) + parentAppIdentity := utils.GetParentAppIdentity(a, *argoTrackingMetadata.AppInstanceLabelKey, *argoTrackingMetadata.TrackingMethod) if utils.IsChildApp(parentAppIdentity) { logCtx.Info("processing as child application") @@ -165,27 +156,29 @@ func (s *applicationEventReporter) StreamApplicationEvents( } rs := utils.GetAppAsResource(a) + utils.SetHealthStatusIfMissing(rs) parentDesiredManifests, manifestGenErr := s.getDesiredManifests(ctx, parentApplicationEntity, nil, logCtx) - // helm app hasnt revision - // TODO: add check if it helm application parentAppSyncRevisionsMetadata, err := s.getApplicationRevisionsMetadata(ctx, logCtx, parentApplicationEntity) if err != nil { logCtx.WithError(err).Warn("failed to get parent application's revision metadata, resuming") } - utils.SetHealthStatusIfMissing(rs) - err = s.processResource(ctx, *rs, parentApplicationEntity, logCtx, eventProcessingStartedAt, parentDesiredManifests, appTree, manifestGenErr, a, parentAppSyncRevisionsMetadata, appInstanceLabelKey, trackingMethod, applicationVersions) + err = s.processResource(ctx, *rs, logCtx, eventProcessingStartedAt, parentDesiredManifests, manifestGenErr, a, applicationVersions, &ReportedEntityParentApp{ + app: parentApplicationEntity, + appTree: appTree, + revisionsMetadata: parentAppSyncRevisionsMetadata, + }, argoTrackingMetadata) if err != nil { s.metricsServer.IncErroredEventsCounter(metrics.MetricChildAppEventType, metrics.MetricEventUnknownErrorType, a.Name) return err } s.metricsServer.ObserveEventProcessingDurationHistogramDuration(a.Name, metrics.MetricChildAppEventType, metricTimer.Duration()) } else { - logCtx.Info("processing as root application") // will get here only for root applications (not managed as a resource by another application) - appEvent, err := s.getApplicationEventPayload(ctx, a, appTree, eventProcessingStartedAt, appInstanceLabelKey, trackingMethod, applicationVersions) + logCtx.Info("processing as root application") + appEvent, err := s.getApplicationEventPayload(ctx, a, appTree, eventProcessingStartedAt, applicationVersions, argoTrackingMetadata) if err != nil { s.metricsServer.IncErroredEventsCounter(metrics.MetricParentAppEventType, metrics.MetricEventGetPayloadErrorType, a.Name) return fmt.Errorf("failed to get application event: %w", err) @@ -216,7 +209,11 @@ func (s *applicationEventReporter) StreamApplicationEvents( s.metricsServer.IncCachedIgnoredEventsCounter(metrics.MetricResourceEventType, a.Name) continue } - err := s.processResource(ctx, rs, a, logCtx, eventProcessingStartedAt, desiredManifests, appTree, manifestGenErr, nil, revisionsMetadata, appInstanceLabelKey, trackingMethod, nil) + err := s.processResource(ctx, rs, logCtx, eventProcessingStartedAt, desiredManifests, manifestGenErr, nil, nil, &ReportedEntityParentApp{ + app: a, + appTree: appTree, + revisionsMetadata: revisionsMetadata, + }, argoTrackingMetadata) if err != nil { s.metricsServer.IncErroredEventsCounter(metrics.MetricResourceEventType, metrics.MetricEventUnknownErrorType, a.Name) return err @@ -225,6 +222,16 @@ func (s *applicationEventReporter) StreamApplicationEvents( return nil } +func (s *applicationEventReporter) resolveApplicationVersions(ctx context.Context, a *appv1.Application, logCtx *log.Entry) *apiclient.ApplicationVersions { + syncRevision := utils.GetOperationStateRevision(a) + if syncRevision == nil { + return nil + } + + syncManifests, _ := s.getDesiredManifests(ctx, a, syncRevision, logCtx) + return syncManifests.GetApplicationVersions() +} + func (s *applicationEventReporter) getAppForResourceReporting( rs appv1.ResourceStatus, ctx context.Context, @@ -252,17 +259,14 @@ func (s *applicationEventReporter) getAppForResourceReporting( func (s *applicationEventReporter) processResource( ctx context.Context, rs appv1.ResourceStatus, - parentApplication *appv1.Application, logCtx *log.Entry, appEventProcessingStartedAt string, desiredManifests *apiclient.ManifestResponse, - appTree *appv1.ApplicationTree, manifestGenErr bool, - originalApplication *appv1.Application, - revisionsMetadata *utils.AppSyncRevisionsMetadata, - appInstanceLabelKey string, - trackingMethod appv1.TrackingMethod, - applicationVersions *apiclient.ApplicationVersions, + originalApplication *appv1.Application, // passed onlu if resource is app + applicationVersions *apiclient.ApplicationVersions, // passed onlu if resource is app + reportedEntityParentApp *ReportedEntityParentApp, + argoTrackingMetadata *ArgoTrackingMetadata, ) error { metricsEventType := metrics.MetricResourceEventType if utils.IsApp(rs) { @@ -277,7 +281,7 @@ func (s *applicationEventReporter) processResource( // get resource desired state desiredState := getResourceDesiredState(&rs, desiredManifests, logCtx) - actualState, err := s.getResourceActualState(ctx, logCtx, metricsEventType, rs, parentApplication, originalApplication) + actualState, err := s.getResourceActualState(ctx, logCtx, metricsEventType, rs, reportedEntityParentApp.app, originalApplication) if err != nil { return err } @@ -285,7 +289,7 @@ func (s *applicationEventReporter) processResource( return nil } - parentApplicationToReport, revisionMetadataToReport := s.getAppForResourceReporting(rs, ctx, logCtx, parentApplication, revisionsMetadata) + parentApplicationToReport, revisionMetadataToReport := s.getAppForResourceReporting(rs, ctx, logCtx, reportedEntityParentApp.app, reportedEntityParentApp.revisionsMetadata) var originalAppRevisionMetadata *utils.AppSyncRevisionsMetadata = nil @@ -293,9 +297,28 @@ func (s *applicationEventReporter) processResource( originalAppRevisionMetadata, _ = s.getApplicationRevisionsMetadata(ctx, logCtx, originalApplication) } - ev, err := getResourceEventPayload(parentApplicationToReport, &rs, actualState, desiredState, appTree, manifestGenErr, appEventProcessingStartedAt, originalApplication, revisionMetadataToReport, originalAppRevisionMetadata, appInstanceLabelKey, trackingMethod, applicationVersions) + ev, err := getResourceEventPayload( + appEventProcessingStartedAt, + &ReportedResource{ + rs: &rs, + actualState: actualState, + desiredState: desiredState, + manifestGenErr: manifestGenErr, + rsAsAppInfo: &ReportedResourceAsApp{ + app: originalApplication, + revisionsMetadata: originalAppRevisionMetadata, + applicationVersions: applicationVersions, + }, + }, + &ReportedEntityParentApp{ + app: parentApplicationToReport, + appTree: reportedEntityParentApp.appTree, + revisionsMetadata: revisionMetadataToReport, + }, + argoTrackingMetadata, + ) if err != nil { - s.metricsServer.IncErroredEventsCounter(metricsEventType, metrics.MetricEventGetPayloadErrorType, parentApplication.Name) + s.metricsServer.IncErroredEventsCounter(metricsEventType, metrics.MetricEventGetPayloadErrorType, reportedEntityParentApp.app.Name) logCtx.WithError(err).Warn("failed to get event payload, resuming") return nil } @@ -307,7 +330,7 @@ func (s *applicationEventReporter) processResource( appName = appRes.Name } else { utils.LogWithResourceStatus(logCtx, rs).Info("streaming resource event") - appName = parentApplication.Name + appName = reportedEntityParentApp.app.Name } if err := s.codefreshClient.SendEvent(ctx, appName, ev); err != nil { diff --git a/event_reporter/reporter/application_event_reporter_test.go b/event_reporter/reporter/application_event_reporter_test.go index 621c0ac65adc8..24f57525b9a5d 100644 --- a/event_reporter/reporter/application_event_reporter_test.go +++ b/event_reporter/reporter/application_event_reporter_test.go @@ -38,12 +38,10 @@ import ( "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "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" "github.com/argoproj/argo-cd/v2/pkg/codefresh" - "github.com/argoproj/argo-cd/v2/util/argo" ) const ( @@ -226,7 +224,7 @@ func TestStreamApplicationEvent(t *testing.T) { assert.Equal(t, *app, actualApp) return nil } - _ = eventReporter.StreamApplicationEvents(context.Background(), app, "", false, common.LabelKeyAppInstance, argo.TrackingMethodLabel) + _ = eventReporter.StreamApplicationEvents(context.Background(), app, "", false, getMockedArgoTrackingMetadata()) }) } diff --git a/event_reporter/reporter/event_payload.go b/event_reporter/reporter/event_payload.go index 9aeda6fdbc517..e5963e2496fd4 100644 --- a/event_reporter/reporter/event_payload.go +++ b/event_reporter/reporter/event_payload.go @@ -20,164 +20,108 @@ import ( ) func getResourceEventPayload( - parentApplication *appv1.Application, - rs *appv1.ResourceStatus, - actualState *application.ApplicationResourceResponse, - desiredState *apiclient.Manifest, - apptree *appv1.ApplicationTree, - manifestGenErr bool, appEventProcessingStartedAt string, - originalApplication *appv1.Application, // passed when rs is application - revisionsMetadata *utils.AppSyncRevisionsMetadata, - originalAppRevisionsMetadata *utils.AppSyncRevisionsMetadata, // passed when rs is application - appInstanceLabelKey string, - trackingMethod appv1.TrackingMethod, - applicationVersions *apiclient.ApplicationVersions, + rr *ReportedResource, + reportedEntityParentApp *ReportedEntityParentApp, + argoTrackingMetadata *ArgoTrackingMetadata, ) (*events.Event, error) { var ( err error syncStarted = metav1.Now() syncFinished *metav1.Time - errors = []*events.ObjectError{} logCtx *log.Entry ) - if originalApplication != nil { - logCtx = log.WithField("application", originalApplication.Name) + if rr.rsAsAppInfo != nil && rr.rsAsAppInfo.app != nil { + logCtx = log.WithField("application", rr.rsAsAppInfo.app.Name) } else { logCtx = log.NewEntry(log.StandardLogger()) } - object := []byte(*actualState.Manifest) - - if originalAppRevisionsMetadata != nil && len(object) != 0 { - actualObject, err := appv1.UnmarshalToUnstructured(*actualState.Manifest) + object := []byte(*rr.actualState.Manifest) - if err == nil { - actualObject = utils.AddCommitsDetailsToAnnotations(actualObject, originalAppRevisionsMetadata) - if originalApplication != nil { - actualObject = utils.AddCommitDetailsToLabels(actualObject, getApplicationLegacyRevisionDetails(originalApplication, originalAppRevisionsMetadata)) - } + if rr.rsAsAppInfo != nil && rr.rsAsAppInfo.revisionsMetadata != nil && len(object) != 0 { + actualObject, err := appv1.UnmarshalToUnstructured(*rr.actualState.Manifest) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal manifest: %w", err) + } - object, err = actualObject.MarshalJSON() - if err != nil { - return nil, fmt.Errorf("failed to marshal unstructured object: %w", err) - } + object, err = addCommitDetailsToUnstructured(actualObject, rr) + if err != nil { + return nil, err } } if len(object) == 0 { - if len(desiredState.CompiledManifest) == 0 { - // no actual or desired state, don't send event - u := &unstructured.Unstructured{} - apiVersion := rs.Version - if rs.Group != "" { - apiVersion = rs.Group + "/" + rs.Version - } - - u.SetAPIVersion(apiVersion) - u.SetKind(rs.Kind) - u.SetName(rs.Name) - u.SetNamespace(rs.Namespace) - if originalAppRevisionsMetadata != nil { - u = utils.AddCommitsDetailsToAnnotations(u, originalAppRevisionsMetadata) - if originalApplication != nil { - u = utils.AddCommitDetailsToLabels(u, getApplicationLegacyRevisionDetails(originalApplication, originalAppRevisionsMetadata)) - } - } - - object, err = u.MarshalJSON() + if len(rr.desiredState.CompiledManifest) == 0 { + object, err = buildEventObjectAsLiveAndCompiledManifestsEmpty(rr) if err != nil { - return nil, fmt.Errorf("failed to marshal unstructured object: %w", err) + return nil, err } } else { - // no actual state, use desired state as event object - unstructuredWithNamespace, err := utils.AddDestNamespaceToManifest([]byte(desiredState.CompiledManifest), rs) + object, err = useCompiledManifestAsEventObject(rr) if err != nil { - return nil, fmt.Errorf("failed to add destination namespace to manifest: %w", err) - } - if originalAppRevisionsMetadata != nil { - unstructuredWithNamespace = utils.AddCommitsDetailsToAnnotations(unstructuredWithNamespace, originalAppRevisionsMetadata) - if originalApplication != nil { - unstructuredWithNamespace = utils.AddCommitDetailsToLabels(unstructuredWithNamespace, getApplicationLegacyRevisionDetails(originalApplication, originalAppRevisionsMetadata)) - } + return nil, err } - - object, _ = unstructuredWithNamespace.MarshalJSON() } - } else if rs.RequiresPruning && !manifestGenErr { + } else if rr.rs.RequiresPruning && !rr.manifestGenErr { // resource should be deleted - desiredState.CompiledManifest = "" - manifest := "" - actualState.Manifest = &manifest + makeDesiredAndLiveManifestEmpty(rr.actualState, rr.desiredState) } - if (originalApplication != nil && originalApplication.DeletionTimestamp != nil) || parentApplication.ObjectMeta.DeletionTimestamp != nil { + if (rr.rsAsAppInfo != nil && rr.rsAsAppInfo.app != nil && rr.rsAsAppInfo.app.DeletionTimestamp != nil) || reportedEntityParentApp.app.ObjectMeta.DeletionTimestamp != nil { // resource should be deleted in case if application in process of deletion - desiredState.CompiledManifest = "" - manifest := "" - actualState.Manifest = &manifest - } - - if parentApplication.Status.OperationState != nil { - syncStarted = parentApplication.Status.OperationState.StartedAt - syncFinished = parentApplication.Status.OperationState.FinishedAt - errors = append(errors, parseResourceSyncResultErrors(rs, parentApplication.Status.OperationState)...) + makeDesiredAndLiveManifestEmpty(rr.actualState, rr.desiredState) } - // for primitive resources that are synced right away and don't require progression time (like configmap) - if rs.Status == appv1.SyncStatusCodeSynced && rs.Health != nil && rs.Health.Status == health.HealthStatusHealthy { - syncFinished = &syncStarted - } - - // parent application not include errors in application originally was created with broken state, for example in destination missed namespace - if originalApplication != nil && originalApplication.Status.OperationState != nil { - errors = append(errors, parseApplicationSyncResultErrors(originalApplication.Status.OperationState)...) + if len(rr.desiredState.RawManifest) == 0 && len(rr.desiredState.CompiledManifest) != 0 { + // for handling helm defined resources, etc... + y, err := yaml.JSONToYAML([]byte(rr.desiredState.CompiledManifest)) + if err == nil { + rr.desiredState.RawManifest = string(y) + } } - if originalApplication != nil && originalApplication.Status.Conditions != nil { - errors = append(errors, parseApplicationSyncResultErrorsFromConditions(originalApplication.Status)...) + if reportedEntityParentApp.app.Status.OperationState != nil { + syncStarted = reportedEntityParentApp.app.Status.OperationState.StartedAt + syncFinished = reportedEntityParentApp.app.Status.OperationState.FinishedAt } - if originalApplication != nil { - errors = append(errors, parseAggregativeHealthErrorsOfApplication(originalApplication, apptree)...) + // for primitive resources that are synced right away and don't require progression time (like configmap) + if rr.rs.Status == appv1.SyncStatusCodeSynced && rr.rs.Health != nil && rr.rs.Health.Status == health.HealthStatusHealthy { + syncFinished = &syncStarted } - if len(desiredState.RawManifest) == 0 && len(desiredState.CompiledManifest) != 0 { - // for handling helm defined resources, etc... - y, err := yaml.JSONToYAML([]byte(desiredState.CompiledManifest)) - if err == nil { - desiredState.RawManifest = string(y) + var applicationVersionsEvents *events.ApplicationVersions + if rr.rsAsAppInfo != nil { + applicationVersionsEvents, err = utils.RepoAppVersionsToEvent(rr.rsAsAppInfo.applicationVersions) + if err != nil { + logCtx.Errorf("failed to convert appVersions: %v", err) } } - applicationVersionsEvents, err := utils.RepoAppVersionsToEvent(applicationVersions) - if err != nil { - logCtx.Errorf("failed to convert appVersions: %v", err) - } - source := events.ObjectSource{ - DesiredManifest: desiredState.CompiledManifest, - ActualManifest: *actualState.Manifest, - GitManifest: desiredState.RawManifest, - RepoURL: parentApplication.Status.Sync.ComparedTo.Source.RepoURL, - Path: desiredState.Path, - Revision: utils.GetApplicationLatestRevision(parentApplication), - OperationSyncRevision: utils.GetOperationRevision(parentApplication), - HistoryId: utils.GetLatestAppHistoryId(parentApplication), - AppName: parentApplication.Name, - AppNamespace: parentApplication.Namespace, - AppUID: string(parentApplication.ObjectMeta.UID), - AppLabels: parentApplication.Labels, - SyncStatus: string(rs.Status), + DesiredManifest: rr.desiredState.CompiledManifest, + ActualManifest: *rr.actualState.Manifest, + GitManifest: rr.desiredState.RawManifest, + RepoURL: reportedEntityParentApp.app.Status.Sync.ComparedTo.Source.RepoURL, + Path: rr.desiredState.Path, + Revision: utils.GetApplicationLatestRevision(reportedEntityParentApp.app), + OperationSyncRevision: utils.GetOperationRevision(reportedEntityParentApp.app), + HistoryId: utils.GetLatestAppHistoryId(reportedEntityParentApp.app), + AppName: reportedEntityParentApp.app.Name, + AppNamespace: reportedEntityParentApp.app.Namespace, + AppUID: string(reportedEntityParentApp.app.ObjectMeta.UID), + AppLabels: reportedEntityParentApp.app.Labels, + SyncStatus: string(rr.rs.Status), SyncStartedAt: syncStarted, SyncFinishedAt: syncFinished, - Cluster: parentApplication.Spec.Destination.Server, - AppInstanceLabelKey: appInstanceLabelKey, - TrackingMethod: string(trackingMethod), + Cluster: reportedEntityParentApp.app.Spec.Destination.Server, + AppInstanceLabelKey: *argoTrackingMetadata.AppInstanceLabelKey, + TrackingMethod: string(*argoTrackingMetadata.TrackingMethod), } - if revisionsMetadata != nil && revisionsMetadata.SyncRevisions != nil { - revisionMetadata := getApplicationLegacyRevisionDetails(parentApplication, revisionsMetadata) + if reportedEntityParentApp.revisionsMetadata != nil && reportedEntityParentApp.revisionsMetadata.SyncRevisions != nil { + revisionMetadata := getApplicationLegacyRevisionDetails(reportedEntityParentApp.app, reportedEntityParentApp.revisionsMetadata) if revisionMetadata != nil { source.CommitMessage = revisionMetadata.Message source.CommitAuthor = revisionMetadata.Author @@ -185,40 +129,124 @@ func getResourceEventPayload( } } - if rs.Health != nil { - source.HealthStatus = (*string)(&rs.Health.Status) - source.HealthMessage = &rs.Health.Message - if rs.Health.Status != health.HealthStatusHealthy { - errors = append(errors, parseAggregativeHealthErrors(rs, apptree, false)...) - } + if rr.rs.Health != nil { + source.HealthStatus = (*string)(&rr.rs.Health.Status) + source.HealthMessage = &rr.rs.Health.Message } payload := events.EventPayload{ Timestamp: appEventProcessingStartedAt, Object: object, Source: &source, - Errors: errors, + Errors: getResourceEventPayloadErrors(rr, reportedEntityParentApp), AppVersions: applicationVersionsEvents, } - logCtx.Infof("AppVersion before encoding: %v", utils.SafeString(payload.AppVersions.AppVersion)) + if payload.AppVersions != nil { + logCtx.Infof("AppVersion before encoding: %v", utils.SafeString(payload.AppVersions.AppVersion)) + } payloadBytes, err := json.Marshal(&payload) if err != nil { - return nil, fmt.Errorf("failed to marshal payload for resource %s/%s: %w", rs.Namespace, rs.Name, err) + return nil, fmt.Errorf("failed to marshal payload for resource %s/%s: %w", rr.rs.Namespace, rr.rs.Name, err) } return &events.Event{Payload: payloadBytes}, nil } +func getResourceEventPayloadErrors( + rr *ReportedResource, + reportedEntityParentApp *ReportedEntityParentApp, +) []*events.ObjectError { + var errors []*events.ObjectError + + if reportedEntityParentApp.app.Status.OperationState != nil { + errors = append(errors, parseResourceSyncResultErrors(rr.rs, reportedEntityParentApp.app.Status.OperationState)...) + } + + // parent application not include errors in application originally was created with broken state, for example in destination missed namespace + if rr.rsAsAppInfo != nil && rr.rsAsAppInfo.app != nil { + if rr.rsAsAppInfo.app.Status.OperationState != nil { + errors = append(errors, parseApplicationSyncResultErrors(rr.rsAsAppInfo.app.Status.OperationState)...) + } + + if rr.rsAsAppInfo.app.Status.Conditions != nil { + errors = append(errors, parseApplicationSyncResultErrorsFromConditions(rr.rsAsAppInfo.app.Status)...) + } + + errors = append(errors, parseAggregativeHealthErrorsOfApplication(rr.rsAsAppInfo.app, reportedEntityParentApp.appTree)...) + } + + if rr.rs.Health != nil && rr.rs.Health.Status != health.HealthStatusHealthy { + errors = append(errors, parseAggregativeHealthErrors(rr.rs, reportedEntityParentApp.appTree, false)...) + } + + return errors +} + +func useCompiledManifestAsEventObject( + rr *ReportedResource, +) ([]byte, error) { + // no actual state, use desired state as event object + unstructuredWithNamespace, err := utils.AddDestNamespaceToManifest([]byte(rr.desiredState.CompiledManifest), rr.rs) + if err != nil { + return nil, fmt.Errorf("failed to add destination namespace to manifest: %w", err) + } + + return addCommitDetailsToUnstructured(unstructuredWithNamespace, rr) +} + +func buildEventObjectAsLiveAndCompiledManifestsEmpty( + rr *ReportedResource, +) ([]byte, error) { + // no actual or desired state, don't send event + u := &unstructured.Unstructured{} + + u.SetAPIVersion(rr.GetApiVersion()) + u.SetKind(rr.rs.Kind) + u.SetName(rr.rs.Name) + u.SetNamespace(rr.rs.Namespace) + + return addCommitDetailsToUnstructured(u, rr) +} + +// when empty minifests reported to codefresh they will get deleted +func makeDesiredAndLiveManifestEmpty( + actualState *application.ApplicationResourceResponse, + desiredState *apiclient.Manifest, +) { + // resource should be deleted + desiredState.CompiledManifest = "" + manifest := "" + actualState.Manifest = &manifest +} + +func addCommitDetailsToUnstructured( + u *unstructured.Unstructured, + rr *ReportedResource, +) ([]byte, error) { + if rr.rsAsAppInfo != nil && rr.rsAsAppInfo.revisionsMetadata != nil { + u = utils.AddCommitsDetailsToAnnotations(u, rr.rsAsAppInfo.revisionsMetadata) + if rr.rsAsAppInfo.app != nil { + u = utils.AddCommitDetailsToLabels(u, getApplicationLegacyRevisionDetails(rr.rsAsAppInfo.app, rr.rsAsAppInfo.revisionsMetadata)) + } + } + + object, err := u.MarshalJSON() + if err != nil { + return nil, fmt.Errorf("failed to marshal unstructured object: %w", err) + } + + return object, err +} + func (s *applicationEventReporter) getApplicationEventPayload( ctx context.Context, a *appv1.Application, appTree *appv1.ApplicationTree, eventProcessingStartedAt string, - appInstanceLabelKey string, - trackingMethod appv1.TrackingMethod, applicationVersions *apiclient.ApplicationVersions, + argoTrackingMetadata *ArgoTrackingMetadata, ) (*events.Event, error) { var ( syncStarted = metav1.Now() @@ -287,8 +315,8 @@ func (s *applicationEventReporter) getApplicationEventPayload( HealthStatus: &hs, HealthMessage: &a.Status.Health.Message, Cluster: a.Spec.Destination.Server, - AppInstanceLabelKey: appInstanceLabelKey, - TrackingMethod: string(trackingMethod), + AppInstanceLabelKey: *argoTrackingMetadata.AppInstanceLabelKey, + TrackingMethod: string(*argoTrackingMetadata.TrackingMethod), } errors = append(errors, parseApplicationSyncResultErrorsFromConditions(a.Status)...) diff --git a/event_reporter/reporter/event_payload_test.go b/event_reporter/reporter/event_payload_test.go index dc305a099c897..b0cf4c9afe916 100644 --- a/event_reporter/reporter/event_payload_test.go +++ b/event_reporter/reporter/event_payload_test.go @@ -18,6 +18,16 @@ import ( "github.com/argoproj/argo-cd/v2/util/argo" ) +func getMockedArgoTrackingMetadata() *ArgoTrackingMetadata { + appInstanceLabelKey := common.LabelKeyAppInstance + trackingMethod := argo.TrackingMethodLabel + + return &ArgoTrackingMetadata{ + AppInstanceLabelKey: &appInstanceLabelKey, + TrackingMethod: &trackingMethod, + } +} + func TestGetResourceEventPayload(t *testing.T) { t.Run("Deleting timestamp is empty", func(t *testing.T) { app := v1alpha1.Application{ @@ -48,7 +58,17 @@ func TestGetResourceEventPayload(t *testing.T) { }}, } - event, err := getResourceEventPayload(&app, &rs, &actualState, &desiredState, &appTree, true, "", nil, &revisionMetadata, nil, common.LabelKeyAppInstance, argo.TrackingMethodLabel, &repoApiclient.ApplicationVersions{}) + event, err := getResourceEventPayload("", &ReportedResource{ + rs: &rs, + actualState: &actualState, + desiredState: &desiredState, + manifestGenErr: true, + rsAsAppInfo: nil, + }, &ReportedEntityParentApp{ + app: &app, + appTree: &appTree, + revisionsMetadata: &revisionMetadata, + }, getMockedArgoTrackingMetadata()) require.NoError(t, err) var eventPayload events.EventPayload @@ -80,7 +100,17 @@ func TestGetResourceEventPayload(t *testing.T) { SyncRevisions: []*utils.RevisionWithMetadata{}, } - event, err := getResourceEventPayload(&app, &rs, &actualState, &desiredState, &appTree, true, "", nil, &revisionMetadata, nil, common.LabelKeyAppInstance, argo.TrackingMethodLabel, &repoApiclient.ApplicationVersions{}) + event, err := getResourceEventPayload("", &ReportedResource{ + rs: &rs, + actualState: &actualState, + desiredState: &desiredState, + manifestGenErr: true, + rsAsAppInfo: nil, + }, &ReportedEntityParentApp{ + app: &app, + appTree: &appTree, + revisionsMetadata: &revisionMetadata, + }, getMockedArgoTrackingMetadata()) require.NoError(t, err) var eventPayload events.EventPayload @@ -107,6 +137,15 @@ func TestGetResourceEventPayloadWithoutRevision(t *testing.T) { } appTree := v1alpha1.ApplicationTree{} - _, err := getResourceEventPayload(&app, &rs, &actualState, &desiredState, &appTree, true, "", nil, nil, nil, common.LabelKeyAppInstance, argo.TrackingMethodLabel, &repoApiclient.ApplicationVersions{}) + _, err := getResourceEventPayload("", &ReportedResource{ + rs: &rs, + actualState: &actualState, + desiredState: &desiredState, + manifestGenErr: true, + rsAsAppInfo: nil, + }, &ReportedEntityParentApp{ + app: &app, + appTree: &appTree, + }, getMockedArgoTrackingMetadata()) assert.NoError(t, err) } diff --git a/event_reporter/reporter/types.go b/event_reporter/reporter/types.go new file mode 100644 index 0000000000000..910104394f0bc --- /dev/null +++ b/event_reporter/reporter/types.go @@ -0,0 +1,42 @@ +package reporter + +import ( + "github.com/argoproj/argo-cd/v2/event_reporter/utils" + "github.com/argoproj/argo-cd/v2/pkg/apiclient/application" + appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + "github.com/argoproj/argo-cd/v2/reposerver/apiclient" +) + +type ReportedResource struct { + rs *appv1.ResourceStatus + rsAsAppInfo *ReportedResourceAsApp // passed if resource is application + actualState *application.ApplicationResourceResponse + desiredState *apiclient.Manifest + manifestGenErr bool +} + +type ReportedResourceAsApp struct { + app *appv1.Application + revisionsMetadata *utils.AppSyncRevisionsMetadata + applicationVersions *apiclient.ApplicationVersions +} + +type ReportedEntityParentApp struct { + app *appv1.Application + appTree *appv1.ApplicationTree + revisionsMetadata *utils.AppSyncRevisionsMetadata +} + +type ArgoTrackingMetadata struct { + AppInstanceLabelKey *string + TrackingMethod *appv1.TrackingMethod +} + +func (rr *ReportedResource) GetApiVersion() string { + apiVersion := rr.rs.Version + if rr.rs.Group != "" { + apiVersion = rr.rs.Group + "/" + rr.rs.Version + } + + return apiVersion +}