From dca993aad860a3f04369be7bf1f8d7c39dd2306d Mon Sep 17 00:00:00 2001 From: Adrian Fernandez De La Torre Date: Sat, 6 Jun 2026 19:02:04 +0200 Subject: [PATCH] Migrate event recording to fluxcd/pkg/runtime/events v1 Replace k8s.io/client-go/tools/record with the Flux events recorder which emits events.k8s.io/v1 Events with structured fields (Action, Related). Add Source to reconcile.Request so events reference the originating source object. Remove the internal FakeRecorder, using events.FakeRecorder from fluxcd/pkg instead Signed-off-by: Adrian Fernandez De La Torre --- internal/controller/helmrelease_controller.go | 33 ++--- .../controller/helmrelease_controller_test.go | 72 +++++------ internal/controller_test/suite_test.go | 27 +--- internal/controller_test/wait_test.go | 15 +-- internal/reconcile/atomic_release.go | 9 +- internal/reconcile/atomic_release_test.go | 28 +++-- internal/reconcile/correct_cluster_drift.go | 15 +-- .../reconcile/correct_cluster_drift_test.go | 37 +++--- internal/reconcile/helmchart_template.go | 22 ++-- internal/reconcile/helmchart_template_test.go | 67 ++++++---- internal/reconcile/install.go | 12 +- internal/reconcile/install_test.go | 108 +++++++++++++--- internal/reconcile/reconcile.go | 3 + internal/reconcile/release.go | 17 +-- internal/reconcile/rollback_remediation.go | 13 +- .../reconcile/rollback_remediation_test.go | 39 +++--- internal/reconcile/test.go | 11 +- internal/reconcile/test_test.go | 35 +++--- internal/reconcile/uninstall.go | 10 +- internal/reconcile/uninstall_remediation.go | 10 +- .../reconcile/uninstall_remediation_test.go | 33 ++--- internal/reconcile/uninstall_test.go | 33 ++--- internal/reconcile/unlock.go | 11 +- internal/reconcile/unlock_test.go | 41 +++--- internal/reconcile/upgrade.go | 12 +- internal/reconcile/upgrade_test.go | 39 +++--- internal/testutil/fake_recorder.go | 117 ------------------ main.go | 4 +- 28 files changed, 449 insertions(+), 424 deletions(-) delete mode 100644 internal/testutil/fake_recorder.go diff --git a/internal/controller/helmrelease_controller.go b/internal/controller/helmrelease_controller.go index e4d201f15..f0605b7e5 100644 --- a/internal/controller/helmrelease_controller.go +++ b/internal/controller/helmrelease_controller.go @@ -39,13 +39,13 @@ import ( "k8s.io/apimachinery/pkg/util/wait" "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/client-go/rest" - 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" "sigs.k8s.io/controller-runtime/pkg/reconcile" aclv1 "github.com/fluxcd/pkg/apis/acl" + 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" @@ -56,14 +56,13 @@ import ( runtimeClient "github.com/fluxcd/pkg/runtime/client" "github.com/fluxcd/pkg/runtime/conditions" helper "github.com/fluxcd/pkg/runtime/controller" + "github.com/fluxcd/pkg/runtime/events" "github.com/fluxcd/pkg/runtime/jitter" "github.com/fluxcd/pkg/runtime/logger" "github.com/fluxcd/pkg/runtime/object" "github.com/fluxcd/pkg/runtime/patch" "github.com/fluxcd/pkg/ssa" - sourcev1 "github.com/fluxcd/source-controller/api/v1" - v2 "github.com/fluxcd/helm-controller/api/v2" intacl "github.com/fluxcd/helm-controller/internal/acl" "github.com/fluxcd/helm-controller/internal/action" @@ -72,6 +71,7 @@ import ( "github.com/fluxcd/helm-controller/internal/kube" "github.com/fluxcd/helm-controller/internal/loader" intreconcile "github.com/fluxcd/helm-controller/internal/reconcile" + sourcev1 "github.com/fluxcd/source-controller/api/v1" ) // +kubebuilder:rbac:groups=helm.toolkit.fluxcd.io,resources=helmreleases,verbs=get;list;watch;create;update;patch;delete @@ -86,7 +86,7 @@ import ( // HelmReleaseReconciler reconciles a HelmRelease object. type HelmReleaseReconciler struct { client.Client - kuberecorder.EventRecorder + events.EventRecorder helper.Metrics // Kubernetes configuration @@ -207,7 +207,7 @@ func (r *HelmReleaseReconciler) Reconcile(ctx context.Context, req ctrl.Request) conditions.MarkFalse(obj, meta.ReadyCondition, meta.InvalidCELExpressionReason, "%s", errMsg) conditions.MarkStalled(obj, meta.InvalidCELExpressionReason, "%s", errMsg) obj.Status.ObservedGeneration = obj.Generation - r.Eventf(obj, corev1.EventTypeWarning, meta.InvalidCELExpressionReason, "%s", err.Error()) + r.Eventf(obj, nil, corev1.EventTypeWarning, meta.InvalidCELExpressionReason, eventv1.ActionFailed, "%s", err.Error()) return ctrl.Result{}, reconcile.TerminalError(err) } @@ -248,7 +248,7 @@ func (r *HelmReleaseReconciler) reconcileRelease(ctx context.Context, errMsg := fmt.Sprintf("%s: %v", terminalErrorMessage, err) conditions.MarkFalse(obj, meta.ReadyCondition, meta.InvalidCELExpressionReason, "%s", errMsg) conditions.MarkStalled(obj, meta.InvalidCELExpressionReason, "%s", errMsg) - r.Eventf(obj, corev1.EventTypeWarning, meta.InvalidCELExpressionReason, "%s", err.Error()) + r.Eventf(obj, nil, corev1.EventTypeWarning, meta.InvalidCELExpressionReason, eventv1.ActionFailed, "%s", err.Error()) return ctrl.Result{}, err } @@ -256,7 +256,7 @@ func (r *HelmReleaseReconciler) reconcileRelease(ctx context.Context, msg := fmt.Sprintf("dependencies do not meet ready condition (%s): retrying in %s", err.Error(), r.DependencyRequeueInterval.String()) conditions.MarkFalse(obj, meta.ReadyCondition, v2.DependencyNotReadyReason, "%s", err) - r.Eventf(obj, corev1.EventTypeNormal, v2.DependencyNotReadyReason, "%s", err.Error()) + r.Eventf(obj, nil, corev1.EventTypeNormal, v2.DependencyNotReadyReason, eventv1.ActionWaiting, "%s", err.Error()) log.Info(msg) // Exponential backoff would cause execution to be prolonged too much, @@ -278,7 +278,7 @@ func (r *HelmReleaseReconciler) reconcileRelease(ctx context.Context, conditions.MarkStalled(obj, aclv1.AccessDeniedReason, "%s", err) conditions.MarkFalse(obj, meta.ReadyCondition, aclv1.AccessDeniedReason, "%s", err) conditions.Delete(obj, meta.ReconcilingCondition) - r.Eventf(obj, corev1.EventTypeWarning, aclv1.AccessDeniedReason, "%s", err.Error()) + r.Eventf(obj, source, corev1.EventTypeWarning, aclv1.AccessDeniedReason, eventv1.ActionFailed, "%s", err.Error()) // Recovering from this is not possible without a restart of the // controller or a change of spec, both triggering a new @@ -317,7 +317,7 @@ func (r *HelmReleaseReconciler) reconcileRelease(ctx context.Context, obj.Spec.ValuesFrom...) if err != nil { conditions.MarkFalse(obj, meta.ReadyCondition, "ValuesError", "%s", err) - r.Eventf(obj, corev1.EventTypeWarning, "ValuesError", "%s", err.Error()) + r.Eventf(obj, source, corev1.EventTypeWarning, "ValuesError", eventv1.ActionFailed, "%s", err.Error()) return ctrl.Result{}, err } // Remove any stale corresponding Ready=False condition with Unknown. @@ -336,7 +336,7 @@ func (r *HelmReleaseReconciler) reconcileRelease(ctx context.Context, } conditions.MarkFalse(obj, meta.ReadyCondition, v2.ArtifactFailedReason, "Could not load chart: %s", err) - r.Eventf(obj, corev1.EventTypeWarning, v2.ArtifactFailedReason, "%s", err.Error()) + r.Eventf(obj, source, corev1.EventTypeWarning, v2.ArtifactFailedReason, eventv1.ActionFailed, "%s", err.Error()) return ctrl.Result{}, err } // Remove any stale corresponding Ready=False condition with Unknown. @@ -375,7 +375,7 @@ func (r *HelmReleaseReconciler) reconcileRelease(ctx context.Context, if reason, changed := action.ReleaseTargetChanged(obj, loadedChart.Name()); changed && (reason != action.TargetChartName || obj.GetUpgrade().GetChartNameChangeStrategy() == v2.ChartNameChangeStrategyReinstall) { log.Info(fmt.Sprintf("release target configuration changed (%s): running uninstall for current release", reason)) - if err = r.reconcileUninstall(ctx, getter, obj); err != nil && !errors.Is(err, intreconcile.ErrNoLatest) { + if err = r.reconcileUninstall(ctx, getter, obj, source); err != nil && !errors.Is(err, intreconcile.ErrNoLatest) { return ctrl.Result{}, err } obj.Status.ClearHistory() @@ -420,6 +420,7 @@ func (r *HelmReleaseReconciler) reconcileRelease(ctx context.Context, // Off we go! if err = intreconcile.NewAtomicRelease(patchHelper, cfg, r.EventRecorder, r.FieldManager, r.DisallowedFieldManagers, r.DefaultToRetryOnFailure).Reconcile(ctx, &intreconcile.Request{ Object: obj, + Source: source, Chart: loadedChart, Values: values, }); err != nil { @@ -429,8 +430,8 @@ func (r *HelmReleaseReconciler) reconcileRelease(ctx context.Context, conditions.MarkFalse(obj, meta.ReadyCondition, meta.HealthCheckCanceledReason, "New reconciliation triggered by %s/%s/%s", qes.Kind, qes.Namespace, qes.Name) log.Info("New reconciliation triggered, canceling health checks", "trigger", qes) - r.Eventf(obj, corev1.EventTypeNormal, meta.HealthCheckCanceledReason, - "Health checks canceled due to new reconciliation triggered by %s/%s/%s", + r.Eventf(obj, source, corev1.EventTypeNormal, meta.HealthCheckCanceledReason, + eventv1.ActionReconciling, "Health checks canceled due to new reconciliation triggered by %s/%s/%s", qes.Kind, qes.Namespace, qes.Name) return ctrl.Result{Requeue: true}, nil } @@ -555,7 +556,7 @@ func (r *HelmReleaseReconciler) reconcileReleaseDeletion(ctx context.Context, ob } // Attempt to uninstall the release. - if err = r.reconcileUninstall(ctx, getter, obj); err != nil && !errors.Is(err, intreconcile.ErrNoLatest) { + if err = r.reconcileUninstall(ctx, getter, obj, nil); err != nil && !errors.Is(err, intreconcile.ErrNoLatest) { return err } if err == nil { @@ -578,7 +579,7 @@ func (r *HelmReleaseReconciler) reconcileChartTemplate(ctx context.Context, obj }) } -func (r *HelmReleaseReconciler) reconcileUninstall(ctx context.Context, getter genericclioptions.RESTClientGetter, obj *v2.HelmRelease) error { +func (r *HelmReleaseReconciler) reconcileUninstall(ctx context.Context, getter genericclioptions.RESTClientGetter, obj *v2.HelmRelease, source sourcev1.Source) error { // Construct config factory for current release first to validate // storage configuration before building the resource manager. cfg, err := action.NewConfigFactory(getter, @@ -601,7 +602,7 @@ func (r *HelmReleaseReconciler) reconcileUninstall(ctx context.Context, getter g cfg.NewResourceManager = resourceManager // Run uninstall. - return intreconcile.NewUninstall(cfg, r.EventRecorder).Reconcile(ctx, &intreconcile.Request{Object: obj}) + return intreconcile.NewUninstall(cfg, r.EventRecorder).Reconcile(ctx, &intreconcile.Request{Object: obj, Source: source}) } // checkDependencies checks if the dependencies of the current HelmRelease are ready. diff --git a/internal/controller/helmrelease_controller_test.go b/internal/controller/helmrelease_controller_test.go index 090092c82..c8edb025c 100644 --- a/internal/controller/helmrelease_controller_test.go +++ b/internal/controller/helmrelease_controller_test.go @@ -39,7 +39,6 @@ import ( "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" - "k8s.io/client-go/tools/record" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" "sigs.k8s.io/controller-runtime/pkg/client/interceptor" @@ -52,6 +51,7 @@ import ( "github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/chartutil" "github.com/fluxcd/pkg/runtime/conditions" + "github.com/fluxcd/pkg/runtime/events" "github.com/fluxcd/pkg/runtime/patch" sourcev1 "github.com/fluxcd/source-controller/api/v1" @@ -106,7 +106,7 @@ func TestHelmReleaseReconciler_reconcileRelease(t *testing.T) { WithStatusSubresource(&v2.HelmRelease{}). WithObjects(dependency, obj). Build(), - EventRecorder: record.NewFakeRecorder(32), + EventRecorder: events.NewFakeRecorder(32, false), DependencyRequeueInterval: 5 * time.Second, } r.APIReader = r.Client @@ -171,7 +171,7 @@ func TestHelmReleaseReconciler_reconcileRelease(t *testing.T) { WithStatusSubresource(&v2.HelmRelease{}). WithObjects(obj). Build(), - EventRecorder: record.NewFakeRecorder(32), + EventRecorder: events.NewFakeRecorder(32, false), } r.APIReader = r.Client @@ -347,7 +347,7 @@ func TestHelmReleaseReconciler_reconcileRelease(t *testing.T) { WithStatusSubresource(&v2.HelmRelease{}). WithObjects(chart, obj). Build(), - EventRecorder: record.NewFakeRecorder(32), + EventRecorder: events.NewFakeRecorder(32, false), } r.APIReader = r.Client @@ -468,7 +468,7 @@ func TestHelmReleaseReconciler_reconcileRelease(t *testing.T) { Client: c, APIReader: c, GetClusterConfig: GetTestClusterConfig, - EventRecorder: record.NewFakeRecorder(32), + EventRecorder: events.NewFakeRecorder(32, false), } res, err := r.reconcileRelease(context.TODO(), patch.NewSerialPatcher(obj, c), obj, nil) @@ -565,7 +565,7 @@ func TestHelmReleaseReconciler_reconcileRelease(t *testing.T) { r := &HelmReleaseReconciler{ Client: c, GetClusterConfig: GetTestClusterConfig, - EventRecorder: record.NewFakeRecorder(32), + EventRecorder: events.NewFakeRecorder(32, false), } //Store the Helm release mock in the test namespace. @@ -667,7 +667,7 @@ func TestHelmReleaseReconciler_reconcileRelease(t *testing.T) { r := &HelmReleaseReconciler{ Client: c, GetClusterConfig: GetTestClusterConfig, - EventRecorder: record.NewFakeRecorder(32), + EventRecorder: events.NewFakeRecorder(32, false), } //Store the Helm release mock in the test namespace. @@ -752,7 +752,7 @@ func TestHelmReleaseReconciler_reconcileRelease(t *testing.T) { Client: c, APIReader: c, GetClusterConfig: GetTestClusterConfig, - EventRecorder: record.NewFakeRecorder(32), + EventRecorder: events.NewFakeRecorder(32, false), } _, err = r.reconcileRelease(context.TODO(), patch.NewSerialPatcher(obj, c), obj, nil) @@ -825,7 +825,7 @@ func TestHelmReleaseReconciler_reconcileRelease(t *testing.T) { Client: c, APIReader: c, GetClusterConfig: GetTestClusterConfig, - EventRecorder: record.NewFakeRecorder(32), + EventRecorder: events.NewFakeRecorder(32, false), } res, err := r.reconcileRelease(context.TODO(), patch.NewSerialPatcher(obj, c), obj, nil) @@ -913,7 +913,7 @@ func TestHelmReleaseReconciler_reconcileRelease(t *testing.T) { Client: c, APIReader: c, GetClusterConfig: GetTestClusterConfig, - EventRecorder: record.NewFakeRecorder(32), + EventRecorder: events.NewFakeRecorder(32, false), } _, err = r.reconcileRelease(context.TODO(), patch.NewSerialPatcher(obj, c), obj, nil) @@ -1003,7 +1003,7 @@ func TestHelmReleaseReconciler_reconcileRelease(t *testing.T) { Client: c, APIReader: c, GetClusterConfig: GetTestClusterConfig, - EventRecorder: record.NewFakeRecorder(32), + EventRecorder: events.NewFakeRecorder(32, false), FieldManager: "test", } @@ -1119,7 +1119,7 @@ func TestHelmReleaseReconciler_reconcileReleaseFromHelmChartSource(t *testing.T) WithStatusSubresource(&v2.HelmRelease{}). WithObjects(obj). Build(), - EventRecorder: record.NewFakeRecorder(32), + EventRecorder: events.NewFakeRecorder(32, false), } _, err := r.reconcileRelease(context.TODO(), patch.NewSerialPatcher(obj, r.Client), obj, nil) @@ -1154,7 +1154,7 @@ func TestHelmReleaseReconciler_reconcileReleaseFromHelmChartSource(t *testing.T) WithStatusSubresource(&v2.HelmRelease{}). WithObjects(obj). Build(), - EventRecorder: record.NewFakeRecorder(32), + EventRecorder: events.NewFakeRecorder(32, false), } res, err := r.reconcileRelease(context.TODO(), patch.NewSerialPatcher(obj, r.Client), obj, nil) @@ -1272,7 +1272,7 @@ func TestHelmReleaseReconciler_reconcileReleaseFromHelmChartSource(t *testing.T) WithObjects(chart, obj). Build(), DependencyRequeueInterval: 10 * time.Second, - EventRecorder: record.NewFakeRecorder(32), + EventRecorder: events.NewFakeRecorder(32, false), } res, err := r.reconcileRelease(context.TODO(), patch.NewSerialPatcher(obj, r.Client), obj, nil) @@ -1352,7 +1352,7 @@ func TestHelmReleaseReconciler_reconcileReleaseFromHelmChartSource(t *testing.T) WithObjects(chart, sharedChart, obj). Build(), DependencyRequeueInterval: 10 * time.Second, - EventRecorder: record.NewFakeRecorder(32), + EventRecorder: events.NewFakeRecorder(32, false), } res, err := r.reconcileRelease(context.TODO(), patch.NewSerialPatcher(obj, r.Client), obj, nil) @@ -1479,7 +1479,7 @@ func TestHelmReleaseReconciler_reconcileReleaseFromHelmChartSource(t *testing.T) r := &HelmReleaseReconciler{ Client: c, GetClusterConfig: GetTestClusterConfig, - EventRecorder: record.NewFakeRecorder(32), + EventRecorder: events.NewFakeRecorder(32, false), } //Store the Helm release mock in the test namespace. @@ -1586,7 +1586,7 @@ func TestHelmReleaseReconciler_reconcileReleaseFromOCIRepositorySource(t *testin WithStatusSubresource(&v2.HelmRelease{}). WithObjects(obj). Build(), - EventRecorder: record.NewFakeRecorder(32), + EventRecorder: events.NewFakeRecorder(32, false), } _, err := r.reconcileRelease(context.TODO(), patch.NewSerialPatcher(obj, r.Client), obj, nil) @@ -1621,7 +1621,7 @@ func TestHelmReleaseReconciler_reconcileReleaseFromOCIRepositorySource(t *testin WithStatusSubresource(&v2.HelmRelease{}). WithObjects(obj). Build(), - EventRecorder: record.NewFakeRecorder(32), + EventRecorder: events.NewFakeRecorder(32, false), } res, err := r.reconcileRelease(context.TODO(), patch.NewSerialPatcher(obj, r.Client), obj, nil) @@ -1742,7 +1742,7 @@ func TestHelmReleaseReconciler_reconcileReleaseFromOCIRepositorySource(t *testin WithStatusSubresource(&v2.HelmRelease{}). WithObjects(ocirepo, obj). Build(), - EventRecorder: record.NewFakeRecorder(32), + EventRecorder: events.NewFakeRecorder(32, false), } _, err := r.reconcileRelease(context.TODO(), patch.NewSerialPatcher(obj, r.Client), obj, nil) @@ -1801,7 +1801,7 @@ func TestHelmReleaseReconciler_reconcileReleaseFromOCIRepositorySource(t *testin WithObjects(ocirepo, obj). Build(), DependencyRequeueInterval: 10 * time.Second, - EventRecorder: record.NewFakeRecorder(32), + EventRecorder: events.NewFakeRecorder(32, false), } res, err := r.reconcileRelease(context.TODO(), patch.NewSerialPatcher(obj, r.Client), obj, nil) @@ -1881,7 +1881,7 @@ func TestHelmReleaseReconciler_reconcileReleaseFromOCIRepositorySource(t *testin WithObjects(chart, ocirepo, obj). Build(), DependencyRequeueInterval: 10 * time.Second, - EventRecorder: record.NewFakeRecorder(32), + EventRecorder: events.NewFakeRecorder(32, false), } res, err := r.reconcileRelease(context.TODO(), patch.NewSerialPatcher(obj, r.Client), obj, nil) @@ -1945,7 +1945,7 @@ func TestHelmReleaseReconciler_reconcileReleaseFromOCIRepositorySource(t *testin WithObjects(ocirepo, obj). Build(), GetClusterConfig: GetTestClusterConfig, - EventRecorder: record.NewFakeRecorder(32), + EventRecorder: events.NewFakeRecorder(32, false), } _, err = r.reconcileRelease(context.TODO(), patch.NewSerialPatcher(obj, r.Client), obj, nil) @@ -2039,7 +2039,7 @@ func TestHelmReleaseReconciler_reconcileReleaseFromOCIRepositorySource(t *testin r := &HelmReleaseReconciler{ Client: c, GetClusterConfig: GetTestClusterConfig, - EventRecorder: record.NewFakeRecorder(32), + EventRecorder: events.NewFakeRecorder(32, false), } _, err = r.reconcileRelease(context.TODO(), patch.NewSerialPatcher(obj, r.Client), obj, nil) @@ -2136,7 +2136,7 @@ func TestHelmReleaseReconciler_reconcileReleaseFromOCIRepositorySource(t *testin r := &HelmReleaseReconciler{ Client: c, GetClusterConfig: GetTestClusterConfig, - EventRecorder: record.NewFakeRecorder(32), + EventRecorder: events.NewFakeRecorder(32, false), } _, err = r.reconcileRelease(context.TODO(), patch.NewSerialPatcher(obj, r.Client), obj, nil) @@ -2213,7 +2213,7 @@ func TestHelmReleaseReconciler_reconcileReleaseFromOCIRepositorySource(t *testin r := &HelmReleaseReconciler{ Client: c, GetClusterConfig: GetTestClusterConfig, - EventRecorder: record.NewFakeRecorder(32), + EventRecorder: events.NewFakeRecorder(32, false), } _, err = r.reconcileRelease(context.TODO(), patch.NewSerialPatcher(obj, r.Client), obj, nil) @@ -2332,7 +2332,7 @@ func TestHelmReleaseReconciler_reconcileReleaseFromOCIRepositorySource(t *testin r := &HelmReleaseReconciler{ Client: c, GetClusterConfig: GetTestClusterConfig, - EventRecorder: record.NewFakeRecorder(32), + EventRecorder: events.NewFakeRecorder(32, false), } // Store the Helm release mock in the test namespace. @@ -2427,7 +2427,7 @@ func TestHelmReleaseReconciler_reconcileDelete(t *testing.T) { Client: testEnv.Client, APIReader: testEnv.Client, GetClusterConfig: GetTestClusterConfig, - EventRecorder: record.NewFakeRecorder(32), + EventRecorder: events.NewFakeRecorder(32, false), } // Store the Helm release mock in the test namespace. @@ -2538,7 +2538,7 @@ func TestHelmReleaseReconciler_reconcileReleaseDeletion(t *testing.T) { Client: testEnv.Client, APIReader: testEnv.Client, GetClusterConfig: GetTestClusterConfig, - EventRecorder: record.NewFakeRecorder(32), + EventRecorder: events.NewFakeRecorder(32, false), } // Store the Helm release mock in the test namespace. @@ -2787,7 +2787,7 @@ func TestHelmReleaseReconciler_reconcileReleaseDeletion(t *testing.T) { Host: "https://failing-mock.local", }, nil }, - EventRecorder: record.NewFakeRecorder(32), + EventRecorder: events.NewFakeRecorder(32, false), } obj := &v2.HelmRelease{ @@ -2877,7 +2877,7 @@ func TestHelmReleaseReconciler_reconcileChartTemplate(t *testing.T) { r := &HelmReleaseReconciler{ Client: fake.NewClientBuilder().WithScheme(NewTestScheme()).Build(), - EventRecorder: record.NewFakeRecorder(32), + EventRecorder: events.NewFakeRecorder(32, false), } obj := &v2.HelmRelease{ @@ -2909,7 +2909,7 @@ func TestHelmReleaseReconciler_reconcileUninstall(t *testing.T) { } // We do not care about the result of the uninstall, only that it was attempted. - err := (&HelmReleaseReconciler{}).reconcileUninstall(context.TODO(), getter, obj) + err := (&HelmReleaseReconciler{}).reconcileUninstall(context.TODO(), getter, obj, nil) g.Expect(err).To(HaveOccurred()) g.Expect(errors.Is(err, intreconcile.ErrNoLatest)).To(BeTrue()) }) @@ -2923,7 +2923,7 @@ func TestHelmReleaseReconciler_reconcileUninstall(t *testing.T) { }, } - err := (&HelmReleaseReconciler{}).reconcileUninstall(context.TODO(), nil, obj) + err := (&HelmReleaseReconciler{}).reconcileUninstall(context.TODO(), nil, obj, nil) g.Expect(err).To(HaveOccurred()) g.Expect(conditions.IsFalse(obj, meta.ReadyCondition)).To(BeTrue()) @@ -2973,7 +2973,7 @@ func TestHelmReleaseReconciler_reconcileUninstall(t *testing.T) { Client: testEnv.Client, APIReader: testEnv.Client, GetClusterConfig: GetTestClusterConfig, - EventRecorder: record.NewFakeRecorder(32), + EventRecorder: events.NewFakeRecorder(32, false), } // Store the Helm release mock in the test namespace. @@ -2986,7 +2986,7 @@ func TestHelmReleaseReconciler_reconcileUninstall(t *testing.T) { store := helmstorage.Init(cfg.Driver) g.Expect(store.Create(rls)).To(Succeed()) - err = r.reconcileUninstall(context.TODO(), getter, obj) + err = r.reconcileUninstall(context.TODO(), getter, obj, nil) g.Expect(err).To(HaveOccurred()) // Verify status of Helm release has not been updated. @@ -3613,7 +3613,7 @@ func TestHelmReleaseReconciler_getHelmChart(t *testing.T) { r := &HelmReleaseReconciler{ Client: c.Build(), - EventRecorder: record.NewFakeRecorder(32), + EventRecorder: events.NewFakeRecorder(32, false), } curAllow := intacl.AllowCrossNamespaceRef @@ -3732,7 +3732,7 @@ func TestHelmReleaseReconciler_getSourceFromOCIRef_DirectSourceFetch(t *testing. Client: fakeClient, APIReader: fakeAPIReader, DirectSourceFetch: tt.directSourceFetch, - EventRecorder: record.NewFakeRecorder(32), + EventRecorder: events.NewFakeRecorder(32, false), } rel := &v2.HelmRelease{ diff --git a/internal/controller_test/suite_test.go b/internal/controller_test/suite_test.go index f78053ef6..4def9f73d 100644 --- a/internal/controller_test/suite_test.go +++ b/internal/controller_test/suite_test.go @@ -28,6 +28,7 @@ import ( "go.uber.org/zap/zapcore" "helm.sh/helm/v4/pkg/kube" corev1 "k8s.io/api/core/v1" + eventsv1 "k8s.io/api/events/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -44,10 +45,10 @@ import ( helper "github.com/fluxcd/pkg/runtime/controller" "github.com/fluxcd/pkg/runtime/testenv" "github.com/fluxcd/pkg/testserver" - sourcev1 "github.com/fluxcd/source-controller/api/v1" v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/controller" + sourcev1 "github.com/fluxcd/source-controller/api/v1" // +kubebuilder:scaffold:imports ) @@ -75,6 +76,7 @@ func randStringRunes(n int) string { func NewTestScheme() *runtime.Scheme { s := runtime.NewScheme() utilruntime.Must(corev1.AddToScheme(s)) + utilruntime.Must(eventsv1.AddToScheme(s)) utilruntime.Must(apiextensionsv1.AddToScheme(s)) utilruntime.Must(sourcev1.AddToScheme(s)) utilruntime.Must(v2.AddToScheme(s)) @@ -166,7 +168,7 @@ func StartController() error { timeout := time.Second * 10 reconciler = &controller.HelmReleaseReconciler{ Client: testEnv, - EventRecorder: testEnv.GetEventRecorderFor(controllerName), + EventRecorder: testEnv.GetEventRecorder(controllerName), Metrics: helper.Metrics{}, GetClusterConfig: GetTestClusterConfig, ClientOpts: rclient.Options{ @@ -279,24 +281,3 @@ func applyHelmChart(objKey client.ObjectKey, artifact *meta.Artifact) error { } return nil } - -func getEvents(objName string, annotations map[string]string) []corev1.Event { - var result []corev1.Event - events := &corev1.EventList{} - _ = k8sClient.List(testCtx, events) - for _, event := range events.Items { - if event.InvolvedObject.Name == objName { - if 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 -} diff --git a/internal/controller_test/wait_test.go b/internal/controller_test/wait_test.go index 3c03dc868..9602d37f3 100644 --- a/internal/controller_test/wait_test.go +++ b/internal/controller_test/wait_test.go @@ -23,7 +23,7 @@ import ( "time" "github.com/opencontainers/go-digest" - corev1 "k8s.io/api/core/v1" + eventsv1 "k8s.io/api/events/v1" apimeta "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -31,6 +31,7 @@ import ( "github.com/fluxcd/pkg/apis/kustomize" "github.com/fluxcd/pkg/apis/meta" + "github.com/fluxcd/pkg/runtime/testenv" sourcev1 "github.com/fluxcd/source-controller/api/v1" . "github.com/onsi/gomega" @@ -226,10 +227,10 @@ func TestHelmReleaseReconciler_CancelHealthCheckOnNewRevision(t *testing.T) { // Verify the HealthCheckCanceled event was emitted. g.Eventually(func() bool { - events := getEvents(resultHR.GetName(), nil) + events := testenv.GetEvents(testCtx, k8sClient, resultHR.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 } } @@ -237,8 +238,8 @@ func TestHelmReleaseReconciler_CancelHealthCheckOnNewRevision(t *testing.T) { }, timeout, time.Second).Should(BeTrue(), "HealthCheckCanceled event should be recorded") // Verify the event message indicates the trigger source. - events := getEvents(resultHR.GetName(), nil) - var cancelEvent *corev1.Event + events := testenv.GetEvents(testCtx, k8sClient, resultHR.GetName(), "", nil) + var cancelEvent *eventsv1.Event for i := range events { if events[i].Reason == meta.HealthCheckCanceledReason { cancelEvent = &events[i] @@ -246,8 +247,8 @@ func TestHelmReleaseReconciler_CancelHealthCheckOnNewRevision(t *testing.T) { } } g.Expect(cancelEvent).ToNot(BeNil()) - g.Expect(cancelEvent.Message).To(ContainSubstring("Health checks canceled")) - g.Expect(cancelEvent.Message).To(ContainSubstring("HelmChart")) + g.Expect(cancelEvent.Note).To(ContainSubstring("Health checks canceled")) + g.Expect(cancelEvent.Note).To(ContainSubstring("HelmChart")) // Verify the HelmRelease becomes Ready after the fixed revision is reconciled. g.Eventually(func() bool { diff --git a/internal/reconcile/atomic_release.go b/internal/reconcile/atomic_release.go index 8025e22d9..e9838c783 100644 --- a/internal/reconcile/atomic_release.go +++ b/internal/reconcile/atomic_release.go @@ -26,11 +26,12 @@ import ( "helm.sh/helm/v4/pkg/kube" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/tools/record" ctrl "sigs.k8s.io/controller-runtime" + eventv1 "github.com/fluxcd/pkg/apis/event/v1" "github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/runtime/conditions" + "github.com/fluxcd/pkg/runtime/events" "github.com/fluxcd/pkg/runtime/logger" "github.com/fluxcd/pkg/runtime/patch" "github.com/fluxcd/pkg/ssa/jsondiff" @@ -113,7 +114,7 @@ var ( type AtomicRelease struct { patchHelper *patch.SerialPatcher configFactory *action.ConfigFactory - eventRecorder record.EventRecorder + eventRecorder events.EventRecorder strategy releaseStrategy fieldManager string disallowedFieldManagers []string @@ -122,7 +123,7 @@ type AtomicRelease struct { // NewAtomicRelease returns a new AtomicRelease reconciler configured with the // provided values. -func NewAtomicRelease(patchHelper *patch.SerialPatcher, cfg *action.ConfigFactory, recorder record.EventRecorder, fieldManager string, disallowedFieldManagers []string, defaultToRetryOnFailure bool) *AtomicRelease { +func NewAtomicRelease(patchHelper *patch.SerialPatcher, cfg *action.ConfigFactory, recorder events.EventRecorder, fieldManager string, disallowedFieldManagers []string, defaultToRetryOnFailure bool) *AtomicRelease { return &AtomicRelease{ patchHelper: patchHelper, eventRecorder: recorder, @@ -442,7 +443,7 @@ func (r *AtomicRelease) actionForState(ctx context.Context, req *Request, state msg := fmt.Sprintf("Cluster state of release %s has drifted from the desired state:\n%s", req.Object.Status.History.Latest().FullReleaseName(), diff.SummarizeDiffSet(state.Diff)) - r.eventRecorder.Eventf(req.Object, corev1.EventTypeWarning, v2.DriftDetectedReason, "%s", msg) + r.eventRecorder.Eventf(req.Object, req.Source, corev1.EventTypeWarning, "DriftDetected", eventv1.ActionReconciling, "%s", msg) conditions.MarkTrue(req.Object, v2.DriftedCondition, v2.DriftDetectedReason, "%s", msg) if req.Object.GetDriftDetection().GetMode() == v2.DriftDetectionEnabled { diff --git a/internal/reconcile/atomic_release_test.go b/internal/reconcile/atomic_release_test.go index 7637c3bc4..0d207d41f 100644 --- a/internal/reconcile/atomic_release_test.go +++ b/internal/reconcile/atomic_release_test.go @@ -31,14 +31,16 @@ import ( helmstorage "helm.sh/helm/v4/pkg/storage" helmdriver "helm.sh/helm/v4/pkg/storage/driver" corev1 "k8s.io/api/core/v1" + eventsv1 "k8s.io/api/events/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/client-go/tools/record" "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client/fake" + eventv1 "github.com/fluxcd/pkg/apis/event/v1" "github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/runtime/conditions" + "github.com/fluxcd/pkg/runtime/events" "github.com/fluxcd/pkg/runtime/patch" "github.com/fluxcd/pkg/ssa/jsondiff" @@ -171,7 +173,7 @@ func TestAtomicRelease_Reconcile(t *testing.T) { WithStatusSubresource(&v2.HelmRelease{}). Build() patchHelper := patch.NewSerialPatcher(obj, client) - recorder := new(record.FakeRecorder) + recorder := new(events.FakeRecorder) req := &Request{ Object: obj, @@ -1222,7 +1224,7 @@ func TestAtomicRelease_Reconcile_Scenarios(t *testing.T) { WithStatusSubresource(&v2.HelmRelease{}). Build() patchHelper := patch.NewSerialPatcher(obj, client) - recorder := new(record.FakeRecorder) + recorder := new(events.FakeRecorder) req := &Request{ Object: obj, @@ -1454,7 +1456,7 @@ func TestAtomicRelease_Reconcile_PostRenderers_Scenarios(t *testing.T) { WithStatusSubresource(&v2.HelmRelease{}). Build() patchHelper := patch.NewSerialPatcher(obj, client) - recorder := new(record.FakeRecorder) + recorder := new(events.FakeRecorder) req := &Request{ Object: obj, @@ -1483,7 +1485,7 @@ func TestAtomicRelease_actionForState(t *testing.T) { status func(releases []*helmrelease.Release) v2.HelmReleaseStatus state ReleaseState want ActionReconciler - wantEvent *corev1.Event + wantEvent *eventsv1.Event wantErr error assertConditions []metav1.Condition }{ @@ -1647,10 +1649,11 @@ func TestAtomicRelease_actionForState(t *testing.T) { } }, want: &CorrectClusterDrift{}, - wantEvent: &corev1.Event{ + wantEvent: &eventsv1.Event{ Reason: "DriftDetected", Type: corev1.EventTypeWarning, - Message: fmt.Sprintf( + Action: eventv1.ActionReconciling, + Note: fmt.Sprintf( "Cluster state of release %s has drifted from the desired state:\n%s", mockReleaseNamespace+"/"+mockReleaseName+".v1", "Deployment/something/mock removed", @@ -1719,10 +1722,11 @@ func TestAtomicRelease_actionForState(t *testing.T) { }}, want: nil, wantErr: nil, - wantEvent: &corev1.Event{ + wantEvent: &eventsv1.Event{ Reason: "DriftDetected", Type: corev1.EventTypeWarning, - Message: fmt.Sprintf( + Action: eventv1.ActionReconciling, + Note: fmt.Sprintf( "Cluster state of release %s has drifted from the desired state:\n%s", mockReleaseNamespace+"/"+mockReleaseName+".v1", "Deployment/something/mock changed (0 additions, 1 changes, 0 removals)", @@ -2093,7 +2097,7 @@ func TestAtomicRelease_actionForState(t *testing.T) { } } - recorder := testutil.NewFakeRecorder(1, false) + recorder := events.NewFakeRecorder(1, false) r := &AtomicRelease{configFactory: cfg, eventRecorder: recorder} got, err := r.actionForState(context.TODO(), &Request{Object: obj}, tt.state) @@ -2111,7 +2115,7 @@ func TestAtomicRelease_actionForState(t *testing.T) { g.Expect(got).To(want) if tt.wantEvent != nil { - g.Expect(recorder.GetEvents()).To(ConsistOf([]corev1.Event{*tt.wantEvent})) + g.Expect(recorder.GetEvents()).To(ConsistOf([]eventsv1.Event{*tt.wantEvent})) } else { g.Expect(recorder.GetEvents()).To(BeEmpty()) } @@ -2442,7 +2446,7 @@ func TestAtomicRelease_Reconcile_CommonMetadata_Scenarios(t *testing.T) { WithStatusSubresource(&v2.HelmRelease{}). Build() patchHelper := patch.NewSerialPatcher(obj, client) - recorder := new(record.FakeRecorder) + recorder := new(events.FakeRecorder) req := &Request{ Object: obj, diff --git a/internal/reconcile/correct_cluster_drift.go b/internal/reconcile/correct_cluster_drift.go index 6a5675683..97b799461 100644 --- a/internal/reconcile/correct_cluster_drift.go +++ b/internal/reconcile/correct_cluster_drift.go @@ -22,10 +22,11 @@ import ( corev1 "k8s.io/api/core/v1" apierrutil "k8s.io/apimachinery/pkg/util/errors" - "k8s.io/client-go/tools/record" + eventv1 "github.com/fluxcd/pkg/apis/event/v1" "github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/runtime/conditions" + "github.com/fluxcd/pkg/runtime/events" "github.com/fluxcd/pkg/ssa" "github.com/fluxcd/pkg/ssa/jsondiff" @@ -44,12 +45,12 @@ import ( // whether the cluster state was successfully corrected or not. type CorrectClusterDrift struct { configFactory *action.ConfigFactory - eventRecorder record.EventRecorder + eventRecorder events.EventRecorder diff jsondiff.DiffSet fieldManager string } -func NewCorrectClusterDrift(configFactory *action.ConfigFactory, recorder record.EventRecorder, diff jsondiff.DiffSet, fieldManager string) *CorrectClusterDrift { +func NewCorrectClusterDrift(configFactory *action.ConfigFactory, recorder events.EventRecorder, diff jsondiff.DiffSet, fieldManager string) *CorrectClusterDrift { return &CorrectClusterDrift{ configFactory: configFactory, eventRecorder: recorder, @@ -104,13 +105,13 @@ func (r *CorrectClusterDrift) report(obj *v2.HelmRelease, changeSet *ssa.ChangeS sb.WriteString(changeSet.String()) } - r.eventRecorder.AnnotatedEventf(obj, eventMeta(cur.ChartVersion, cur.ConfigDigest, + r.eventRecorder.AnnotatedEventf(obj, nil, eventMeta(cur.ChartVersion, cur.ConfigDigest, addAppVersion(cur.AppVersion), addOCIDigest(cur.OCIDigest)), corev1.EventTypeWarning, - "DriftCorrectionFailed", "%s", sb.String()) + "DriftCorrectionFailed", eventv1.ActionFailed, "%s", sb.String()) case changeSet != nil && len(changeSet.Entries) > 0: - r.eventRecorder.AnnotatedEventf(obj, eventMeta(cur.ChartVersion, cur.ConfigDigest, + r.eventRecorder.AnnotatedEventf(obj, nil, eventMeta(cur.ChartVersion, cur.ConfigDigest, addAppVersion(cur.AppVersion), addOCIDigest(cur.OCIDigest)), corev1.EventTypeNormal, - "DriftCorrected", "Cluster state of release %s has been corrected:\n%s", + "DriftCorrected", eventv1.ActionApplied, "Cluster state of release %s has been corrected:\n%s", obj.Status.History.Latest().FullReleaseName(), changeSet.String()) } } diff --git a/internal/reconcile/correct_cluster_drift_test.go b/internal/reconcile/correct_cluster_drift_test.go index b3d27ac45..abfc1fb6a 100644 --- a/internal/reconcile/correct_cluster_drift_test.go +++ b/internal/reconcile/correct_cluster_drift_test.go @@ -24,19 +24,21 @@ import ( . "github.com/onsi/gomega" extjsondiff "github.com/wI2L/jsondiff" corev1 "k8s.io/api/core/v1" + eventsv1 "k8s.io/api/events/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" apierrutil "k8s.io/apimachinery/pkg/util/errors" "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/conditions" + "github.com/fluxcd/pkg/runtime/events" "github.com/fluxcd/pkg/ssa" "github.com/fluxcd/pkg/ssa/jsondiff" v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/action" - "github.com/fluxcd/helm-controller/internal/testutil" ) func TestCorrectClusterDrift_Reconcile(t *testing.T) { @@ -145,7 +147,7 @@ func TestCorrectClusterDrift_Reconcile(t *testing.T) { cfg, err := action.NewConfigFactory(getter, action.WithStorage(action.DefaultStorageDriver, namedNS.Name)) g.Expect(err).ToNot(HaveOccurred()) - recorder := testutil.NewFakeRecorder(10, false) + recorder := events.NewFakeRecorder(10, false) r := NewCorrectClusterDrift(cfg, recorder, tt.diff(namedNS.Name), testFieldManager) g.Expect(r.Reconcile(context.TODO(), &Request{ @@ -183,7 +185,7 @@ func TestCorrectClusterDrift_report(t *testing.T) { obj *v2.HelmRelease changeSet *ssa.ChangeSet err error - wantEvent []corev1.Event + wantEvent []eventsv1.Event }{ { name: "with multiple changes", @@ -200,11 +202,12 @@ func TestCorrectClusterDrift_report(t *testing.T) { }, }, }, - wantEvent: []corev1.Event{ + wantEvent: []eventsv1.Event{ { Type: corev1.EventTypeNormal, Reason: "DriftCorrected", - Message: `Cluster state of release mock-ns/mock-release.v3 has been corrected: + Action: eventv1.ActionApplied, + Note: `Cluster state of release mock-ns/mock-release.v3 has been corrected: Secret/namespace/name created Deployment/namespace/name configured`, }, @@ -233,11 +236,12 @@ Deployment/namespace/name configured`, errors.New("error 1"), errors.New("error 2"), }), - wantEvent: []corev1.Event{ + wantEvent: []eventsv1.Event{ { Type: corev1.EventTypeWarning, Reason: "DriftCorrectionFailed", - Message: `Failed to partially correct cluster state of release mock-ns/mock-release.v3: + Action: eventv1.ActionFailed, + Note: `Failed to partially correct cluster state of release mock-ns/mock-release.v3: error 1 error 2 @@ -255,11 +259,12 @@ ConfigMap/namespace/name configured`, errors.New("error 1"), errors.New("error 2"), }), - wantEvent: []corev1.Event{ + wantEvent: []eventsv1.Event{ { + Action: eventv1.ActionFailed, Type: corev1.EventTypeWarning, Reason: "DriftCorrectionFailed", - Message: `Failed to correct cluster state of release mock-ns/mock-release.v3: + Note: `Failed to correct cluster state of release mock-ns/mock-release.v3: error 1 error 2`, }, @@ -276,11 +281,12 @@ error 2`, }, }, }, - wantEvent: []corev1.Event{ + wantEvent: []eventsv1.Event{ { + Action: eventv1.ActionApplied, Type: corev1.EventTypeNormal, Reason: "DriftCorrected", - Message: `Cluster state of release mock-ns/mock-release.v3 has been corrected: + Note: `Cluster state of release mock-ns/mock-release.v3 has been corrected: Secret/namespace/name created`, }, }, @@ -289,11 +295,12 @@ Secret/namespace/name created`, name: "with single error", obj: mockObj.DeepCopy(), err: errors.New("error 1"), - wantEvent: []corev1.Event{ + wantEvent: []eventsv1.Event{ { Type: corev1.EventTypeWarning, Reason: "DriftCorrectionFailed", - Message: `Failed to correct cluster state of release mock-ns/mock-release.v3: + Action: eventv1.ActionFailed, + Note: `Failed to correct cluster state of release mock-ns/mock-release.v3: error 1`, }, }, @@ -301,7 +308,7 @@ error 1`, { name: "empty change set", obj: mockObj.DeepCopy(), - wantEvent: []corev1.Event{}, + wantEvent: []eventsv1.Event{}, }, } @@ -309,7 +316,7 @@ error 1`, t.Run(tt.name, func(t *testing.T) { g := NewWithT(t) - recorder := testutil.NewFakeRecorder(10, false) + recorder := events.NewFakeRecorder(10, false) r := &CorrectClusterDrift{ eventRecorder: recorder, } diff --git a/internal/reconcile/helmchart_template.go b/internal/reconcile/helmchart_template.go index 9768803e8..d187259b5 100644 --- a/internal/reconcile/helmchart_template.go +++ b/internal/reconcile/helmchart_template.go @@ -25,12 +25,12 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" - "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/client/apiutil" - eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" + eventv1 "github.com/fluxcd/pkg/apis/event/v1" + "github.com/fluxcd/pkg/runtime/events" "github.com/fluxcd/pkg/ssa" sourcev1 "github.com/fluxcd/source-controller/api/v1" @@ -62,13 +62,13 @@ import ( // not continue to attempt to create or update the v1.HelmChart. type HelmChartTemplate struct { client client.Client - eventRecorder record.EventRecorder + eventRecorder events.EventRecorder fieldManager string } // NewHelmChartTemplate returns a new HelmChartTemplate reconciler configured // with the provided values. -func NewHelmChartTemplate(client client.Client, recorder record.EventRecorder, fieldManager string) *HelmChartTemplate { +func NewHelmChartTemplate(client client.Client, recorder events.EventRecorder, fieldManager string) *HelmChartTemplate { return &HelmChartTemplate{ client: client, eventRecorder: recorder, @@ -92,7 +92,7 @@ func (r *HelmChartTemplate) Reconcile(ctx context.Context, req *Request) error { if (obj.Status.HelmChart != "" && obj.Status.HelmChart != chartRef.String()) || !obj.DeletionTimestamp.IsZero() { // If the HelmRelease is being deleted, we need to short-circuit to // avoid recreating the HelmChart. - if err := r.reconcileDelete(ctx, req.Object); err != nil || !obj.DeletionTimestamp.IsZero() { + if err := r.reconcileDelete(ctx, req.Object, req.Source); err != nil || !obj.DeletionTimestamp.IsZero() { return err } } @@ -100,7 +100,7 @@ func (r *HelmChartTemplate) Reconcile(ctx context.Context, req *Request) error { if mustCleanDeployedChart(obj) { // If the HelmRelease has a ChartRef and no Chart template, and the // HelmChart is present, we need to clean it up. - if err := r.reconcileDelete(ctx, req.Object); err != nil { + if err := r.reconcileDelete(ctx, req.Object, req.Source); err != nil { return err } return nil @@ -145,7 +145,7 @@ func (r *HelmChartTemplate) Reconcile(ctx context.Context, req *Request) error { entry, err := rm.Apply(ctx, u, ssa.DefaultApplyOptions()) if err != nil { err = fmt.Errorf("failed to run server-side apply: %w", err) - r.eventRecorder.Eventf(req.Object, eventv1.EventTypeTrace, "HelmChartSyncErr", "%s", err.Error()) + r.eventRecorder.Eventf(req.Object, req.Source, eventv1.EventTypeTrace, "HelmChartSyncErr", eventv1.ActionFailed, "%s", err.Error()) return err } @@ -158,8 +158,8 @@ func (r *HelmChartTemplate) Reconcile(ctx context.Context, req *Request) error { )) ctrl.LoggerFrom(ctx).Info(msg) - r.eventRecorder.Eventf(req.Object, eventv1.EventTypeTrace, - fmt.Sprintf("HelmChart%s", strings.Title(entry.Action.String())), "%s", msg) + r.eventRecorder.Eventf(req.Object, req.Source, eventv1.EventTypeTrace, + fmt.Sprintf("HelmChart%s", strings.Title(entry.Action.String())), entry.Action.String(), "%s", msg) case ssa.UnchangedAction: msg := fmt.Sprintf("%s with SourceRef '%s/%s/%s' is in-sync", entry.Subject, newChart.Spec.SourceRef.Kind, newChart.GetNamespace(), newChart.Spec.SourceRef.Name) @@ -178,7 +178,7 @@ func (r *HelmChartTemplate) Reconcile(ctx context.Context, req *Request) error { // reconcileDelete handles the garbage collection of the current HelmChart in // the Status object of the given HelmRelease. -func (r *HelmChartTemplate) reconcileDelete(ctx context.Context, obj *v2.HelmRelease) error { +func (r *HelmChartTemplate) reconcileDelete(ctx context.Context, obj *v2.HelmRelease, src sourcev1.Source) error { if !obj.Spec.Suspend && obj.Status.HelmChart != "" { ns, name := obj.Status.GetHelmChart() namespacedName := types.NamespacedName{Namespace: ns, Name: name} @@ -202,7 +202,7 @@ func (r *HelmChartTemplate) reconcileDelete(ctx context.Context, obj *v2.HelmRel err = fmt.Errorf("failed to delete HelmChart '%s': %w", obj.Status.HelmChart, err) return err } - r.eventRecorder.Eventf(obj, eventv1.EventTypeTrace, "HelmChartDeleted", "deleted HelmChart '%s'", obj.Status.HelmChart) + r.eventRecorder.Eventf(obj, src, eventv1.EventTypeTrace, "HelmChartDeleted", eventv1.ActionDeleted, "deleted HelmChart '%s'", obj.Status.HelmChart) } // Truncate the chart reference in the status object. diff --git a/internal/reconcile/helmchart_template_test.go b/internal/reconcile/helmchart_template_test.go index d7ce5fa17..ead7a1444 100644 --- a/internal/reconcile/helmchart_template_test.go +++ b/internal/reconcile/helmchart_template_test.go @@ -27,14 +27,14 @@ import ( apierrors "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" "sigs.k8s.io/controller-runtime/pkg/client/fake" "github.com/fluxcd/pkg/apis/meta" - sourcev1 "github.com/fluxcd/source-controller/api/v1" + "github.com/fluxcd/pkg/runtime/events" v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/acl" + sourcev1 "github.com/fluxcd/source-controller/api/v1" ) func TestHelmChartTemplate_Reconcile(t *testing.T) { @@ -77,10 +77,9 @@ func TestHelmChartTemplate_Reconcile(t *testing.T) { g.Expect(testEnv.Cleanup(context.Background(), &existingChart)).To(Succeed()) }) - recorder := record.NewFakeRecorder(32) r := &HelmChartTemplate{ client: testEnv, - eventRecorder: recorder, + eventRecorder: events.NewFakeRecorder(32, false), fieldManager: testFieldManager, } @@ -133,7 +132,7 @@ func TestHelmChartTemplate_Reconcile(t *testing.T) { r := &HelmChartTemplate{ client: testEnv, - eventRecorder: record.NewFakeRecorder(32), + eventRecorder: events.NewFakeRecorder(32, false), fieldManager: testFieldManager, } @@ -178,10 +177,9 @@ func TestHelmChartTemplate_Reconcile(t *testing.T) { t.Run("HelmChart NotFound creates HelmChart", func(t *testing.T) { g := NewWithT(t) - recorder := record.NewFakeRecorder(32) r := &HelmChartTemplate{ client: testEnv, - eventRecorder: recorder, + eventRecorder: events.NewFakeRecorder(32, false), fieldManager: testFieldManager, } @@ -250,10 +248,9 @@ func TestHelmChartTemplate_Reconcile(t *testing.T) { g.Expect(testEnv.Cleanup(context.Background(), &existingChart)).To(Succeed()) }) - recorder := record.NewFakeRecorder(32) r := &HelmChartTemplate{ client: testEnv, - eventRecorder: recorder, + eventRecorder: events.NewFakeRecorder(32, false), fieldManager: testFieldManager, } @@ -321,10 +318,9 @@ func TestHelmChartTemplate_Reconcile(t *testing.T) { g.Expect(testEnv.Cleanup(context.Background(), existingChart)).To(Succeed()) }) - recorder := record.NewFakeRecorder(32) r := &HelmChartTemplate{ client: testEnv, - eventRecorder: recorder, + eventRecorder: events.NewFakeRecorder(32, false), fieldManager: testFieldManager, } @@ -364,10 +360,9 @@ func TestHelmChartTemplate_Reconcile(t *testing.T) { t.Run("sets owner labels on HelmChart", func(t *testing.T) { g := NewWithT(t) - recorder := record.NewFakeRecorder(32) r := &HelmChartTemplate{ client: testEnv, - eventRecorder: recorder, + eventRecorder: events.NewFakeRecorder(32, false), fieldManager: testFieldManager, } @@ -469,10 +464,9 @@ func TestHelmChartTemplate_Reconcile(t *testing.T) { g.Expect(testEnv.Cleanup(context.Background(), &existingChart)).To(Succeed()) }) - recorder := record.NewFakeRecorder(32) r := &HelmChartTemplate{ client: testEnv, - eventRecorder: recorder, + eventRecorder: events.NewFakeRecorder(32, false), fieldManager: testFieldManager, } @@ -513,10 +507,9 @@ func TestHelmChartTemplate_reconcileDelete(t *testing.T) { }, }) - recorder := record.NewFakeRecorder(32) r := &HelmChartTemplate{ client: builder.Build(), - eventRecorder: recorder, + eventRecorder: events.NewFakeRecorder(32, false), } obj := &v2.HelmRelease{ @@ -528,7 +521,13 @@ func TestHelmChartTemplate_reconcileDelete(t *testing.T) { HelmChart: "default/chart", }, } - err := r.reconcileDelete(context.TODO(), obj) + srcObj := &sourcev1.HelmChart{ + ObjectMeta: metav1.ObjectMeta{ + Name: "chart", + Namespace: "default", + }, + } + err := r.reconcileDelete(context.TODO(), obj, srcObj) g.Expect(err).ToNot(HaveOccurred()) g.Expect(obj.Status.HelmChart).To(BeEmpty()) @@ -552,7 +551,13 @@ func TestHelmChartTemplate_reconcileDelete(t *testing.T) { HelmChart: "default/chart", }, } - err := r.reconcileDelete(context.TODO(), obj) + srcObj := &sourcev1.HelmChart{ + ObjectMeta: metav1.ObjectMeta{ + Name: "chart", + Namespace: "default", + }, + } + err := r.reconcileDelete(context.TODO(), obj, srcObj) g.Expect(err).ToNot(HaveOccurred()) g.Expect(obj.Status.HelmChart).To(BeEmpty()) }) @@ -569,10 +574,9 @@ func TestHelmChartTemplate_reconcileDelete(t *testing.T) { }, }) - recorder := record.NewFakeRecorder(32) r := &HelmChartTemplate{ client: builder.Build(), - eventRecorder: recorder, + eventRecorder: events.NewFakeRecorder(32, false), } obj := &v2.HelmRelease{ @@ -588,7 +592,13 @@ func TestHelmChartTemplate_reconcileDelete(t *testing.T) { HelmChart: "default/chart", }, } - err := r.reconcileDelete(context.TODO(), obj) + srcObj := &sourcev1.HelmChart{ + ObjectMeta: metav1.ObjectMeta{ + Name: "chart", + Namespace: "default", + }, + } + err := r.reconcileDelete(context.TODO(), obj, srcObj) g.Expect(err).ToNot(HaveOccurred()) g.Expect(obj.Status.HelmChart).ToNot(BeEmpty()) @@ -623,12 +633,18 @@ func TestHelmChartTemplate_reconcileDelete(t *testing.T) { HelmChart: "other/chart", }, } + srcObj := &sourcev1.HelmChart{ + ObjectMeta: metav1.ObjectMeta{ + Name: "chart", + Namespace: "default", + }, + } currentAllow := acl.AllowCrossNamespaceRef acl.AllowCrossNamespaceRef = false t.Cleanup(func() { acl.AllowCrossNamespaceRef = currentAllow }) - err := r.reconcileDelete(context.TODO(), obj) + err := r.reconcileDelete(context.TODO(), obj, srcObj) g.Expect(err).To(HaveOccurred()) g.Expect(obj.Status.HelmChart).ToNot(BeEmpty()) @@ -647,7 +663,10 @@ func TestHelmChartTemplate_reconcileDelete(t *testing.T) { obj := &v2.HelmRelease{ Status: v2.HelmReleaseStatus{}, } - err := r.reconcileDelete(context.TODO(), obj) + srcObj := &sourcev1.HelmChart{ + Status: sourcev1.HelmChartStatus{}, + } + err := r.reconcileDelete(context.TODO(), obj, srcObj) g.Expect(err).ToNot(HaveOccurred()) }) } diff --git a/internal/reconcile/install.go b/internal/reconcile/install.go index 58428604b..b1f6fe564 100644 --- a/internal/reconcile/install.go +++ b/internal/reconcile/install.go @@ -24,10 +24,10 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/tools/record" "github.com/fluxcd/pkg/chartutil" "github.com/fluxcd/pkg/runtime/conditions" + "github.com/fluxcd/pkg/runtime/events" v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/action" @@ -58,13 +58,13 @@ import ( // e.g. action.VerifySnapshot before calling Reconcile. type Install struct { configFactory *action.ConfigFactory - eventRecorder record.EventRecorder + eventRecorder events.EventRecorder defaultToRetryOnFailure bool } // NewInstall returns a new Install reconciler configured with the provided // values. -func NewInstall(cfg *action.ConfigFactory, recorder record.EventRecorder, defaultToRetryOnFailure bool) *Install { +func NewInstall(cfg *action.ConfigFactory, recorder events.EventRecorder, defaultToRetryOnFailure bool) *Install { return &Install{configFactory: cfg, eventRecorder: recorder, defaultToRetryOnFailure: defaultToRetryOnFailure} } @@ -72,7 +72,7 @@ func (r *Install) Reconcile(ctx context.Context, req *Request) error { var ( logBuf = action.NewDebugLogBuffer(ctx) obsReleases = make(observedReleases) - cfg = r.configFactory.Build(logBuf, observeRelease(obsReleases), observeInventory(req.Object, req.Chart, r.configFactory.Getter, r.eventRecorder)) + cfg = r.configFactory.Build(logBuf, observeRelease(obsReleases), observeInventory(req.Object, req.Source, req.Chart, r.configFactory.Getter, r.eventRecorder)) startTime = time.Now() ) @@ -161,10 +161,12 @@ func (r *Install) failure(req *Request, buffer *action.LogBuffer, err error) { // Condition summary. r.eventRecorder.AnnotatedEventf( req.Object, + req.Source, eventMeta(req.Chart.Metadata.Version, chartutil.DigestValues(digest.Canonical, req.Values).String(), addAppVersion(req.Chart.AppVersion()), addOCIDigest(req.Object.Status.LastAttemptedRevisionDigest)), corev1.EventTypeWarning, v2.InstallFailedReason, + string(v2.ReleaseActionInstall), "%s", eventMessageWithLog(msg, buffer), ) @@ -196,9 +198,11 @@ func (r *Install) success(req *Request) { // Record event. r.eventRecorder.AnnotatedEventf( req.Object, + req.Source, eventMeta(cur.ChartVersion, cur.ConfigDigest, addAppVersion(cur.AppVersion), addOCIDigest(cur.OCIDigest)), corev1.EventTypeNormal, v2.InstallSucceededReason, + string(v2.ReleaseActionInstall), "%s", msg, ) diff --git a/internal/reconcile/install_test.go b/internal/reconcile/install_test.go index 1150d3297..aaaa93815 100644 --- a/internal/reconcile/install_test.go +++ b/internal/reconcile/install_test.go @@ -32,15 +32,16 @@ import ( helmstorage "helm.sh/helm/v4/pkg/storage" helmdriver "helm.sh/helm/v4/pkg/storage/driver" corev1 "k8s.io/api/core/v1" + eventsv1 "k8s.io/api/events/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/tools/record" "sigs.k8s.io/controller-runtime/pkg/client" - 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/chartutil" "github.com/fluxcd/pkg/runtime/conditions" + "github.com/fluxcd/pkg/runtime/events" v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/action" @@ -48,6 +49,7 @@ import ( "github.com/fluxcd/helm-controller/internal/release" "github.com/fluxcd/helm-controller/internal/storage" "github.com/fluxcd/helm-controller/internal/testutil" + sourcev1 "github.com/fluxcd/source-controller/api/v1" ) func TestInstall_Reconcile(t *testing.T) { @@ -345,7 +347,7 @@ func TestInstall_Reconcile(t *testing.T) { cfg.Driver = tt.driver(cfg.Driver) } - recorder := new(record.FakeRecorder) + recorder := new(events.FakeRecorder) got := (NewInstall(cfg, recorder, false)).Reconcile(context.TODO(), &Request{ Object: obj, Chart: tt.chart, @@ -467,7 +469,7 @@ func TestInstall_Reconcile_withSubchartWithCRDs(t *testing.T) { store := helmstorage.Init(cfg.Driver) chart := testutil.BuildChartWithSubchartWithCRD() - recorder := new(record.FakeRecorder) + recorder := new(events.FakeRecorder) got := (NewInstall(cfg, recorder, false)).Reconcile(context.TODO(), &Request{ Object: obj, Chart: chart, @@ -528,6 +530,14 @@ func TestInstall_Reconcile_withSubchartWithCRDs(t *testing.T) { func TestInstall_failure(t *testing.T) { var ( obj = &v2.HelmRelease{ + TypeMeta: metav1.TypeMeta{ + Kind: v2.HelmReleaseKind, + APIVersion: v2.GroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: mockReleaseName, + Namespace: mockReleaseNamespace, + }, Spec: v2.HelmReleaseSpec{ ReleaseName: mockReleaseName, TargetNamespace: mockReleaseNamespace, @@ -536,6 +546,16 @@ func TestInstall_failure(t *testing.T) { LastAttemptedRevisionDigest: "sha256:1234567890", }, } + source = &sourcev1.GitRepository{ + TypeMeta: metav1.TypeMeta{ + Kind: sourcev1.GitRepositoryKind, + APIVersion: sourcev1.GroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-source", + Namespace: mockReleaseNamespace, + }, + } chrt = testutil.BuildChart() err = errors.New("installation error") ) @@ -543,12 +563,17 @@ func TestInstall_failure(t *testing.T) { t.Run("records failure", func(t *testing.T) { g := NewWithT(t) - recorder := testutil.NewFakeRecorder(10, false) + recorder := events.NewFakeRecorder(10, true) r := &Install{ eventRecorder: recorder, } - req := &Request{Object: obj.DeepCopy(), Chart: chrt, Values: map[string]any{"foo": "bar"}} + req := &Request{ + Object: obj.DeepCopy(), + Source: source, + Chart: chrt, + Values: map[string]any{"foo": "bar"}, + } r.failure(req, nil, err) expectMsg := fmt.Sprintf(fmtInstallFailure, mockReleaseNamespace, mockReleaseName, chrt.Name(), @@ -558,11 +583,24 @@ func TestInstall_failure(t *testing.T) { *conditions.FalseCondition(v2.ReleasedCondition, v2.InstallFailedReason, "%s", expectMsg), })) g.Expect(req.Object.Status.Failures).To(Equal(int64(1))) - g.Expect(recorder.GetEvents()).To(ConsistOf([]corev1.Event{ + g.Expect(recorder.GetEvents()).To(ConsistOf([]eventsv1.Event{ { - Type: corev1.EventTypeWarning, - Reason: v2.InstallFailedReason, - Message: expectMsg, + Type: corev1.EventTypeWarning, + Reason: v2.InstallFailedReason, + Note: expectMsg, + Action: string(v2.ReleaseActionInstall), + Regarding: corev1.ObjectReference{ + Kind: v2.HelmReleaseKind, + APIVersion: v2.GroupVersion.String(), + Name: mockReleaseName, + Namespace: mockReleaseNamespace, + }, + Related: &corev1.ObjectReference{ + Kind: sourcev1.GitRepositoryKind, + APIVersion: sourcev1.GroupVersion.String(), + Name: "test-source", + Namespace: mockReleaseNamespace, + }, ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ eventMetaGroupKey(metaOCIDigestKey): obj.Status.LastAttemptedRevisionDigest, @@ -578,7 +616,7 @@ func TestInstall_failure(t *testing.T) { t.Run("records failure with logs", func(t *testing.T) { g := NewWithT(t) - recorder := testutil.NewFakeRecorder(10, false) + recorder := events.NewFakeRecorder(10, false) r := &Install{ eventRecorder: recorder, } @@ -591,7 +629,7 @@ func TestInstall_failure(t *testing.T) { events := recorder.GetEvents() g.Expect(events).To(HaveLen(1)) - g.Expect(events[0].Message).To(ContainSubstring(expectSubStr)) + g.Expect(events[0].Note).To(ContainSubstring(expectSubStr)) }) } @@ -603,24 +641,43 @@ func TestInstall_success(t *testing.T) { Chart: testutil.BuildChart(), }) obj = &v2.HelmRelease{ + TypeMeta: metav1.TypeMeta{ + Kind: v2.HelmReleaseKind, + APIVersion: v2.GroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: mockReleaseName, + Namespace: mockReleaseNamespace, + }, Status: v2.HelmReleaseStatus{ History: v2.Snapshots{ release.ObservedToSnapshot(release.ObserveRelease(cur)), }, }, } + source = &sourcev1.GitRepository{ + TypeMeta: metav1.TypeMeta{ + Kind: sourcev1.GitRepositoryKind, + APIVersion: sourcev1.GroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-source", + Namespace: mockReleaseNamespace, + }, + } ) t.Run("records success", func(t *testing.T) { g := NewWithT(t) - recorder := testutil.NewFakeRecorder(10, false) + recorder := events.NewFakeRecorder(10, true) r := &Install{ eventRecorder: recorder, } req := &Request{ Object: obj.DeepCopy(), + Source: source, } r.success(req) @@ -631,11 +688,24 @@ func TestInstall_success(t *testing.T) { g.Expect(req.Object.Status.Conditions).To(conditions.MatchConditions([]metav1.Condition{ *conditions.TrueCondition(v2.ReleasedCondition, v2.InstallSucceededReason, "%s", expectMsg), })) - g.Expect(recorder.GetEvents()).To(ConsistOf([]corev1.Event{ + g.Expect(recorder.GetEvents()).To(ConsistOf([]eventsv1.Event{ { - Type: corev1.EventTypeNormal, - Reason: v2.InstallSucceededReason, - Message: expectMsg, + Type: corev1.EventTypeNormal, + Reason: v2.InstallSucceededReason, + Note: expectMsg, + Action: string(v2.ReleaseActionInstall), + Regarding: corev1.ObjectReference{ + Kind: v2.HelmReleaseKind, + APIVersion: v2.GroupVersion.String(), + Name: mockReleaseName, + Namespace: mockReleaseNamespace, + }, + Related: &corev1.ObjectReference{ + Kind: sourcev1.GitRepositoryKind, + APIVersion: sourcev1.GroupVersion.String(), + Name: "test-source", + Namespace: mockReleaseNamespace, + }, ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ eventMetaGroupKey(eventv1.MetaRevisionKey): obj.Status.History.Latest().ChartVersion, @@ -650,7 +720,7 @@ func TestInstall_success(t *testing.T) { t.Run("clears failures if retry strategy is configured", func(t *testing.T) { g := NewWithT(t) - recorder := testutil.NewFakeRecorder(10, false) + recorder := events.NewFakeRecorder(10, false) r := &Install{ eventRecorder: recorder, } @@ -676,7 +746,7 @@ func TestInstall_success(t *testing.T) { t.Run("records success with TestSuccess=False", func(t *testing.T) { g := NewWithT(t) - recorder := testutil.NewFakeRecorder(10, false) + recorder := events.NewFakeRecorder(10, false) r := &Install{ eventRecorder: recorder, } diff --git a/internal/reconcile/reconcile.go b/internal/reconcile/reconcile.go index de98349a9..d9e56d076 100644 --- a/internal/reconcile/reconcile.go +++ b/internal/reconcile/reconcile.go @@ -23,6 +23,7 @@ import ( helmchart "helm.sh/helm/v4/pkg/chart/v2" v2 "github.com/fluxcd/helm-controller/api/v2" + v1 "github.com/fluxcd/source-controller/api/v1" ) const ( @@ -78,6 +79,8 @@ type Request struct { // Object is the Helm release to be reconciled, and describes the desired // state to the ActionReconciler. Object *v2.HelmRelease + // Source is the source object containing the HelmChart + Source v1.Source // Chart is the Helm chart to be installed or upgraded. Chart *helmchart.Chart // Values is the Helm chart values to be used for the installation or diff --git a/internal/reconcile/release.go b/internal/reconcile/release.go index 3bedcb54c..57d39b3a3 100644 --- a/internal/reconcile/release.go +++ b/internal/reconcile/release.go @@ -24,12 +24,13 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/cli-runtime/pkg/genericclioptions" - "k8s.io/client-go/tools/record" "sigs.k8s.io/controller-runtime/pkg/client" - 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/runtime/conditions" + "github.com/fluxcd/pkg/runtime/events" + sourcev1 "github.com/fluxcd/source-controller/api/v1" helmchart "helm.sh/helm/v4/pkg/chart/v2" helmrelease "helm.sh/helm/v4/pkg/release" helmreleasev1 "helm.sh/helm/v4/pkg/release/v1" @@ -149,7 +150,7 @@ func observeRelease(observed observedReleases) storage.ObserveFunc { // observeInventory returns a storage.ObserveFunc that builds an inventory // from the release manifest and chart CRDs, and stores it in the HelmRelease // status. -func observeInventory(obj *v2.HelmRelease, chart *helmchart.Chart, getter genericclioptions.RESTClientGetter, recorder record.EventRecorder) storage.ObserveFunc { +func observeInventory(obj *v2.HelmRelease, src sourcev1.Source, chart *helmchart.Chart, getter genericclioptions.RESTClientGetter, recorder events.EventRecorder) storage.ObserveFunc { return func(rls helmrelease.Releaser) { rlsTyped, ok := rls.(*helmreleasev1.Release) if !ok { @@ -158,13 +159,13 @@ func observeInventory(obj *v2.HelmRelease, chart *helmchart.Chart, getter generi restCfg, err := getter.ToRESTConfig() if err != nil { - recorder.Eventf(obj, corev1.EventTypeWarning, v2.InventoryBuildFailedReason, + recorder.Eventf(obj, src, corev1.EventTypeWarning, v2.InventoryBuildFailedReason, eventv1.ActionFailed, "failed to build inventory for %s/%s: %s", obj.GetNamespace(), obj.GetName(), err.Error()) return } c, err := client.New(restCfg, client.Options{}) if err != nil { - recorder.Eventf(obj, corev1.EventTypeWarning, v2.InventoryBuildFailedReason, + recorder.Eventf(obj, src, corev1.EventTypeWarning, v2.InventoryBuildFailedReason, eventv1.ActionFailed, "failed to build inventory for %s/%s: %s", obj.GetNamespace(), obj.GetName(), err.Error()) return } @@ -172,17 +173,17 @@ func observeInventory(obj *v2.HelmRelease, chart *helmchart.Chart, getter generi inv := inventory.New() warnings, err := inventory.AddManifest(inv, rlsTyped.Manifest, rlsTyped.Namespace, c) if err != nil { - recorder.Eventf(obj, corev1.EventTypeWarning, v2.InventoryBuildFailedReason, + recorder.Eventf(obj, src, corev1.EventTypeWarning, v2.InventoryBuildFailedReason, eventv1.ActionFailed, "failed to build inventory for %s/%s: %s", obj.GetNamespace(), obj.GetName(), err.Error()) return } if len(warnings) > 0 { - recorder.Eventf(obj, corev1.EventTypeWarning, v2.NamespaceCheckSkippedReason, + recorder.Eventf(obj, src, corev1.EventTypeWarning, v2.NamespaceCheckSkippedReason, eventv1.ActionWaiting, "%s", strings.Join(warnings, "; ")) } if chart != nil { if err := inventory.AddCRDs(inv, chart); err != nil { - recorder.Eventf(obj, corev1.EventTypeWarning, v2.InventoryBuildFailedReason, + recorder.Eventf(obj, src, corev1.EventTypeWarning, v2.InventoryBuildFailedReason, eventv1.ActionFailed, "failed to build inventory for %s/%s: %s", obj.GetNamespace(), obj.GetName(), err.Error()) return } diff --git a/internal/reconcile/rollback_remediation.go b/internal/reconcile/rollback_remediation.go index b855d9777..18818df78 100644 --- a/internal/reconcile/rollback_remediation.go +++ b/internal/reconcile/rollback_remediation.go @@ -24,9 +24,9 @@ import ( helmrelease "helm.sh/helm/v4/pkg/release" helmreleasev1 "helm.sh/helm/v4/pkg/release/v1" corev1 "k8s.io/api/core/v1" - "k8s.io/client-go/tools/record" "github.com/fluxcd/pkg/runtime/conditions" + "github.com/fluxcd/pkg/runtime/events" v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/action" @@ -60,12 +60,12 @@ import ( // e.g. action.VerifySnapshot before calling Reconcile. type RollbackRemediation struct { configFactory *action.ConfigFactory - eventRecorder record.EventRecorder + eventRecorder events.EventRecorder } // NewRollbackRemediation returns a new RollbackRemediation reconciler // configured with the provided values. -func NewRollbackRemediation(configFactory *action.ConfigFactory, eventRecorder record.EventRecorder) *RollbackRemediation { +func NewRollbackRemediation(configFactory *action.ConfigFactory, eventRecorder events.EventRecorder) *RollbackRemediation { return &RollbackRemediation{ configFactory: configFactory, eventRecorder: eventRecorder, @@ -76,7 +76,7 @@ func (r *RollbackRemediation) Reconcile(ctx context.Context, req *Request) error var ( cur = req.Object.Status.History.Latest().DeepCopy() logBuf = action.NewDebugLogBuffer(ctx) - cfg = r.configFactory.Build(logBuf, observeRollback(req.Object), observeInventory(req.Object, req.Chart, r.configFactory.Getter, r.eventRecorder)) + cfg = r.configFactory.Build(logBuf, observeRollback(req.Object), observeInventory(req.Object, req.Source, req.Chart, r.configFactory.Getter, r.eventRecorder)) ) defer summarize(req) @@ -142,10 +142,12 @@ func (r *RollbackRemediation) failure(req *Request, prev *v2.Snapshot, buffer *a // Condition summary. r.eventRecorder.AnnotatedEventf( req.Object, + req.Source, eventMeta(prev.ChartVersion, chartutil.DigestValues(digest.Canonical, req.Values).String(), addAppVersion(prev.AppVersion), addOCIDigest(prev.OCIDigest)), corev1.EventTypeWarning, v2.RollbackFailedReason, + string(v2.ReleaseActionRollback), "%s", eventMessageWithLog(msg, buffer), ) @@ -163,10 +165,12 @@ func (r *RollbackRemediation) success(req *Request, prev *v2.Snapshot) { // Record event. r.eventRecorder.AnnotatedEventf( req.Object, + req.Source, eventMeta(prev.ChartVersion, chartutil.DigestValues(digest.Canonical, req.Values).String(), addAppVersion(prev.AppVersion), addOCIDigest(prev.OCIDigest)), corev1.EventTypeNormal, v2.RollbackSucceededReason, + string(v2.ReleaseActionRollback), "%s", msg, ) @@ -197,6 +201,7 @@ func observeRollback(obj *v2.HelmRelease) storage.ObserveFunc { } obs := release.ObserveRelease(rls) + obs.Action = v2.ReleaseActionRollback obj.Status.History = append(v2.Snapshots{release.ObservedToSnapshot(obs)}, obj.Status.History...) } } diff --git a/internal/reconcile/rollback_remediation_test.go b/internal/reconcile/rollback_remediation_test.go index 61ad66423..40be0777f 100644 --- a/internal/reconcile/rollback_remediation_test.go +++ b/internal/reconcile/rollback_remediation_test.go @@ -31,12 +31,13 @@ import ( helmstorage "helm.sh/helm/v4/pkg/storage" helmdriver "helm.sh/helm/v4/pkg/storage/driver" corev1 "k8s.io/api/core/v1" + eventsv1 "k8s.io/api/events/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/tools/record" - 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/runtime/conditions" + "github.com/fluxcd/pkg/runtime/events" "github.com/fluxcd/pkg/chartutil" @@ -292,8 +293,10 @@ func TestRollbackRemediation_Reconcile(t *testing.T) { "storage update error"), }, expectHistory: func(releases []*helmrelease.Release) v2.Snapshots { + rollbackSnap := release.ObservedToSnapshot(release.ObserveRelease(releases[2])) + rollbackSnap.Action = v2.ReleaseActionRollback return v2.Snapshots{ - release.ObservedToSnapshot(release.ObserveRelease(releases[2])), + rollbackSnap, release.ObservedToSnapshot(release.ObserveRelease(releases[1])), release.ObservedToSnapshot(release.ObserveRelease(releases[0])), } @@ -394,7 +397,7 @@ func TestRollbackRemediation_Reconcile(t *testing.T) { cfg.Driver = tt.driver(cfg.Driver) } - recorder := new(record.FakeRecorder) + recorder := new(events.FakeRecorder) got := (NewRollbackRemediation(cfg, recorder)).Reconcile(context.TODO(), &Request{ Object: obj, }) @@ -446,7 +449,7 @@ func TestRollbackRemediation_failure(t *testing.T) { t.Run("records failure", func(t *testing.T) { g := NewWithT(t) - recorder := testutil.NewFakeRecorder(10, false) + recorder := events.NewFakeRecorder(10, false) r := &RollbackRemediation{ eventRecorder: recorder, } @@ -462,11 +465,12 @@ func TestRollbackRemediation_failure(t *testing.T) { *conditions.FalseCondition(v2.RemediatedCondition, v2.RollbackFailedReason, "%s", expectMsg), })) g.Expect(req.Object.Status.Failures).To(Equal(int64(1))) - g.Expect(recorder.GetEvents()).To(ConsistOf([]corev1.Event{ + g.Expect(recorder.GetEvents()).To(ConsistOf([]eventsv1.Event{ { - Type: corev1.EventTypeWarning, - Reason: v2.RollbackFailedReason, - Message: expectMsg, + Type: corev1.EventTypeWarning, + Reason: v2.RollbackFailedReason, + Note: expectMsg, + Action: string(v2.ReleaseActionRollback), ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ eventMetaGroupKey(eventv1.MetaRevisionKey): prev.Chart.Metadata.Version, @@ -481,7 +485,7 @@ func TestRollbackRemediation_failure(t *testing.T) { t.Run("records failure with logs", func(t *testing.T) { g := NewWithT(t) - recorder := testutil.NewFakeRecorder(10, false) + recorder := events.NewFakeRecorder(10, false) r := &RollbackRemediation{ eventRecorder: recorder, } @@ -494,7 +498,7 @@ func TestRollbackRemediation_failure(t *testing.T) { events := recorder.GetEvents() g.Expect(events).To(HaveLen(1)) - g.Expect(events[0].Message).To(ContainSubstring(expectSubStr)) + g.Expect(events[0].Note).To(ContainSubstring(expectSubStr)) }) } @@ -507,7 +511,7 @@ func TestRollbackRemediation_success(t *testing.T) { Version: 4, }) - recorder := testutil.NewFakeRecorder(10, false) + recorder := events.NewFakeRecorder(10, false) r := &RollbackRemediation{ eventRecorder: recorder, } @@ -522,11 +526,12 @@ func TestRollbackRemediation_success(t *testing.T) { *conditions.TrueCondition(v2.RemediatedCondition, v2.RollbackSucceededReason, "%s", expectMsg), })) g.Expect(req.Object.Status.Failures).To(Equal(int64(0))) - g.Expect(recorder.GetEvents()).To(ConsistOf([]corev1.Event{ + g.Expect(recorder.GetEvents()).To(ConsistOf([]eventsv1.Event{ { - Type: corev1.EventTypeNormal, - Reason: v2.RollbackSucceededReason, - Message: expectMsg, + Type: corev1.EventTypeNormal, + Reason: v2.RollbackSucceededReason, + Action: string(v2.ReleaseActionRollback), + Note: expectMsg, ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ eventMetaGroupKey(eventv1.MetaRevisionKey): prev.Chart.Metadata.Version, @@ -551,6 +556,7 @@ func Test_observeRollback(t *testing.T) { }) observeRollback(obj)(rls) expect := release.ObservedToSnapshot(release.ObserveRelease(rls)) + expect.Action = v2.ReleaseActionRollback g.Expect(obj.Status.History).To(testutil.Equal(v2.Snapshots{ expect, @@ -580,6 +586,7 @@ func Test_observeRollback(t *testing.T) { Status: helmreleasecommon.StatusPendingRollback, }) expect := release.ObservedToSnapshot(release.ObserveRelease(rls)) + expect.Action = v2.ReleaseActionRollback observeRollback(obj)(rls) g.Expect(obj.Status.History).To(testutil.Equal(v2.Snapshots{ diff --git a/internal/reconcile/test.go b/internal/reconcile/test.go index a1c4b8599..c5160d66f 100644 --- a/internal/reconcile/test.go +++ b/internal/reconcile/test.go @@ -24,9 +24,10 @@ import ( helmrelease "helm.sh/helm/v4/pkg/release" helmreleasev1 "helm.sh/helm/v4/pkg/release/v1" corev1 "k8s.io/api/core/v1" - "k8s.io/client-go/tools/record" + eventv1 "github.com/fluxcd/pkg/apis/event/v1" "github.com/fluxcd/pkg/runtime/conditions" + "github.com/fluxcd/pkg/runtime/events" v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/action" @@ -61,11 +62,11 @@ import ( // e.g. action.VerifySnapshot before calling Reconcile. type Test struct { configFactory *action.ConfigFactory - eventRecorder record.EventRecorder + eventRecorder events.EventRecorder } // NewTest returns a new Test reconciler configured with the provided values. -func NewTest(cfg *action.ConfigFactory, recorder record.EventRecorder) *Test { +func NewTest(cfg *action.ConfigFactory, recorder events.EventRecorder) *Test { return &Test{configFactory: cfg, eventRecorder: recorder} } @@ -144,9 +145,11 @@ func (r *Test) failure(req *Request, err error) { // Condition summary. r.eventRecorder.AnnotatedEventf( req.Object, + req.Source, eventMeta(cur.ChartVersion, cur.ConfigDigest, addAppVersion(cur.AppVersion), addOCIDigest(cur.OCIDigest)), corev1.EventTypeWarning, v2.TestFailedReason, + eventv1.ActionFailed, "%s", msg, ) @@ -181,9 +184,11 @@ func (r *Test) success(req *Request) { // Record event. r.eventRecorder.AnnotatedEventf( req.Object, + req.Source, eventMeta(cur.ChartVersion, cur.ConfigDigest, addAppVersion(cur.AppVersion), addOCIDigest(cur.OCIDigest)), corev1.EventTypeNormal, v2.TestSucceededReason, + eventv1.ActionValidated, "%s", msg, ) diff --git a/internal/reconcile/test_test.go b/internal/reconcile/test_test.go index 6267d4a78..23f08973f 100644 --- a/internal/reconcile/test_test.go +++ b/internal/reconcile/test_test.go @@ -30,12 +30,13 @@ import ( helmstorage "helm.sh/helm/v4/pkg/storage" helmdriver "helm.sh/helm/v4/pkg/storage/driver" corev1 "k8s.io/api/core/v1" + eventsv1 "k8s.io/api/events/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/tools/record" - 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/runtime/conditions" + "github.com/fluxcd/pkg/runtime/events" "github.com/fluxcd/pkg/chartutil" @@ -320,7 +321,7 @@ func TestTest_Reconcile(t *testing.T) { cfg.Driver = tt.driver(cfg.Driver) } - recorder := new(record.FakeRecorder) + recorder := new(events.FakeRecorder) got := (NewTest(cfg, recorder)).Reconcile(context.TODO(), &Request{ Object: obj, }) @@ -506,7 +507,7 @@ func TestTest_failure(t *testing.T) { t.Run("records failure", func(t *testing.T) { g := NewWithT(t) - recorder := testutil.NewFakeRecorder(10, false) + recorder := events.NewFakeRecorder(10, false) r := &Test{ eventRecorder: recorder, } @@ -525,11 +526,12 @@ func TestTest_failure(t *testing.T) { g.Expect(req.Object.Status.Failures).To(Equal(int64(1))) g.Expect(req.Object.Status.InstallFailures).To(BeZero()) g.Expect(req.Object.Status.UpgradeFailures).To(BeZero()) - g.Expect(recorder.GetEvents()).To(ConsistOf([]corev1.Event{ + g.Expect(recorder.GetEvents()).To(ConsistOf([]eventsv1.Event{ { - Type: corev1.EventTypeWarning, - Reason: v2.TestFailedReason, - Message: expectMsg, + Type: corev1.EventTypeWarning, + Reason: v2.TestFailedReason, + Action: eventv1.ActionFailed, + Note: expectMsg, ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ eventMetaGroupKey(eventv1.MetaRevisionKey): cur.Chart.Metadata.Version, @@ -544,7 +546,7 @@ func TestTest_failure(t *testing.T) { t.Run("increases remediation failure count", func(t *testing.T) { g := NewWithT(t) - recorder := testutil.NewFakeRecorder(10, false) + recorder := events.NewFakeRecorder(10, false) r := &Test{ eventRecorder: recorder, } @@ -561,7 +563,7 @@ func TestTest_failure(t *testing.T) { t.Run("follows ignore failure instructions", func(t *testing.T) { g := NewWithT(t) - recorder := testutil.NewFakeRecorder(10, false) + recorder := events.NewFakeRecorder(10, false) r := &Test{ eventRecorder: recorder, } @@ -596,7 +598,7 @@ func TestTest_success(t *testing.T) { ) t.Run("records success", func(t *testing.T) { - recorder := testutil.NewFakeRecorder(10, false) + recorder := events.NewFakeRecorder(10, false) r := &Test{ eventRecorder: recorder, } @@ -622,11 +624,12 @@ func TestTest_success(t *testing.T) { *conditions.TrueCondition(v2.TestSuccessCondition, v2.TestSucceededReason, "%s", expectMsg), })) g.Expect(req.Object.Status.Failures).To(Equal(int64(0))) - g.Expect(recorder.GetEvents()).To(ConsistOf([]corev1.Event{ + g.Expect(recorder.GetEvents()).To(ConsistOf([]eventsv1.Event{ { - Type: corev1.EventTypeNormal, - Reason: v2.TestSucceededReason, - Message: expectMsg, + Type: corev1.EventTypeNormal, + Reason: v2.TestSucceededReason, + Action: eventv1.ActionValidated, + Note: expectMsg, ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ eventMetaGroupKey(eventv1.MetaRevisionKey): cur.Chart.Metadata.Version, @@ -640,7 +643,7 @@ func TestTest_success(t *testing.T) { t.Run("records success without hooks", func(t *testing.T) { r := &Test{ - eventRecorder: new(testutil.FakeRecorder), + eventRecorder: new(events.FakeRecorder), } obj := obj.DeepCopy() diff --git a/internal/reconcile/uninstall.go b/internal/reconcile/uninstall.go index 3cd546c42..866e47420 100644 --- a/internal/reconcile/uninstall.go +++ b/internal/reconcile/uninstall.go @@ -27,9 +27,9 @@ import ( helmreleasev1 "helm.sh/helm/v4/pkg/release/v1" helmdriver "helm.sh/helm/v4/pkg/storage/driver" corev1 "k8s.io/api/core/v1" - "k8s.io/client-go/tools/record" "github.com/fluxcd/pkg/runtime/conditions" + "github.com/fluxcd/pkg/runtime/events" v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/action" @@ -69,12 +69,12 @@ import ( // e.g. action.VerifySnapshot before calling Reconcile. type Uninstall struct { configFactory *action.ConfigFactory - eventRecorder record.EventRecorder + eventRecorder events.EventRecorder } // NewUninstall returns a new Uninstall reconciler configured with the provided // values. -func NewUninstall(cfg *action.ConfigFactory, recorder record.EventRecorder) *Uninstall { +func NewUninstall(cfg *action.ConfigFactory, recorder events.EventRecorder) *Uninstall { return &Uninstall{configFactory: cfg, eventRecorder: recorder} } @@ -183,8 +183,10 @@ func (r *Uninstall) failure(req *Request, buffer *action.LogBuffer, err error) { // Condition summary. r.eventRecorder.AnnotatedEventf( req.Object, + req.Source, eventMeta(cur.ChartVersion, cur.ConfigDigest, addAppVersion(cur.AppVersion), addOCIDigest(cur.OCIDigest)), corev1.EventTypeWarning, v2.UninstallFailedReason, + string(v2.ReleaseActionUninstall), "%s", eventMessageWithLog(msg, buffer), ) @@ -208,9 +210,11 @@ func (r *Uninstall) success(req *Request) { // Condition summary. r.eventRecorder.AnnotatedEventf( req.Object, + req.Source, eventMeta(cur.ChartVersion, cur.ConfigDigest, addAppVersion(cur.AppVersion), addOCIDigest(cur.OCIDigest)), corev1.EventTypeNormal, v2.UninstallSucceededReason, + string(v2.ReleaseActionUninstall), "%s", msg, ) diff --git a/internal/reconcile/uninstall_remediation.go b/internal/reconcile/uninstall_remediation.go index 843a65d11..3c10ce352 100644 --- a/internal/reconcile/uninstall_remediation.go +++ b/internal/reconcile/uninstall_remediation.go @@ -24,9 +24,9 @@ import ( helmreleasev1 "helm.sh/helm/v4/pkg/release/v1" corev1 "k8s.io/api/core/v1" - "k8s.io/client-go/tools/record" "github.com/fluxcd/pkg/runtime/conditions" + "github.com/fluxcd/pkg/runtime/events" v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/action" @@ -68,12 +68,12 @@ var ( // e.g. action.VerifySnapshot before calling Reconcile. type UninstallRemediation struct { configFactory *action.ConfigFactory - eventRecorder record.EventRecorder + eventRecorder events.EventRecorder } // NewUninstallRemediation returns a new UninstallRemediation reconciler // configured with the provided values. -func NewUninstallRemediation(cfg *action.ConfigFactory, recorder record.EventRecorder) *UninstallRemediation { +func NewUninstallRemediation(cfg *action.ConfigFactory, recorder events.EventRecorder) *UninstallRemediation { return &UninstallRemediation{configFactory: cfg, eventRecorder: recorder} } @@ -159,9 +159,11 @@ func (r *UninstallRemediation) failure(req *Request, buffer *action.LogBuffer, e // Condition summary. r.eventRecorder.AnnotatedEventf( req.Object, + req.Source, eventMeta(cur.ChartVersion, cur.ConfigDigest, addAppVersion(cur.AppVersion), addOCIDigest(cur.OCIDigest)), corev1.EventTypeWarning, v2.UninstallFailedReason, + string(v2.ReleaseActionUninstallRemediation), "%s", eventMessageWithLog(msg, buffer), ) @@ -181,9 +183,11 @@ func (r *UninstallRemediation) success(req *Request) { // Record event. r.eventRecorder.AnnotatedEventf( req.Object, + req.Source, eventMeta(cur.ChartVersion, cur.ConfigDigest, addAppVersion(cur.AppVersion), addOCIDigest(cur.OCIDigest)), corev1.EventTypeNormal, v2.UninstallSucceededReason, + string(v2.ReleaseActionUninstallRemediation), "%s", msg, ) diff --git a/internal/reconcile/uninstall_remediation_test.go b/internal/reconcile/uninstall_remediation_test.go index 3179a1862..b1baf7f00 100644 --- a/internal/reconcile/uninstall_remediation_test.go +++ b/internal/reconcile/uninstall_remediation_test.go @@ -30,11 +30,12 @@ import ( helmstorage "helm.sh/helm/v4/pkg/storage" helmdriver "helm.sh/helm/v4/pkg/storage/driver" corev1 "k8s.io/api/core/v1" + eventsv1 "k8s.io/api/events/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/tools/record" - eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" + eventv1 "github.com/fluxcd/pkg/apis/event/v1" "github.com/fluxcd/pkg/runtime/conditions" + "github.com/fluxcd/pkg/runtime/events" "github.com/fluxcd/pkg/chartutil" @@ -390,7 +391,7 @@ func TestUninstallRemediation_Reconcile(t *testing.T) { cfg.Driver = tt.driver(cfg.Driver) } - recorder := new(record.FakeRecorder) + recorder := new(events.FakeRecorder) got := NewUninstallRemediation(cfg, recorder).Reconcile(context.TODO(), &Request{ Object: obj, }) @@ -442,7 +443,7 @@ func TestUninstallRemediation_failure(t *testing.T) { t.Run("records failure", func(t *testing.T) { g := NewWithT(t) - recorder := testutil.NewFakeRecorder(10, false) + recorder := events.NewFakeRecorder(10, false) r := &UninstallRemediation{ eventRecorder: recorder, } @@ -459,11 +460,12 @@ func TestUninstallRemediation_failure(t *testing.T) { *conditions.FalseCondition(v2.RemediatedCondition, v2.UninstallFailedReason, "%s", expectMsg), })) g.Expect(req.Object.Status.Failures).To(Equal(int64(1))) - g.Expect(recorder.GetEvents()).To(ConsistOf([]corev1.Event{ + g.Expect(recorder.GetEvents()).To(ConsistOf([]eventsv1.Event{ { - Type: corev1.EventTypeWarning, - Reason: v2.UninstallFailedReason, - Message: expectMsg, + Type: corev1.EventTypeWarning, + Reason: v2.UninstallFailedReason, + Action: string(v2.ReleaseActionUninstallRemediation), + Note: expectMsg, ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ eventMetaGroupKey(eventv1.MetaRevisionKey): cur.Chart.Metadata.Version, @@ -478,7 +480,7 @@ func TestUninstallRemediation_failure(t *testing.T) { t.Run("records failure with logs", func(t *testing.T) { g := NewWithT(t) - recorder := testutil.NewFakeRecorder(10, false) + recorder := events.NewFakeRecorder(10, false) r := &UninstallRemediation{ eventRecorder: recorder, } @@ -491,7 +493,7 @@ func TestUninstallRemediation_failure(t *testing.T) { events := recorder.GetEvents() g.Expect(events).To(HaveLen(1)) - g.Expect(events[0].Message).To(ContainSubstring(expectSubStr)) + g.Expect(events[0].Note).To(ContainSubstring(expectSubStr)) }) } @@ -505,7 +507,7 @@ func TestUninstallRemediation_success(t *testing.T) { Version: 4, }) - recorder := testutil.NewFakeRecorder(10, false) + recorder := events.NewFakeRecorder(10, false) r := &UninstallRemediation{ eventRecorder: recorder, } @@ -529,11 +531,12 @@ func TestUninstallRemediation_success(t *testing.T) { *conditions.TrueCondition(v2.RemediatedCondition, v2.UninstallSucceededReason, "%s", expectMsg), })) g.Expect(req.Object.Status.Failures).To(Equal(int64(0))) - g.Expect(recorder.GetEvents()).To(ConsistOf([]corev1.Event{ + g.Expect(recorder.GetEvents()).To(ConsistOf([]eventsv1.Event{ { - Type: corev1.EventTypeNormal, - Reason: v2.UninstallSucceededReason, - Message: expectMsg, + Type: corev1.EventTypeNormal, + Reason: v2.UninstallSucceededReason, + Action: string(v2.ReleaseActionUninstallRemediation), + Note: expectMsg, ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ eventMetaGroupKey(eventv1.MetaRevisionKey): cur.Chart.Metadata.Version, diff --git a/internal/reconcile/uninstall_test.go b/internal/reconcile/uninstall_test.go index 29e8534e0..563f73c2e 100644 --- a/internal/reconcile/uninstall_test.go +++ b/internal/reconcile/uninstall_test.go @@ -30,12 +30,13 @@ import ( helmstorage "helm.sh/helm/v4/pkg/storage" helmdriver "helm.sh/helm/v4/pkg/storage/driver" corev1 "k8s.io/api/core/v1" + eventsv1 "k8s.io/api/events/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/tools/record" - 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/runtime/conditions" + "github.com/fluxcd/pkg/runtime/events" "github.com/fluxcd/pkg/chartutil" @@ -535,7 +536,7 @@ func TestUninstall_Reconcile(t *testing.T) { cfg.Driver = tt.driver(cfg.Driver) } - recorder := new(record.FakeRecorder) + recorder := new(events.FakeRecorder) got := NewUninstall(cfg, recorder).Reconcile(context.TODO(), &Request{ Object: obj, }) @@ -593,7 +594,7 @@ func TestUninstall_failure(t *testing.T) { t.Run("records failure", func(t *testing.T) { g := NewWithT(t) - recorder := testutil.NewFakeRecorder(10, false) + recorder := events.NewFakeRecorder(10, false) r := &Uninstall{ eventRecorder: recorder, } @@ -610,11 +611,12 @@ func TestUninstall_failure(t *testing.T) { *conditions.FalseCondition(v2.ReleasedCondition, v2.UninstallFailedReason, "%s", expectMsg), })) g.Expect(req.Object.Status.Failures).To(Equal(int64(1))) - g.Expect(recorder.GetEvents()).To(ConsistOf([]corev1.Event{ + g.Expect(recorder.GetEvents()).To(ConsistOf([]eventsv1.Event{ { - Type: corev1.EventTypeWarning, - Reason: v2.UninstallFailedReason, - Message: expectMsg, + Type: corev1.EventTypeWarning, + Reason: v2.UninstallFailedReason, + Action: string(v2.ReleaseActionUninstall), + Note: expectMsg, ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ eventMetaGroupKey(eventv1.MetaRevisionKey): cur.Chart.Metadata.Version, @@ -629,7 +631,7 @@ func TestUninstall_failure(t *testing.T) { t.Run("records failure with logs", func(t *testing.T) { g := NewWithT(t) - recorder := testutil.NewFakeRecorder(10, false) + recorder := events.NewFakeRecorder(10, false) r := &Uninstall{ eventRecorder: recorder, } @@ -642,7 +644,7 @@ func TestUninstall_failure(t *testing.T) { events := recorder.GetEvents() g.Expect(events).To(HaveLen(1)) - g.Expect(events[0].Message).To(ContainSubstring(expectSubStr)) + g.Expect(events[0].Note).To(ContainSubstring(expectSubStr)) }) } @@ -656,7 +658,7 @@ func TestUninstall_success(t *testing.T) { Version: 4, }) - recorder := testutil.NewFakeRecorder(10, false) + recorder := events.NewFakeRecorder(10, false) r := &Uninstall{ eventRecorder: recorder, } @@ -679,11 +681,12 @@ func TestUninstall_success(t *testing.T) { *conditions.FalseCondition(v2.ReleasedCondition, v2.UninstallSucceededReason, "%s", expectMsg), })) g.Expect(req.Object.Status.Failures).To(Equal(int64(0))) - g.Expect(recorder.GetEvents()).To(ConsistOf([]corev1.Event{ + g.Expect(recorder.GetEvents()).To(ConsistOf([]eventsv1.Event{ { - Type: corev1.EventTypeNormal, - Reason: v2.UninstallSucceededReason, - Message: expectMsg, + Type: corev1.EventTypeNormal, + Reason: v2.UninstallSucceededReason, + Action: string(v2.ReleaseActionUninstall), + Note: expectMsg, ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ eventMetaGroupKey(eventv1.MetaRevisionKey): cur.Chart.Metadata.Version, diff --git a/internal/reconcile/unlock.go b/internal/reconcile/unlock.go index c78a194eb..46c88894e 100644 --- a/internal/reconcile/unlock.go +++ b/internal/reconcile/unlock.go @@ -26,9 +26,10 @@ import ( helmreleasecommon "helm.sh/helm/v4/pkg/release/common" helmreleasev1 "helm.sh/helm/v4/pkg/release/v1" corev1 "k8s.io/api/core/v1" - "k8s.io/client-go/tools/record" + eventv1 "github.com/fluxcd/pkg/apis/event/v1" "github.com/fluxcd/pkg/runtime/conditions" + "github.com/fluxcd/pkg/runtime/events" v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/action" @@ -51,12 +52,12 @@ import ( // propagated to the Ready condition on the Request.Object. type Unlock struct { configFactory *action.ConfigFactory - eventRecorder record.EventRecorder + eventRecorder events.EventRecorder } // NewUnlock returns a new Unlock reconciler configured with the provided // values. -func NewUnlock(cfg *action.ConfigFactory, recorder record.EventRecorder) *Unlock { +func NewUnlock(cfg *action.ConfigFactory, recorder events.EventRecorder) *Unlock { return &Unlock{configFactory: cfg, eventRecorder: recorder} } @@ -121,9 +122,11 @@ func (r *Unlock) failure(req *Request, cur *v2.Snapshot, status helmreleasecommo // Record warning event. r.eventRecorder.AnnotatedEventf( req.Object, + req.Source, eventMeta(cur.ChartVersion, cur.ConfigDigest, addAppVersion(cur.AppVersion), addOCIDigest(cur.OCIDigest)), corev1.EventTypeWarning, "PendingRelease", + eventv1.ActionFailed, "%s", msg, ) @@ -141,9 +144,11 @@ func (r *Unlock) success(req *Request, cur *v2.Snapshot, status helmreleasecommo // Record event. r.eventRecorder.AnnotatedEventf( req.Object, + req.Source, eventMeta(cur.ChartVersion, cur.ConfigDigest, addAppVersion(cur.AppVersion), addOCIDigest(cur.OCIDigest)), corev1.EventTypeNormal, "PendingRelease", + eventv1.ActionValidated, "%s", msg, ) diff --git a/internal/reconcile/unlock_test.go b/internal/reconcile/unlock_test.go index 4a67b5a14..047667d46 100644 --- a/internal/reconcile/unlock_test.go +++ b/internal/reconcile/unlock_test.go @@ -30,12 +30,13 @@ import ( helmstorage "helm.sh/helm/v4/pkg/storage" helmdriver "helm.sh/helm/v4/pkg/storage/driver" corev1 "k8s.io/api/core/v1" + eventsv1 "k8s.io/api/events/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/tools/record" - 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/runtime/conditions" + "github.com/fluxcd/pkg/runtime/events" "github.com/fluxcd/pkg/chartutil" @@ -338,7 +339,7 @@ func TestUnlock_Reconcile(t *testing.T) { cfg.Driver = tt.driver(cfg.Driver) } - recorder := new(record.FakeRecorder) + recorder := new(events.FakeRecorder) got := NewUnlock(cfg, recorder).Reconcile(context.TODO(), &Request{ Object: obj, }) @@ -380,8 +381,7 @@ func TestUnlock_failure(t *testing.T) { status = helmreleasecommon.StatusPendingInstall err = fmt.Errorf("unlock error") ) - - recorder := testutil.NewFakeRecorder(10, false) + recorder := events.NewFakeRecorder(10, false) r := &Unlock{ eventRecorder: recorder, } @@ -398,11 +398,12 @@ func TestUnlock_failure(t *testing.T) { *conditions.FalseCondition(v2.ReleasedCondition, "PendingRelease", "%s", expectMsg), })) g.Expect(req.Object.Status.Failures).To(Equal(int64(1))) - g.Expect(recorder.GetEvents()).To(ConsistOf([]corev1.Event{ + g.Expect(recorder.GetEvents()).To(ConsistOf([]eventsv1.Event{ { - Type: corev1.EventTypeWarning, - Reason: "PendingRelease", - Message: expectMsg, + Type: corev1.EventTypeWarning, + Reason: "PendingRelease", + Action: eventv1.ActionFailed, + Note: expectMsg, ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ eventMetaGroupKey(eventv1.MetaRevisionKey): cur.Chart.Metadata.Version, @@ -428,7 +429,7 @@ func TestUnlock_success(t *testing.T) { status = helmreleasecommon.StatusPendingInstall ) - recorder := testutil.NewFakeRecorder(10, false) + recorder := events.NewFakeRecorder(10, false) r := &Unlock{ eventRecorder: recorder, } @@ -445,11 +446,12 @@ func TestUnlock_success(t *testing.T) { *conditions.FalseCondition(v2.ReleasedCondition, "PendingRelease", "%s", expectMsg), })) g.Expect(req.Object.Status.Failures).To(Equal(int64(0))) - g.Expect(recorder.GetEvents()).To(ConsistOf([]corev1.Event{ + g.Expect(recorder.GetEvents()).To(ConsistOf([]eventsv1.Event{ { - Type: corev1.EventTypeNormal, - Reason: "PendingRelease", - Message: expectMsg, + Type: corev1.EventTypeNormal, + Reason: "PendingRelease", + Action: eventv1.ActionValidated, + Note: expectMsg, ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ eventMetaGroupKey(eventv1.MetaRevisionKey): cur.Chart.Metadata.Version, @@ -508,7 +510,7 @@ func TestUnlock_withOCIDigest(t *testing.T) { store := helmstorage.Init(cfg.Driver) g.Expect(store.Create(rls)).To(Succeed()) - recorder := testutil.NewFakeRecorder(10, false) + recorder := events.NewFakeRecorder(10, false) got := NewUnlock(cfg, recorder).Reconcile(context.TODO(), &Request{ Object: obj, }) @@ -534,11 +536,12 @@ func TestUnlock_withOCIDigest(t *testing.T) { fmt.Sprintf("%s@%s", rls.Chart.Name(), rls.Chart.Metadata.Version), rls.Info.Status.String()) - g.Expect(recorder.GetEvents()).To(ConsistOf([]corev1.Event{ + g.Expect(recorder.GetEvents()).To(ConsistOf([]eventsv1.Event{ { - Type: corev1.EventTypeNormal, - Reason: "PendingRelease", - Message: expectMsg, + Type: corev1.EventTypeNormal, + Reason: "PendingRelease", + Action: eventv1.ActionValidated, + Note: expectMsg, ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ eventMetaGroupKey(metaOCIDigestKey): expected.OCIDigest, diff --git a/internal/reconcile/upgrade.go b/internal/reconcile/upgrade.go index d5e43f3ba..ad38366f7 100644 --- a/internal/reconcile/upgrade.go +++ b/internal/reconcile/upgrade.go @@ -24,10 +24,10 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/tools/record" "github.com/fluxcd/pkg/chartutil" "github.com/fluxcd/pkg/runtime/conditions" + "github.com/fluxcd/pkg/runtime/events" v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/action" @@ -54,13 +54,13 @@ import ( // e.g. action.VerifySnapshot before calling Reconcile. type Upgrade struct { configFactory *action.ConfigFactory - eventRecorder record.EventRecorder + eventRecorder events.EventRecorder defaultToRetryOnFailure bool } // NewUpgrade returns a new Upgrade reconciler configured with the provided // values. -func NewUpgrade(cfg *action.ConfigFactory, recorder record.EventRecorder, defaultToRetryOnFailure bool) *Upgrade { +func NewUpgrade(cfg *action.ConfigFactory, recorder events.EventRecorder, defaultToRetryOnFailure bool) *Upgrade { return &Upgrade{configFactory: cfg, eventRecorder: recorder, defaultToRetryOnFailure: defaultToRetryOnFailure} } @@ -68,7 +68,7 @@ func (r *Upgrade) Reconcile(ctx context.Context, req *Request) error { var ( logBuf = action.NewDebugLogBuffer(ctx) obsReleases = make(observedReleases) - cfg = r.configFactory.Build(logBuf, observeRelease(obsReleases), observeInventory(req.Object, req.Chart, r.configFactory.Getter, r.eventRecorder)) + cfg = r.configFactory.Build(logBuf, observeRelease(obsReleases), observeInventory(req.Object, req.Source, req.Chart, r.configFactory.Getter, r.eventRecorder)) startTime = time.Now() ) @@ -151,10 +151,12 @@ func (r *Upgrade) failure(req *Request, buffer *action.LogBuffer, err error) { // Condition summary. r.eventRecorder.AnnotatedEventf( req.Object, + req.Source, eventMeta(req.Chart.Metadata.Version, chartutil.DigestValues(digest.Canonical, req.Values).String(), addAppVersion(req.Chart.AppVersion()), addOCIDigest(req.Object.Status.LastAttemptedRevisionDigest)), corev1.EventTypeWarning, v2.UpgradeFailedReason, + string(v2.ReleaseActionUpgrade), "%s", eventMessageWithLog(msg, buffer), ) @@ -186,9 +188,11 @@ func (r *Upgrade) success(req *Request) { // Record event. r.eventRecorder.AnnotatedEventf( req.Object, + req.Source, eventMeta(cur.ChartVersion, cur.ConfigDigest, addAppVersion(cur.AppVersion), addOCIDigest(cur.OCIDigest)), corev1.EventTypeNormal, v2.UpgradeSucceededReason, + string(v2.ReleaseActionUpgrade), "%s", msg, ) diff --git a/internal/reconcile/upgrade_test.go b/internal/reconcile/upgrade_test.go index 674c1b16f..799db4458 100644 --- a/internal/reconcile/upgrade_test.go +++ b/internal/reconcile/upgrade_test.go @@ -32,15 +32,16 @@ import ( helmstorage "helm.sh/helm/v4/pkg/storage" helmdriver "helm.sh/helm/v4/pkg/storage/driver" corev1 "k8s.io/api/core/v1" + eventsv1 "k8s.io/api/events/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/tools/record" "sigs.k8s.io/controller-runtime/pkg/client" - 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/chartutil" "github.com/fluxcd/pkg/runtime/conditions" + "github.com/fluxcd/pkg/runtime/events" v2 "github.com/fluxcd/helm-controller/api/v2" "github.com/fluxcd/helm-controller/internal/action" @@ -497,7 +498,7 @@ func TestUpgrade_Reconcile(t *testing.T) { cfg.Driver = tt.driver(cfg.Driver) } - recorder := new(record.FakeRecorder) + recorder := new(events.FakeRecorder) got := NewUpgrade(cfg, recorder, false).Reconcile(context.TODO(), &Request{ Object: obj, Chart: tt.chart, @@ -653,7 +654,7 @@ func TestUpgrade_Reconcile_withSubchartWithCRDs(t *testing.T) { _ = testEnv.Delete(context.TODO(), subChartCRD) chart := testutil.BuildChartWithSubchartWithCRD() - recorder := new(record.FakeRecorder) + recorder := new(events.FakeRecorder) got := NewUpgrade(cfg, recorder, false).Reconcile(context.TODO(), &Request{ Object: obj, Chart: chart, @@ -724,7 +725,7 @@ func TestUpgrade_failure(t *testing.T) { t.Run("records failure", func(t *testing.T) { g := NewWithT(t) - recorder := testutil.NewFakeRecorder(10, false) + recorder := events.NewFakeRecorder(10, true) r := &Upgrade{ eventRecorder: recorder, } @@ -739,11 +740,12 @@ func TestUpgrade_failure(t *testing.T) { *conditions.FalseCondition(v2.ReleasedCondition, v2.UpgradeFailedReason, "%s", expectMsg), })) g.Expect(req.Object.Status.Failures).To(Equal(int64(1))) - g.Expect(recorder.GetEvents()).To(ConsistOf([]corev1.Event{ + g.Expect(recorder.GetEvents()).To(ConsistOf([]eventsv1.Event{ { - Type: corev1.EventTypeWarning, - Reason: v2.UpgradeFailedReason, - Message: expectMsg, + Type: corev1.EventTypeWarning, + Reason: v2.UpgradeFailedReason, + Action: string(v2.ReleaseActionUpgrade), + Note: expectMsg, ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ eventMetaGroupKey(metaOCIDigestKey): obj.Status.LastAttemptedRevisionDigest, @@ -759,7 +761,7 @@ func TestUpgrade_failure(t *testing.T) { t.Run("records failure with logs", func(t *testing.T) { g := NewWithT(t) - recorder := testutil.NewFakeRecorder(10, false) + recorder := events.NewFakeRecorder(10, false) r := &Upgrade{ eventRecorder: recorder, } @@ -772,7 +774,7 @@ func TestUpgrade_failure(t *testing.T) { events := recorder.GetEvents() g.Expect(events).To(HaveLen(1)) - g.Expect(events[0].Message).To(ContainSubstring(expectSubStr)) + g.Expect(events[0].Note).To(ContainSubstring(expectSubStr)) }) } @@ -795,7 +797,7 @@ func TestUpgrade_success(t *testing.T) { t.Run("records success", func(t *testing.T) { g := NewWithT(t) - recorder := testutil.NewFakeRecorder(10, false) + recorder := events.NewFakeRecorder(10, true) r := &Upgrade{ eventRecorder: recorder, } @@ -812,11 +814,12 @@ func TestUpgrade_success(t *testing.T) { g.Expect(req.Object.Status.Conditions).To(conditions.MatchConditions([]metav1.Condition{ *conditions.TrueCondition(v2.ReleasedCondition, v2.UpgradeSucceededReason, "%s", expectMsg), })) - g.Expect(recorder.GetEvents()).To(ConsistOf([]corev1.Event{ + g.Expect(recorder.GetEvents()).To(ConsistOf([]eventsv1.Event{ { - Type: corev1.EventTypeNormal, - Reason: v2.UpgradeSucceededReason, - Message: expectMsg, + Type: corev1.EventTypeNormal, + Reason: v2.UpgradeSucceededReason, + Action: string(v2.ReleaseActionUpgrade), + Note: expectMsg, ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ eventMetaGroupKey(eventv1.MetaRevisionKey): obj.Status.History.Latest().ChartVersion, @@ -831,7 +834,7 @@ func TestUpgrade_success(t *testing.T) { t.Run("clears failures if retry strategy is configured", func(t *testing.T) { g := NewWithT(t) - recorder := testutil.NewFakeRecorder(10, false) + recorder := events.NewFakeRecorder(10, false) r := &Upgrade{ eventRecorder: recorder, } @@ -857,7 +860,7 @@ func TestUpgrade_success(t *testing.T) { t.Run("records success with TestSuccess=False", func(t *testing.T) { g := NewWithT(t) - recorder := testutil.NewFakeRecorder(10, false) + recorder := events.NewFakeRecorder(10, false) r := &Upgrade{ eventRecorder: recorder, } diff --git a/internal/testutil/fake_recorder.go b/internal/testutil/fake_recorder.go deleted file mode 100644 index d64b0bb3a..000000000 --- a/internal/testutil/fake_recorder.go +++ /dev/null @@ -1,117 +0,0 @@ -/* -Copyright 2022 The Flux authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package testutil - -import ( - "fmt" - - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/runtime" - - _ "k8s.io/client-go/tools/record" -) - -// FakeRecorder is used as a fake during tests. -// -// It was invented to be used in tests which require more precise control over -// e.g. assertions of specific event fields like Reason. For which string -// comparisons on the concentrated event message using record.FakeRecorder is -// not sufficient. -// -// To empty the Events channel into a slice of the recorded events, use -// GetEvents(). Not initializing Events will cause the recorder to not record -// any messages. -type FakeRecorder struct { - Events chan corev1.Event - IncludeObject bool -} - -// NewFakeRecorder creates new fake event recorder with an Events channel with -// the given size. Setting includeObject to true will cause the recorder to -// include the object reference in the events. -// -// To initialize a recorder which does not record any events, simply use: -// -// recorder := new(FakeRecorder) -func NewFakeRecorder(bufferSize int, includeObject bool) *FakeRecorder { - return &FakeRecorder{ - Events: make(chan corev1.Event, bufferSize), - IncludeObject: includeObject, - } -} - -// Event emits an event with the given message. -func (f *FakeRecorder) Event(obj runtime.Object, eventType, reason, message string) { - f.Eventf(obj, eventType, reason, "%s", message) -} - -// Eventf emits an event with the given message. -func (f *FakeRecorder) Eventf(obj runtime.Object, eventType, reason, message string, args ...any) { - if f.Events != nil { - f.Events <- f.generateEvent(obj, nil, eventType, reason, message, args...) - } -} - -// AnnotatedEventf emits an event with annotations. -func (f *FakeRecorder) AnnotatedEventf(obj runtime.Object, annotations map[string]string, eventType, reason, message string, args ...any) { - if f.Events != nil { - f.Events <- f.generateEvent(obj, annotations, eventType, reason, message, args...) - } -} - -// GetEvents empties the Events channel and returns a slice of recorded events. -// If the Events channel is nil, it returns nil. -func (f *FakeRecorder) GetEvents() (events []corev1.Event) { - if f.Events != nil { - for { - select { - case e := <-f.Events: - events = append(events, e) - default: - return events - } - } - } - return nil -} - -// generateEvent generates a new mocked event with the given parameters. -func (f *FakeRecorder) generateEvent(obj runtime.Object, annotations map[string]string, eventType, reason, message string, args ...any) corev1.Event { - event := corev1.Event{ - InvolvedObject: objectReference(obj, f.IncludeObject), - Type: eventType, - Reason: reason, - Message: fmt.Sprintf(message, args...), - } - if annotations != nil { - event.ObjectMeta.Annotations = annotations - } - return event -} - -// objectReference returns an object reference for the given object with the -// kind and (group) API version set. -func objectReference(obj runtime.Object, includeObject bool) corev1.ObjectReference { - if !includeObject { - return corev1.ObjectReference{} - } - - return corev1.ObjectReference{ - Kind: obj.GetObjectKind().GroupVersionKind().Kind, - APIVersion: obj.GetObjectKind().GroupVersionKind().GroupVersion().String(), - } -} diff --git a/main.go b/main.go index a2153982e..e8a63529f 100644 --- a/main.go +++ b/main.go @@ -321,8 +321,8 @@ func main() { probes.SetupChecks(mgr, setupLog) metricsH := helper.NewMetrics(mgr, metrics.MustMakeRecorder(), v2.HelmReleaseFinalizer) - 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) }