diff --git a/internal/controller/kustomization_controller.go b/internal/controller/kustomization_controller.go index fd4fbffd..0969032f 100644 --- a/internal/controller/kustomization_controller.go +++ b/internal/controller/kustomization_controller.go @@ -37,7 +37,6 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" kerrors "k8s.io/apimachinery/pkg/util/errors" - kuberecorder "k8s.io/client-go/tools/record" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" @@ -47,7 +46,7 @@ import ( "github.com/fluxcd/cli-utils/pkg/kstatus/polling/engine" "github.com/fluxcd/cli-utils/pkg/object" apiacl "github.com/fluxcd/pkg/apis/acl" - eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" + eventv1 "github.com/fluxcd/pkg/apis/event/v1" "github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/auth" authutils "github.com/fluxcd/pkg/auth/utils" @@ -59,6 +58,7 @@ import ( runtimeClient "github.com/fluxcd/pkg/runtime/client" "github.com/fluxcd/pkg/runtime/conditions" runtimeCtrl "github.com/fluxcd/pkg/runtime/controller" + "github.com/fluxcd/pkg/runtime/events" "github.com/fluxcd/pkg/runtime/jitter" "github.com/fluxcd/pkg/runtime/patch" "github.com/fluxcd/pkg/runtime/statusreaders" @@ -86,7 +86,7 @@ import ( // KustomizationReconciler reconciles a Kustomization object type KustomizationReconciler struct { client.Client - kuberecorder.EventRecorder + events.EventRecorder runtimeCtrl.Metrics // Kubernetes options @@ -159,7 +159,8 @@ func (r *KustomizationReconciler) Reconcile(ctx context.Context, req ctrl.Reques time.Since(reconcileStart).String(), obj.Spec.Interval.Duration.String()) log.Info(msg, "revision", obj.Status.LastAttemptedRevision) - r.event(obj, obj.Status.LastAppliedRevision, obj.Status.LastAppliedOriginRevision, eventv1.EventSeverityInfo, msg, + r.event(obj, nil, obj.Status.LastAppliedRevision, obj.Status.LastAppliedOriginRevision, eventv1.EventSeverityInfo, + eventv1.ActionReconciled, msg, map[string]string{ kustomizev1.GroupVersion.Group + "/" + eventv1.MetaCommitStatusKey: eventv1.MetaCommitStatusUpdateValue, }) @@ -168,7 +169,7 @@ func (r *KustomizationReconciler) Reconcile(ctx context.Context, req ctrl.Reques // Prune managed resources if the object is under deletion. if !obj.ObjectMeta.DeletionTimestamp.IsZero() { - return r.finalize(ctx, obj) + return r.finalize(ctx, obj, nil) } // Add finalizer first if it doesn't exist to avoid the race condition @@ -193,7 +194,7 @@ func (r *KustomizationReconciler) Reconcile(ctx context.Context, req ctrl.Reques conditions.MarkFalse(obj, meta.ReadyCondition, meta.InvalidCELExpressionReason, "%s", errMsg) conditions.MarkStalled(obj, meta.InvalidCELExpressionReason, "%s", errMsg) obj.Status.ObservedGeneration = obj.Generation - r.event(obj, "", "", eventv1.EventSeverityError, errMsg, nil) + r.event(obj, nil, "", "", eventv1.EventSeverityError, eventv1.ActionFailed, errMsg, nil) return ctrl.Result{}, reconcile.TerminalError(err) } @@ -205,7 +206,7 @@ func (r *KustomizationReconciler) Reconcile(ctx context.Context, req ctrl.Reques conditions.MarkFalse(obj, meta.ReadyCondition, meta.FeatureGateDisabledReason, msgFmt, gate) conditions.MarkStalled(obj, meta.FeatureGateDisabledReason, msgFmt, gate) log.Error(auth.ErrObjectLevelWorkloadIdentityNotEnabled, msg) - r.event(obj, "", "", eventv1.EventSeverityError, msg, nil) + r.event(obj, nil, "", "", eventv1.EventSeverityError, eventv1.ActionFailed, msg, nil) return ctrl.Result{}, nil } @@ -223,7 +224,7 @@ func (r *KustomizationReconciler) Reconcile(ctx context.Context, req ctrl.Reques if acl.IsAccessDenied(err) { conditions.MarkFalse(obj, meta.ReadyCondition, apiacl.AccessDeniedReason, "%s", err) conditions.MarkStalled(obj, apiacl.AccessDeniedReason, "%s", err) - r.event(obj, "", "", eventv1.EventSeverityError, err.Error(), nil) + r.event(obj, artifactSource, "", "", eventv1.EventSeverityError, eventv1.ActionFailed, err.Error(), nil) return ctrl.Result{}, reconcile.TerminalError(err) } @@ -250,7 +251,7 @@ func (r *KustomizationReconciler) Reconcile(ctx context.Context, req ctrl.Reques conditions.MarkFalse(obj, meta.ReadyCondition, meta.InvalidCELExpressionReason, "%s", errMsg) conditions.MarkStalled(obj, meta.InvalidCELExpressionReason, "%s", errMsg) obj.Status.ObservedGeneration = obj.Generation - r.event(obj, revision, originRevision, eventv1.EventSeverityError, errMsg, nil) + r.event(obj, artifactSource, revision, originRevision, eventv1.EventSeverityError, eventv1.ActionFailed, errMsg, nil) return ctrl.Result{}, err } @@ -258,7 +259,7 @@ func (r *KustomizationReconciler) Reconcile(ctx context.Context, req ctrl.Reques conditions.MarkFalse(obj, meta.ReadyCondition, meta.DependencyNotReadyReason, "%s", err) msg := fmt.Sprintf("Dependencies do not meet ready condition, retrying in %s", r.DependencyRequeueInterval.String()) log.Info(msg) - r.event(obj, revision, originRevision, eventv1.EventSeverityInfo, msg, nil) + r.event(obj, artifactSource, revision, originRevision, eventv1.EventSeverityInfo, eventv1.ActionWaiting, msg, nil) return ctrl.Result{RequeueAfter: r.DependencyRequeueInterval}, nil } log.Info("All dependencies are ready, proceeding with reconciliation") @@ -282,7 +283,7 @@ func (r *KustomizationReconciler) Reconcile(ctx context.Context, req ctrl.Reques meta.HealthCheckCanceledReason, "New reconciliation triggered by %s/%s/%s", qes.Kind, qes.Namespace, qes.Name) ctrl.LoggerFrom(ctx).Info("New reconciliation triggered, canceling health checks", "trigger", qes) - r.event(obj, revision, originRevision, eventv1.EventSeverityInfo, + r.event(obj, artifactSource, revision, originRevision, eventv1.EventSeverityInfo, eventv1.ActionProgressing, fmt.Sprintf("Health checks canceled due to new reconciliation triggered by %s/%s/%s", qes.Kind, qes.Namespace, qes.Name), nil) @@ -300,7 +301,7 @@ func (r *KustomizationReconciler) Reconcile(ctx context.Context, req ctrl.Reques obj.GetRetryInterval().String()), "revision", revision) - r.event(obj, revision, originRevision, eventv1.EventSeverityError, + r.event(obj, artifactSource, revision, originRevision, eventv1.EventSeverityError, eventv1.ActionFailed, reconcileErr.Error(), nil) return ctrl.Result{RequeueAfter: obj.GetRetryInterval()}, nil } @@ -470,7 +471,7 @@ func (r *KustomizationReconciler) reconcile( } // Validate and apply resources in stages. - drifted, changeSet, err := r.apply(ctx, resourceManager, obj, revision, originRevision, objects) + drifted, changeSet, err := r.apply(ctx, resourceManager, obj, src, revision, originRevision, objects) if err != nil { obj.Status.History.Upsert(checksum, time.Now(), time.Since(reconcileStart), meta.ReconciliationFailedReason, historyMeta) conditions.MarkFalse(obj, meta.ReadyCondition, meta.ReconciliationFailedReason, "%s", err) @@ -501,7 +502,7 @@ func (r *KustomizationReconciler) reconcile( // On failure, re-track the objects whose DELETE wasn't confirmed so that the // next reconcile retries — otherwise status.Inventory advances past them // and they leak as untracked orphans (issue #1664). - if _, survivors, err := r.prune(ctx, resourceManager, obj, revision, originRevision, staleObjects); err != nil { + if _, survivors, err := r.prune(ctx, resourceManager, obj, src, revision, originRevision, staleObjects); err != nil { inventory.Merge(obj.Status.Inventory, survivors) obj.Status.History.Upsert(checksum, time.Now(), time.Since(reconcileStart), meta.PruneFailedReason, historyMeta) conditions.MarkFalse(obj, meta.ReadyCondition, meta.PruneFailedReason, "%s", err) @@ -514,6 +515,7 @@ func (r *KustomizationReconciler) reconcile( resourceManager, patcher, obj, + src, revision, originRevision, isNewRevision, @@ -842,6 +844,7 @@ func (r *KustomizationReconciler) build(ctx context.Context, func (r *KustomizationReconciler) apply(ctx context.Context, manager *ssa.ResourceManager, obj *kustomizev1.Kustomization, + src sourcev1.Source, revision string, originRevision string, objects []*unstructured.Unstructured) (bool, *ssa.ChangeSet, error) { @@ -988,7 +991,7 @@ func (r *KustomizationReconciler) apply(ctx context.Context, // emit event only if the server-side apply resulted in changes applyLog := strings.TrimSuffix(changeSetLog.String(), "\n") if applyLog != "" { - r.event(obj, revision, originRevision, eventv1.EventSeverityInfo, applyLog, nil) + r.event(obj, src, revision, originRevision, eventv1.EventSeverityInfo, eventv1.ActionApplied, applyLog, nil) } return applyLog != "", resultSet, nil @@ -998,6 +1001,7 @@ func (r *KustomizationReconciler) checkHealth(ctx context.Context, manager *ssa.ResourceManager, patcher *patch.SerialPatcher, obj *kustomizev1.Kustomization, + src sourcev1.Source, revision string, originRevision string, isNewRevision bool, @@ -1075,7 +1079,7 @@ func (r *KustomizationReconciler) checkHealth(ctx context.Context, // Emit recovery event if the previous health check failed. msg := fmt.Sprintf("Health check passed in %s", time.Since(checkStart).String()) if !wasHealthy || (isNewRevision && drifted) { - r.event(obj, revision, originRevision, eventv1.EventSeverityInfo, msg, nil) + r.event(obj, src, revision, originRevision, eventv1.EventSeverityInfo, eventv1.ActionReconciled, msg, nil) } conditions.MarkTrue(obj, meta.HealthyCondition, meta.SucceededReason, "%s", msg) @@ -1095,6 +1099,7 @@ func (r *KustomizationReconciler) checkHealth(ctx context.Context, func (r *KustomizationReconciler) prune(ctx context.Context, manager *ssa.ResourceManager, obj *kustomizev1.Kustomization, + src sourcev1.Source, revision string, originRevision string, objects []*unstructured.Unstructured) (bool, []*unstructured.Unstructured, error) { @@ -1117,7 +1122,7 @@ func (r *KustomizationReconciler) prune(ctx context.Context, // emit event only if the prune operation resulted in changes if changeSet != nil && len(changeSet.Entries) > 0 { log.Info(fmt.Sprintf("garbage collection completed: %s", changeSet.String())) - r.event(obj, revision, originRevision, eventv1.EventSeverityInfo, changeSet.String(), nil) + r.event(obj, src, revision, originRevision, eventv1.EventSeverityInfo, eventv1.ActionDeleted, changeSet.String(), nil) return true, nil, nil } @@ -1182,7 +1187,7 @@ func finalizerShouldDeleteResources(obj *kustomizev1.Kustomization) bool { // If the service account used for impersonation is no longer available or if a timeout occurs // while waiting for resources to be terminated, an error is logged and the finalizer is removed. func (r *KustomizationReconciler) finalize(ctx context.Context, - obj *kustomizev1.Kustomization) (ctrl.Result, error) { + obj *kustomizev1.Kustomization, src sourcev1.Source) (ctrl.Result, error) { log := ctrl.LoggerFrom(ctx) if finalizerShouldDeleteResources(obj) { objects, _ := inventory.List(obj.Status.Inventory) @@ -1223,14 +1228,16 @@ func (r *KustomizationReconciler) finalize(ctx context.Context, changeSet, err := deleteObjects(ctx, obj, resourceManager, objects) if err != nil { - r.event(obj, obj.Status.LastAppliedRevision, obj.Status.LastAppliedOriginRevision, eventv1.EventSeverityError, "pruning for deleted resource failed", nil) + r.event(obj, src, obj.Status.LastAppliedRevision, obj.Status.LastAppliedOriginRevision, + eventv1.EventSeverityError, eventv1.ActionFailed, "pruning for deleted resource failed", nil) // Return the error so we retry the failed garbage collection return ctrl.Result{}, err } if changeSet != nil && len(changeSet.Entries) > 0 { // Emit event with the resources marked for deletion. - r.event(obj, obj.Status.LastAppliedRevision, obj.Status.LastAppliedOriginRevision, eventv1.EventSeverityInfo, changeSet.String(), nil) + r.event(obj, src, obj.Status.LastAppliedRevision, obj.Status.LastAppliedOriginRevision, + eventv1.EventSeverityInfo, eventv1.ActionDeleted, changeSet.String(), nil) // Wait for the resources marked for deletion to be terminated. if obj.GetDeletionPolicy() == kustomizev1.DeletionPolicyWaitForTermination { @@ -1241,7 +1248,8 @@ func (r *KustomizationReconciler) finalize(ctx context.Context, // Emit an event and log the error if a timeout occurs. msg := "failed to wait for resources termination" log.Error(err, msg) - r.event(obj, obj.Status.LastAppliedRevision, obj.Status.LastAppliedOriginRevision, eventv1.EventSeverityError, msg, nil) + r.event(obj, src, obj.Status.LastAppliedRevision, obj.Status.LastAppliedOriginRevision, + eventv1.EventSeverityError, eventv1.ActionFailed, msg, nil) } } } @@ -1249,7 +1257,8 @@ func (r *KustomizationReconciler) finalize(ctx context.Context, // when the account to impersonate is gone, log the stale objects and continue with the finalization msg := fmt.Sprintf("unable to prune objects: \n%s", ssautil.FmtUnstructuredList(objects)) log.Error(fmt.Errorf("skiping pruning, failed to find account to impersonate"), msg) - r.event(obj, obj.Status.LastAppliedRevision, obj.Status.LastAppliedOriginRevision, eventv1.EventSeverityError, msg, nil) + r.event(obj, src, obj.Status.LastAppliedRevision, obj.Status.LastAppliedOriginRevision, + eventv1.EventSeverityError, eventv1.ActionFailed, msg, nil) } } @@ -1266,7 +1275,10 @@ func (r *KustomizationReconciler) finalize(ctx context.Context, } func (r *KustomizationReconciler) event(obj *kustomizev1.Kustomization, - revision, originRevision, severity, msg string, + src sourcev1.Source, + revision, originRevision, severity, + action string, + msg string, metadata map[string]string) { if metadata == nil { metadata = map[string]string{} @@ -1288,7 +1300,7 @@ func (r *KustomizationReconciler) event(obj *kustomizev1.Kustomization, eventType = corev1.EventTypeWarning } - r.EventRecorder.AnnotatedEventf(obj, metadata, eventType, reason, "%s", msg) + r.AnnotatedEventf(obj, src, metadata, eventType, reason, action, "%s", msg) } func (r *KustomizationReconciler) finalizeStatus(ctx context.Context, diff --git a/internal/controller/kustomization_controller_test.go b/internal/controller/kustomization_controller_test.go index b3672abe..e1637522 100644 --- a/internal/controller/kustomization_controller_test.go +++ b/internal/controller/kustomization_controller_test.go @@ -22,18 +22,18 @@ import ( "testing" "time" - "github.com/fluxcd/pkg/apis/meta" - sourcev1 "github.com/fluxcd/source-controller/api/v1" . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/tools/record" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1" + "github.com/fluxcd/pkg/apis/meta" + "github.com/fluxcd/pkg/runtime/events" + sourcev1 "github.com/fluxcd/source-controller/api/v1" ) func TestKustomizationReconciler_StagedApply(t *testing.T) { @@ -130,7 +130,7 @@ func TestKustomizationReconciler_deleteBeforeFinalizer(t *testing.T) { r := &KustomizationReconciler{ Client: k8sClient, - EventRecorder: record.NewFakeRecorder(32), + EventRecorder: events.NewFakeRecorder(32, true), } // NOTE: Only a real API server responds with an error in this scenario. _, err := r.Reconcile(ctx, ctrl.Request{NamespacedName: client.ObjectKeyFromObject(kustomization)}) diff --git a/internal/controller/kustomization_decryptor_test.go b/internal/controller/kustomization_decryptor_test.go index d4cdb91c..8532c64d 100644 --- a/internal/controller/kustomization_decryptor_test.go +++ b/internal/controller/kustomization_decryptor_test.go @@ -24,8 +24,6 @@ import ( "testing" "time" - "github.com/fluxcd/pkg/apis/meta" - sourcev1 "github.com/fluxcd/source-controller/api/v1" "github.com/hashicorp/vault/api" . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" @@ -33,6 +31,10 @@ import ( "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" + "github.com/fluxcd/pkg/apis/meta" + "github.com/fluxcd/pkg/runtime/testenv" + sourcev1 "github.com/fluxcd/source-controller/api/v1" + kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1" ) @@ -193,10 +195,10 @@ func TestKustomizationReconciler_Decryptor(t *testing.T) { return resultK.Status.LastAppliedRevision == revision }, timeout, time.Second).Should(BeTrue()) - events := getEvents(resultK.GetName(), map[string]string{"kustomize.toolkit.fluxcd.io/revision": revision}) + events := testenv.GetEvents(ctx, k8sClient, resultK.GetName(), "", map[string]string{"kustomize.toolkit.fluxcd.io/revision": revision}) g.Expect(len(events)).To(BeIdenticalTo(1)) - g.Expect(events[0].Message).Should(ContainSubstring("Reconciliation finished")) - g.Expect(events[0].Message).ShouldNot(ContainSubstring("configured")) + g.Expect(events[0].Note).Should(ContainSubstring("Reconciliation finished")) + g.Expect(events[0].Note).ShouldNot(ContainSubstring("configured")) }) t.Run("global SOPS age secret as fallback", func(t *testing.T) { diff --git a/internal/controller/kustomization_externalartifact_test.go b/internal/controller/kustomization_externalartifact_test.go index dedd8a50..7bef0371 100644 --- a/internal/controller/kustomization_externalartifact_test.go +++ b/internal/controller/kustomization_externalartifact_test.go @@ -24,10 +24,6 @@ import ( "testing" "time" - apiacl "github.com/fluxcd/pkg/apis/acl" - "github.com/fluxcd/pkg/apis/meta" - "github.com/fluxcd/pkg/testserver" - sourcev1 "github.com/fluxcd/source-controller/api/v1" . "github.com/onsi/gomega" "github.com/opencontainers/go-digest" apimeta "k8s.io/apimachinery/pkg/api/meta" @@ -35,6 +31,12 @@ import ( "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" + apiacl "github.com/fluxcd/pkg/apis/acl" + "github.com/fluxcd/pkg/apis/meta" + "github.com/fluxcd/pkg/runtime/testenv" + "github.com/fluxcd/pkg/testserver" + sourcev1 "github.com/fluxcd/source-controller/api/v1" + kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1" ) @@ -119,14 +121,14 @@ stringData: g.Expect(readyCondition.Reason).To(Equal(meta.ReconciliationSucceededReason)) g.Expect(resultK.Status.LastAppliedRevision).To(Equal(revision)) - events := getEvents(resultK.GetName(), map[string]string{"kustomize.toolkit.fluxcd.io/revision": revision}) + events := testenv.GetEvents(ctx, k8sClient, resultK.GetName(), "", map[string]string{"kustomize.toolkit.fluxcd.io/revision": revision}) g.Expect(len(events) > 2).To(BeTrue()) g.Expect(events[0].Reason).To(BeIdenticalTo(meta.ProgressingReason)) - g.Expect(events[0].Message).To(ContainSubstring("created")) + g.Expect(events[0].Note).To(ContainSubstring("created")) g.Expect(events[1].Reason).To(BeIdenticalTo(meta.ProgressingReason)) - g.Expect(events[1].Message).To(ContainSubstring("check passed")) + g.Expect(events[1].Note).To(ContainSubstring("check passed")) g.Expect(events[2].Reason).To(BeIdenticalTo(meta.ReconciliationSucceededReason)) - g.Expect(events[2].Message).To(ContainSubstring("finished")) + g.Expect(events[2].Note).To(ContainSubstring("finished")) }) t.Run("watches for external artifact revision change", func(t *testing.T) { @@ -162,9 +164,9 @@ stringData: g.Expect(readyCondition.Reason).To(Equal(apiacl.AccessDeniedReason)) g.Expect(apimeta.IsStatusConditionTrue(resultK.Status.Conditions, meta.StalledCondition)).Should(BeTrue()) - events := getEvents(resultK.GetName(), nil) + events := testenv.GetEvents(ctx, k8sClient, resultK.GetName(), "", nil) g.Expect(events[len(events)-1].Reason).To(BeIdenticalTo(apiacl.AccessDeniedReason)) - g.Expect(events[len(events)-1].Message).To(ContainSubstring("feature gate is disabled")) + g.Expect(events[len(events)-1].Note).To(ContainSubstring("feature gate is disabled")) }) } diff --git a/internal/controller/kustomization_force_test.go b/internal/controller/kustomization_force_test.go index 7d8654cc..0a7f400b 100644 --- a/internal/controller/kustomization_force_test.go +++ b/internal/controller/kustomization_force_test.go @@ -22,9 +22,6 @@ import ( "testing" "time" - "github.com/fluxcd/pkg/apis/meta" - "github.com/fluxcd/pkg/testserver" - sourcev1 "github.com/fluxcd/source-controller/api/v1" . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" apimeta "k8s.io/apimachinery/pkg/api/meta" @@ -32,6 +29,11 @@ import ( "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" + "github.com/fluxcd/pkg/apis/meta" + "github.com/fluxcd/pkg/runtime/testenv" + "github.com/fluxcd/pkg/testserver" + sourcev1 "github.com/fluxcd/source-controller/api/v1" + kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1" ) @@ -141,10 +143,10 @@ stringData: kstatusCheck.CheckErr(ctx, resultK) t.Run("emits validation error event", func(t *testing.T) { - events := getEvents(resultK.GetName(), map[string]string{"kustomize.toolkit.fluxcd.io/revision": revision}) + events := testenv.GetEvents(ctx, k8sClient, resultK.GetName(), "", map[string]string{"kustomize.toolkit.fluxcd.io/revision": revision}) g.Expect(len(events) > 0).To(BeTrue()) g.Expect(events[0].Type).To(BeIdenticalTo("Warning")) - g.Expect(events[0].Message).To(ContainSubstring("field is immutable")) + g.Expect(events[0].Note).To(ContainSubstring("field is immutable")) }) }) diff --git a/internal/controller/kustomization_fuzzer_test.go b/internal/controller/kustomization_fuzzer_test.go index 2404b5ef..00682cd1 100644 --- a/internal/controller/kustomization_fuzzer_test.go +++ b/internal/controller/kustomization_fuzzer_test.go @@ -562,27 +562,6 @@ func randStringRunes(n int) string { return string(b) } -func getEvents(objName string, annotations map[string]string) []corev1.Event { - var result []corev1.Event - events := &corev1.EventList{} - _ = k8sClient.List(ctx, events) - for _, event := range events.Items { - if event.InvolvedObject.Name == objName { - if annotations == nil && len(annotations) == 0 { - result = append(result, event) - } else { - for ak, av := range annotations { - if event.GetAnnotations()[ak] == av { - result = append(result, event) - break - } - } - } - } - } - return result -} - func applyGitRepository(objKey client.ObjectKey, artifactName string, revision string) error { repo := &sourcev1.GitRepository{ TypeMeta: metav1.TypeMeta{ diff --git a/internal/controller/kustomization_origin_revision_test.go b/internal/controller/kustomization_origin_revision_test.go index 5307f69f..054cf92a 100644 --- a/internal/controller/kustomization_origin_revision_test.go +++ b/internal/controller/kustomization_origin_revision_test.go @@ -22,16 +22,18 @@ import ( "testing" "time" - eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" - "github.com/fluxcd/pkg/apis/meta" - "github.com/fluxcd/pkg/testserver" - sourcev1 "github.com/fluxcd/source-controller/api/v1" . "github.com/onsi/gomega" apimeta "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" + eventv1 "github.com/fluxcd/pkg/apis/event/v1" + "github.com/fluxcd/pkg/apis/meta" + "github.com/fluxcd/pkg/runtime/testenv" + "github.com/fluxcd/pkg/testserver" + sourcev1 "github.com/fluxcd/source-controller/api/v1" + kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1" ) @@ -124,7 +126,7 @@ stringData: g.Expect(resultK.Status.History[0].LastReconciledDuration.Duration).To(BeNumerically(">", 0)) g.Expect(resultK.Status.History[0].Metadata).To(ContainElements(revision, "orev")) - events := getEvents(kustomizationKey.Name, nil) + events := testenv.GetEvents(ctx, k8sClient, kustomizationKey.Name, "", nil) g.Expect(events).To(Not(BeEmpty())) annotationKey := kustomizev1.GroupVersion.Group + "/" + eventv1.MetaOriginRevisionKey diff --git a/internal/controller/kustomization_wait_test.go b/internal/controller/kustomization_wait_test.go index fa881379..a3ba8093 100644 --- a/internal/controller/kustomization_wait_test.go +++ b/internal/controller/kustomization_wait_test.go @@ -22,9 +22,8 @@ import ( "testing" "time" - runtimeClient "github.com/fluxcd/pkg/runtime/client" . "github.com/onsi/gomega" - corev1 "k8s.io/api/core/v1" + eventsv1 "k8s.io/api/events/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -35,7 +34,9 @@ import ( "github.com/fluxcd/pkg/apis/kustomize" "github.com/fluxcd/pkg/apis/meta" + runtimeClient "github.com/fluxcd/pkg/runtime/client" "github.com/fluxcd/pkg/runtime/conditions" + "github.com/fluxcd/pkg/runtime/testenv" "github.com/fluxcd/pkg/testserver" sourcev1 "github.com/fluxcd/source-controller/api/v1" @@ -207,10 +208,10 @@ parameters: }) t.Run("emits unhealthy event", func(t *testing.T) { - events := getEvents(resultK.GetName(), map[string]string{"kustomize.toolkit.fluxcd.io/revision": revision}) + events := testenv.GetEvents(ctx, k8sClient, resultK.GetName(), "", map[string]string{"kustomize.toolkit.fluxcd.io/revision": revision}) g.Expect(len(events) > 0).To(BeTrue()) g.Expect(events[len(events)-1].Type).To(BeIdenticalTo("Warning")) - g.Expect(events[len(events)-1].Message).To(ContainSubstring("does-not-exists")) + g.Expect(events[len(events)-1].Note).To(ContainSubstring("does-not-exists")) }) t.Run("recovers and reports healthy status", func(t *testing.T) { @@ -249,10 +250,10 @@ parameters: t.Run("emits recovery event", func(t *testing.T) { expectedMessage := "Health check passed" - events := getEvents(resultK.GetName(), map[string]string{"kustomize.toolkit.fluxcd.io/revision": revision}) + events := testenv.GetEvents(ctx, k8sClient, resultK.GetName(), "", map[string]string{"kustomize.toolkit.fluxcd.io/revision": revision}) g.Expect(len(events) > 1).To(BeTrue()) g.Expect(events[len(events)-2].Type).To(BeIdenticalTo("Normal")) - g.Expect(events[len(events)-2].Message).To(ContainSubstring(expectedMessage)) + g.Expect(events[len(events)-2].Note).To(ContainSubstring(expectedMessage)) }) t.Run("reports new revision healthy status", func(t *testing.T) { @@ -284,10 +285,10 @@ parameters: t.Run("emits event for the new revision", func(t *testing.T) { expectedMessage := "Health check passed" - events := getEvents(resultK.GetName(), map[string]string{"kustomize.toolkit.fluxcd.io/revision": revision}) + events := testenv.GetEvents(ctx, k8sClient, resultK.GetName(), "", map[string]string{"kustomize.toolkit.fluxcd.io/revision": revision}) g.Expect(len(events) > 1).To(BeTrue()) g.Expect(events[len(events)-2].Type).To(BeIdenticalTo("Normal")) - g.Expect(events[len(events)-2].Message).To(ContainSubstring(expectedMessage)) + g.Expect(events[len(events)-2].Note).To(ContainSubstring(expectedMessage)) }) t.Run("finalizes object", func(t *testing.T) { @@ -746,10 +747,10 @@ spec: // Verify the HealthCheckCanceled event was emitted. g.Eventually(func() bool { - events := getEvents(resultK.GetName(), nil) + events := testenv.GetEvents(ctx, k8sClient, resultK.GetName(), "", nil) for _, event := range events { if event.Reason == meta.HealthCheckCanceledReason { - t.Logf("Found HealthCheckCanceled event: %s", event.Message) + t.Logf("Found HealthCheckCanceled event: %s", event.Note) return true } } @@ -757,8 +758,8 @@ spec: }, timeout, time.Second).Should(BeTrue(), "HealthCheckCanceled event should be recorded") // Verify the event message indicates the trigger source. - events := getEvents(resultK.GetName(), nil) - var cancelEvent *corev1.Event + events := testenv.GetEvents(ctx, k8sClient, resultK.GetName(), "", nil) + var cancelEvent *eventsv1.Event for i := range events { if events[i].Reason == meta.HealthCheckCanceledReason { cancelEvent = &events[i] @@ -766,8 +767,8 @@ spec: } } g.Expect(cancelEvent).ToNot(BeNil()) - g.Expect(cancelEvent.Message).To(ContainSubstring("Health checks canceled")) - g.Expect(cancelEvent.Message).To(ContainSubstring("GitRepository")) + g.Expect(cancelEvent.Note).To(ContainSubstring("Health checks canceled")) + g.Expect(cancelEvent.Note).To(ContainSubstring("GitRepository")) } func TestKustomizationReconciler_HealthCheckExprs_GroupOnly(t *testing.T) { diff --git a/internal/controller/suite_test.go b/internal/controller/suite_test.go index a0e0eead..3ee4927f 100644 --- a/internal/controller/suite_test.go +++ b/internal/controller/suite_test.go @@ -29,6 +29,7 @@ import ( "github.com/opencontainers/go-digest" "github.com/ory/dockertest/v3" corev1 "k8s.io/api/core/v1" + eventsv1 "k8s.io/api/events/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/client-go/kubernetes/scheme" @@ -44,6 +45,7 @@ import ( "github.com/fluxcd/pkg/runtime/conditions" kcheck "github.com/fluxcd/pkg/runtime/conditions/check" "github.com/fluxcd/pkg/runtime/controller" + "github.com/fluxcd/pkg/runtime/events" "github.com/fluxcd/pkg/runtime/metrics" "github.com/fluxcd/pkg/runtime/testenv" "github.com/fluxcd/pkg/testserver" @@ -136,12 +138,12 @@ func runInContext(registerControllers func(*testenv.Environment), run func() int code = run() if debugMode { - events := &corev1.EventList{} + events := &eventsv1.EventList{} _ = k8sClient.List(ctx, events) for _, event := range events.Items { fmt.Printf("%s %s \n%s\n", - event.InvolvedObject.Name, event.GetAnnotations()["kustomize.toolkit.fluxcd.io/revision"], - event.Message) + event.Regarding.Name, event.GetAnnotations()["kustomize.toolkit.fluxcd.io/revision"], + event.Note) } } @@ -179,7 +181,7 @@ func TestMain(m *testing.M) { Client: testEnv, Mapper: testEnv.GetRESTMapper(), APIReader: testEnv, - EventRecorder: testEnv.GetEventRecorderFor(controllerName), + EventRecorder: createMockEventRecorder(testEnv, controllerName), Metrics: testMetricsH, DependencyRequeueInterval: 2 * time.Second, ConcurrentSSA: 4, @@ -198,6 +200,15 @@ func TestMain(m *testing.M) { os.Exit(code) } +func createMockEventRecorder(testEnv *testenv.Environment, controllerName string) events.EventRecorder { + logger := ctrl.Log.WithName("events") + eventRecorder, err := events.NewRecorder(testEnv, logger, "", controllerName) + if err != nil { + panic(fmt.Sprintf("Failed to create event recorder: %v", err)) + } + return eventRecorder +} + var letterRunes = []rune("abcdefghijklmnopqrstuvwxyz1234567890") func randStringRunes(n int) string { @@ -241,27 +252,6 @@ func logStatus(t *testing.T, k *kustomizev1.Kustomization) { t.Log(string(sts)) } -func getEvents(objName string, annotations map[string]string) []corev1.Event { - var result []corev1.Event - events := &corev1.EventList{} - _ = k8sClient.List(ctx, events) - for _, event := range events.Items { - if event.InvolvedObject.Name == objName { - if annotations == nil && len(annotations) == 0 { - result = append(result, event) - } else { - for ak, av := range annotations { - if event.GetAnnotations()[ak] == av { - result = append(result, event) - break - } - } - } - } - } - return result -} - func createNamespace(name string) error { namespace := &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{Name: name}, diff --git a/main.go b/main.go index 3201b138..4d795548 100644 --- a/main.go +++ b/main.go @@ -251,8 +251,8 @@ func main() { probes.SetupChecks(mgr, setupLog) - var eventRecorder *events.Recorder - if eventRecorder, err = events.NewRecorder(mgr, ctrl.Log, eventsAddr, controllerName); err != nil { + eventRecorder, err := events.NewRecorder(mgr, ctrl.Log, eventsAddr, controllerName) + if err != nil { setupLog.Error(err, "unable to create event recorder") os.Exit(1) }