From c16676641c7db64ff1e88501f493947aab8d8905 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20B=C3=A4umer?= Date: Tue, 18 May 2021 19:21:56 +0200 Subject: [PATCH 01/10] Fork helm-operator adjustments for CI and documentation (#4) --- .github/labeler.yml | 2 ++ .github/workflows/ci.yml | 6 +---- .github/workflows/deploy.yml | 17 +++++++------- README.md | 44 ++++++++++++++++++++++++++++++++++-- 4 files changed, 54 insertions(+), 15 deletions(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index 164dc38..c169049 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,3 +1,5 @@ +upstream-triage: + - "./*" area/main-binary: - 'main.go' - 'internal/**/*' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ecca3b6..d3a2fa2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,11 +1,7 @@ name: CI on: - push: - branches: - - '**' - pull_request: - branches: [ main ] + - push jobs: diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 1e70ea7..62c6259 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,13 +1,14 @@ name: Deploy -on: - push: - branches: - - '**' - tags: - - 'v*' - pull_request: - branches: [ main ] +# Disabled as we don't need docker images to use the helm-operator as a library. +#on: +# push: +# branches: +# - '**' +# tags: +# - 'v*' +# pull_request: +# branches: [ main ] jobs: goreleaser: diff --git a/README.md b/README.md index 5d88309..90dce68 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # helm-operator -![Build Status](https://github.com/operator-framework/helm-operator-plugins/workflows/CI/badge.svg?branch=master) -[![Coverage Status](https://coveralls.io/repos/github/operator-framework/helm-operator-plugins/badge.svg?branch=master)](https://coveralls.io/github/operator-framework/helm-operator-plugins?branch=master) +[![Build Status](https://github.com/stackrox/helm-operator/workflows/CI/badge.svg?branch=main) Reimplementation of the helm operator to enrich the Helm operator's reconciliation with custom Go code to create a hybrid operator. @@ -41,3 +40,44 @@ if err := reconciler.SetupWithManager(mgr); err != nil { panic(fmt.Sprintf("unable to create reconciler: %s", err)) } ``` + +## Why a fork? + +Initially the Helm operator type was designed to automate Helm chart operations +by mapping the [values](https://helm.sh/docs/chart_template_guide/values_files/) of a Helm chart exactly to a +`CustomResourceDefinition` and defining its watched resources in a `watches.yaml` +[configuration](https://sdk.operatorframework.io/docs/building-operators/helm/tutorial/#watch-the-nginx-cr). + +To write a [Level II+](https://sdk.operatorframework.io/docs/advanced-topics/operator-capabilities/operator-capabilities/) operator +which reuses an already existing Helm chart a [hybrid](https://github.com/operator-framework/operator-sdk/issues/670) +between the Go and Helm operator type is necessary. + +The hybrid approach allows to add customizations to the Helm operator like value mapping based on cluster state or +executing code in on specific events. + +### Quickstart + +Add this module as a replace directive to your `go.mod`: + +``` +go mod edit -replace=github.com/joelanford/helm-operator=github.com/stackrox/helm-operator@main +``` + +Example: + +``` +chart, err := loader.Load("path/to/chart") +if err != nil { + panic(err) +} + +reconciler := reconciler.New( + reconciler.WithChart(*chart), + reconciler.WithGroupVersionKind(gvk), +) + +if err := reconciler.SetupWithManager(mgr); err != nil { + panic(fmt.Sprintf("unable to create reconciler: %s", err)) +} +``` + From 4a89a9f6d85a29271fb9948d44cdaa47a97d4fae Mon Sep 17 00:00:00 2001 From: Gaurav Nelson Date: Thu, 20 May 2021 17:22:49 +1000 Subject: [PATCH 02/10] Updated README (#5) --- README.md | 53 +++++++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 90dce68..b553e81 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # helm-operator -[![Build Status](https://github.com/stackrox/helm-operator/workflows/CI/badge.svg?branch=main) +![Build Status](https://github.com/stackrox/helm-operator/workflows/CI/badge.svg?branch=main) Reimplementation of the helm operator to enrich the Helm operator's reconciliation with custom Go code to create a hybrid operator. @@ -43,41 +43,42 @@ if err := reconciler.SetupWithManager(mgr); err != nil { ## Why a fork? -Initially the Helm operator type was designed to automate Helm chart operations +The Helm operator type automates Helm chart operations by mapping the [values](https://helm.sh/docs/chart_template_guide/values_files/) of a Helm chart exactly to a `CustomResourceDefinition` and defining its watched resources in a `watches.yaml` -[configuration](https://sdk.operatorframework.io/docs/building-operators/helm/tutorial/#watch-the-nginx-cr). +[configuration](https://sdk.operatorframework.io/docs/building-operators/helm/tutorial/#watch-the-nginx-cr) file. -To write a [Level II+](https://sdk.operatorframework.io/docs/advanced-topics/operator-capabilities/operator-capabilities/) operator -which reuses an already existing Helm chart a [hybrid](https://github.com/operator-framework/operator-sdk/issues/670) -between the Go and Helm operator type is necessary. +For creating a [Level II+](https://sdk.operatorframework.io/docs/advanced-topics/operator-capabilities/operator-capabilities/) operator +that reuses an already existing Helm chart, we need a [hybrid](https://github.com/operator-framework/operator-sdk/issues/670) +between the Go and Helm operator types is. -The hybrid approach allows to add customizations to the Helm operator like value mapping based on cluster state or -executing code in on specific events. +The hybrid approach allows adding customizations to the Helm operator, such as: +- value mapping based on cluster state, or +- executing code in specific events. ### Quickstart -Add this module as a replace directive to your `go.mod`: +- Add this module as a replace directive to your `go.mod`: -``` -go mod edit -replace=github.com/joelanford/helm-operator=github.com/stackrox/helm-operator@main -``` + ``` + go mod edit -replace=github.com/joelanford/helm-operator=github.com/stackrox/helm-operator@main + ``` -Example: + For example: -``` -chart, err := loader.Load("path/to/chart") -if err != nil { - panic(err) -} + ```go + chart, err := loader.Load("path/to/chart") + if err != nil { + panic(err) + } -reconciler := reconciler.New( - reconciler.WithChart(*chart), - reconciler.WithGroupVersionKind(gvk), -) + reconciler := reconciler.New( + reconciler.WithChart(*chart), + reconciler.WithGroupVersionKind(gvk), + ) -if err := reconciler.SetupWithManager(mgr); err != nil { - panic(fmt.Sprintf("unable to create reconciler: %s", err)) -} -``` + if err := reconciler.SetupWithManager(mgr); err != nil { + panic(fmt.Sprintf("unable to create reconciler: %s", err)) + } + ``` From 34b82928581b69326e2cc327adf1c14b5de14b31 Mon Sep 17 00:00:00 2001 From: Gaurav Nelson Date: Fri, 21 May 2021 09:57:56 +1000 Subject: [PATCH 03/10] fixed typo in README (#7) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b553e81..ab4a235 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ by mapping the [values](https://helm.sh/docs/chart_template_guide/values_files/) For creating a [Level II+](https://sdk.operatorframework.io/docs/advanced-topics/operator-capabilities/operator-capabilities/) operator that reuses an already existing Helm chart, we need a [hybrid](https://github.com/operator-framework/operator-sdk/issues/670) -between the Go and Helm operator types is. +between the Go and Helm operator types. The hybrid approach allows adding customizations to the Helm operator, such as: - value mapping based on cluster state, or From ff2ca271aec6786fa140cbca978384b0dde33a33 Mon Sep 17 00:00:00 2001 From: Malte Isberner <2822367+misberner@users.noreply.github.com> Date: Tue, 15 Jun 2021 12:16:55 +0200 Subject: [PATCH 04/10] Allow adding reconciliation extensions (#9) --- pkg/extensions/types.go | 12 +++++++ pkg/reconciler/reconciler.go | 69 ++++++++++++++++++++++++++++++++++-- 2 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 pkg/extensions/types.go diff --git a/pkg/extensions/types.go b/pkg/extensions/types.go new file mode 100644 index 0000000..689f91d --- /dev/null +++ b/pkg/extensions/types.go @@ -0,0 +1,12 @@ +package extensions + +import ( + "context" + "github.com/go-logr/logr" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +// ReconcileExtension is an arbitrary extension that can be implemented to run either before +// or after the main Helm reconciliation action. +// An error returned by a ReconcileExtension will cause the Reconcile to fail, unlike a hook error. +type ReconcileExtension func(context.Context, *unstructured.Unstructured, logr.Logger) error diff --git a/pkg/reconciler/reconciler.go b/pkg/reconciler/reconciler.go index f49bad5..444efc5 100644 --- a/pkg/reconciler/reconciler.go +++ b/pkg/reconciler/reconciler.go @@ -56,6 +56,7 @@ import ( "github.com/operator-framework/helm-operator-plugins/pkg/reconciler/internal/updater" internalvalues "github.com/operator-framework/helm-operator-plugins/pkg/reconciler/internal/values" "github.com/operator-framework/helm-operator-plugins/pkg/values" + "github.com/joelanford/helm-operator/pkg/extensions" ) const uninstallFinalizer = "uninstall-helm-release" @@ -70,6 +71,9 @@ type Reconciler struct { preHooks []hook.PreHook postHooks []hook.PostHook + preExtensions []extensions.ReconcileExtension + postExtensions []extensions.ReconcileExtension + log logr.Logger gvk *schema.GroupVersionKind chrt *chart.Chart @@ -420,6 +424,20 @@ func WithPreHook(h hook.PreHook) Option { } } +// WithPreExtension is an Option that configures the reconciler to run the given +// extension before performing any reconciliation steps (including values translation). +// An error returned from the extension will cause the reconciliation to fail. +// This should be preferred to WithPreHook in most cases, except for when the logic +// depends on the translated Helm values. +// The extension will be invoked with the raw object state; meaning it needs to be careful +// to check for existence of the deletionTimestamp field. +func WithPreExtension(e extensions.ReconcileExtension) Option { + return func(r *Reconciler) error { + r.preExtensions = append(r.preExtensions, e) + return nil + } +} + // WithPostHook is an Option that configures the reconciler to run the given // PostHook just after performing any non-uninstall release actions. func WithPostHook(h hook.PostHook) Option { @@ -429,6 +447,22 @@ func WithPostHook(h hook.PostHook) Option { } } +// WithPostExtension is an Option that configures the reconciler to run the given +// extension after performing any reconciliation steps (including uninstall of the release, +// but not removal of the finalizer). +// An error returned from the extension will cause the reconciliation to fail, which might +// prevent the finalizer from getting removed. +// This should be preferred to WithPostHook in most cases, except for when the logic +// depends on the translated Helm values. +// The extension will be invoked with the raw object state; meaning it needs to be careful +// to check for existence of the deletionTimestamp field. +func WithPostExtension(e extensions.ReconcileExtension) Option { + return func(r *Reconciler) error { + r.postExtensions = append(r.postExtensions, e) + return nil + } +} + // WithValueTranslator is an Option that configures a function that translates a // custom resource to the values passed to Helm. // Use this if you need to customize the logic that translates your custom resource to Helm values. @@ -563,6 +597,16 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (res ctrl. } u.UpdateStatus(updater.EnsureCondition(conditions.Initialized(corev1.ConditionTrue, "", ""))) + for _, ext := range r.preExtensions { + if err := ext(ctx, obj, r.log); err != nil { + u.UpdateStatus( + updater.EnsureCondition(conditions.Irreconcilable(corev1.ConditionTrue, conditions.ReasonReconcileError, err)), + updater.EnsureConditionUnknown(conditions.TypeReleaseFailed), + ) + return ctrl.Result{}, err + } + } + if obj.GetDeletionTimestamp() != nil { err := r.handleDeletion(ctx, actionClient, obj, log) return ctrl.Result{}, err @@ -622,6 +666,16 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (res ctrl. } } + for _, ext := range r.postExtensions { + if err := ext(ctx, obj, r.log); err != nil { + u.UpdateStatus( + updater.EnsureCondition(conditions.Irreconcilable(corev1.ConditionTrue, conditions.ReasonReconcileError, err)), + updater.EnsureConditionUnknown(conditions.TypeReleaseFailed), + ) + return ctrl.Result{}, err + } + } + ensureDeployedRelease(&u, rel) u.UpdateStatus( updater.EnsureCondition(conditions.ReleaseFailed(corev1.ConditionFalse, "", "")), @@ -676,7 +730,7 @@ func (r *Reconciler) handleDeletion(ctx context.Context, actionClient helmclient err = applyErr } }() - return r.doUninstall(actionClient, &uninstallUpdater, obj, log) + return r.doUninstall(ctx, actionClient, &uninstallUpdater, obj, log) }(); err != nil { return err } @@ -822,7 +876,7 @@ func (r *Reconciler) doReconcile(actionClient helmclient.ActionInterface, u *upd return nil } -func (r *Reconciler) doUninstall(actionClient helmclient.ActionInterface, u *updater.Updater, obj *unstructured.Unstructured, log logr.Logger) error { +func (r *Reconciler) doUninstall(ctx context.Context, actionClient helmclient.ActionInterface, u *updater.Updater, obj *unstructured.Unstructured, log logr.Logger) error { var opts []helmclient.UninstallOption for name, annot := range r.uninstallAnnotations { if v, ok := obj.GetAnnotations()[name]; ok { @@ -847,6 +901,17 @@ func (r *Reconciler) doUninstall(actionClient helmclient.ActionInterface, u *upd fmt.Println(diff.Generate(resp.Release.Manifest, "")) } } + + for _, ext := range r.postExtensions { + if err := ext(ctx, obj, r.log); err != nil { + u.UpdateStatus( + updater.EnsureCondition(conditions.Irreconcilable(corev1.ConditionTrue, conditions.ReasonReconcileError, err)), + updater.EnsureConditionUnknown(conditions.TypeReleaseFailed), + ) + return err + } + } + u.Update(updater.RemoveFinalizer(uninstallFinalizer)) u.UpdateStatus( updater.EnsureCondition(conditions.ReleaseFailed(corev1.ConditionFalse, "", "")), From 43a4cd85a603bcc836503bc5423b753023988ce7 Mon Sep 17 00:00:00 2001 From: Malte Isberner <2822367+misberner@users.noreply.github.com> Date: Tue, 29 Jun 2021 15:16:01 +0200 Subject: [PATCH 05/10] Allow marking releases stuck in a pending state as failed (#16) --- pkg/client/actionclient.go | 9 ++++ .../internal/conditions/conditions.go | 1 + pkg/reconciler/internal/fake/actionclient.go | 34 +++++++++---- pkg/reconciler/reconciler.go | 51 +++++++++++++++++++ 4 files changed, 84 insertions(+), 11 deletions(-) diff --git a/pkg/client/actionclient.go b/pkg/client/actionclient.go index ef1ed18..acb19b1 100644 --- a/pkg/client/actionclient.go +++ b/pkg/client/actionclient.go @@ -62,6 +62,7 @@ type ActionInterface interface { Get(name string, opts ...GetOption) (*release.Release, error) Install(name, namespace string, chrt *chart.Chart, vals map[string]interface{}, opts ...InstallOption) (*release.Release, error) Upgrade(name, namespace string, chrt *chart.Chart, vals map[string]interface{}, opts ...UpgradeOption) (*release.Release, error) + MarkFailed(release *release.Release, reason string) error Uninstall(name string, opts ...UninstallOption) (*release.UninstallReleaseResponse, error) Reconcile(rel *release.Release) error } @@ -180,6 +181,14 @@ func (c *actionClient) Upgrade(name, namespace string, chrt *chart.Chart, vals m return rel, nil } +func (c *actionClient) MarkFailed(rel *release.Release, reason string) error { + infoCopy := *rel.Info + releaseCopy := *rel + releaseCopy.Info = &infoCopy + releaseCopy.SetStatus(release.StatusFailed, reason) + return c.conf.Releases.Update(&releaseCopy) +} + func (c *actionClient) Uninstall(name string, opts ...UninstallOption) (*release.UninstallReleaseResponse, error) { uninstall := action.NewUninstall(c.conf) for _, o := range opts { diff --git a/pkg/reconciler/internal/conditions/conditions.go b/pkg/reconciler/internal/conditions/conditions.go index 55e2c65..86beb90 100644 --- a/pkg/reconciler/internal/conditions/conditions.go +++ b/pkg/reconciler/internal/conditions/conditions.go @@ -41,6 +41,7 @@ const ( ReasonUpgradeError = status.ConditionReason("UpgradeError") ReasonReconcileError = status.ConditionReason("ReconcileError") ReasonUninstallError = status.ConditionReason("UninstallError") + ReasonPendingError = status.ConditionReason("PendingError") ) func Initialized(stat corev1.ConditionStatus, reason status.ConditionReason, message interface{}) status.Condition { diff --git a/pkg/reconciler/internal/fake/actionclient.go b/pkg/reconciler/internal/fake/actionclient.go index 92b7da6..610cb74 100644 --- a/pkg/reconciler/internal/fake/actionclient.go +++ b/pkg/reconciler/internal/fake/actionclient.go @@ -48,17 +48,19 @@ func (hcg *fakeActionClientGetter) ActionClientFor(_ crclient.Object) (client.Ac } type ActionClient struct { - Gets []GetCall - Installs []InstallCall - Upgrades []UpgradeCall - Uninstalls []UninstallCall - Reconciles []ReconcileCall - - HandleGet func() (*release.Release, error) - HandleInstall func() (*release.Release, error) - HandleUpgrade func() (*release.Release, error) - HandleUninstall func() (*release.UninstallReleaseResponse, error) - HandleReconcile func() error + Gets []GetCall + Installs []InstallCall + Upgrades []UpgradeCall + MarkFaileds []MarkFailedCall + Uninstalls []UninstallCall + Reconciles []ReconcileCall + + HandleGet func() (*release.Release, error) + HandleInstall func() (*release.Release, error) + HandleUpgrade func() (*release.Release, error) + HandleMarkFailed func() error + HandleUninstall func() (*release.UninstallReleaseResponse, error) + HandleReconcile func() error } func NewActionClient() ActionClient { @@ -109,6 +111,11 @@ type UpgradeCall struct { Opts []client.UpgradeOption } +type MarkFailedCall struct { + Release *release.Release + Reason string +} + type UninstallCall struct { Name string Opts []client.UninstallOption @@ -133,6 +140,11 @@ func (c *ActionClient) Upgrade(name, namespace string, chrt *chart.Chart, vals m return c.HandleUpgrade() } +func (c *ActionClient) MarkFailed(rel *release.Release, reason string) error { + c.MarkFaileds = append(c.MarkFaileds, MarkFailedCall{rel, reason}) + return c.HandleMarkFailed() +} + func (c *ActionClient) Uninstall(name string, opts ...client.UninstallOption) (*release.UninstallReleaseResponse, error) { c.Uninstalls = append(c.Uninstalls, UninstallCall{name, opts}) return c.HandleUninstall() diff --git a/pkg/reconciler/reconciler.go b/pkg/reconciler/reconciler.go index 444efc5..5fac798 100644 --- a/pkg/reconciler/reconciler.go +++ b/pkg/reconciler/reconciler.go @@ -82,6 +82,7 @@ type Reconciler struct { skipDependentWatches bool maxConcurrentReconciles int reconcilePeriod time.Duration + markFailedAfter time.Duration maxHistory int skipPrimaryGVKSchemeRegistration bool @@ -351,6 +352,18 @@ func WithMaxReleaseHistory(maxHistory int) Option { } } +// WithMarkFailedAfter specifies the duration after which the reconciler will mark a release in a pending (locked) +// state as false in order to allow rolling forward. +func WithMarkFailedAfter(duration time.Duration) Option { + return func(r *Reconciler) error { + if duration < 0 { + return errors.New("auto-rollback after duration must not be negative") + } + r.markFailedAfter = duration + return nil + } +} + // WithInstallAnnotations is an Option that configures Install annotations // to enable custom action.Install fields to be set based on the value of // annotations found in the custom resource watched by this reconciler. @@ -631,6 +644,10 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (res ctrl. ) return ctrl.Result{}, err } + if state == statePending { + return r.handlePending(actionClient, rel, &u, log) + } + u.UpdateStatus(updater.EnsureCondition(conditions.Irreconcilable(corev1.ConditionFalse, "", ""))) for _, h := range r.preHooks { @@ -707,6 +724,7 @@ const ( stateNeedsInstall helmReleaseState = "needs install" stateNeedsUpgrade helmReleaseState = "needs upgrade" stateUnchanged helmReleaseState = "unchanged" + statePending helmReleaseState = "pending" stateError helmReleaseState = "error" ) @@ -755,6 +773,10 @@ func (r *Reconciler) getReleaseState(client helmclient.ActionInterface, obj meta return nil, stateNeedsInstall, nil } + if currentRelease.Info != nil && currentRelease.Info.Status.IsPending() { + return currentRelease, statePending, nil + } + var opts []helmclient.UpgradeOption if r.maxHistory > 0 { opts = append(opts, func(u *action.Upgrade) error { @@ -849,6 +871,35 @@ func (r *Reconciler) doUpgrade(actionClient helmclient.ActionInterface, u *updat return rel, nil } +func (r *Reconciler) handlePending(actionClient helmclient.ActionInterface, rel *release.Release, u *updater.Updater, log logr.Logger) (ctrl.Result, error) { + err := r.doHandlePending(actionClient, rel, log) + if err == nil { + err = errors.New("unknown error handling pending release") + } + u.UpdateStatus( + updater.EnsureCondition(conditions.Irreconcilable(corev1.ConditionTrue, conditions.ReasonPendingError, err))) + return ctrl.Result{}, err +} + +func (r *Reconciler) doHandlePending(actionClient helmclient.ActionInterface, rel *release.Release, log logr.Logger) error { + if r.markFailedAfter <= 0 { + return errors.New("Release is in a pending (locked) state and cannot be modified. User intervention is required.") + } + if rel.Info == nil || rel.Info.LastDeployed.IsZero() { + return errors.New("Release is in a pending (locked) state and lacks 'last deployed' timestamp. User intervention is required.") + } + if pendingSince := time.Since(rel.Info.LastDeployed.Time); pendingSince < r.markFailedAfter { + return fmt.Errorf("Release is in a pending (locked) state and cannot currently be modified. Release will be marked failed to allow a roll-forward in %v.", r.markFailedAfter-pendingSince) + } + + log.Info("Marking release as failed", "releaseName", rel.Name) + err := actionClient.MarkFailed(rel, fmt.Sprintf("operator marked pending (locked) release as failed after state did not change for %v", r.markFailedAfter)) + if err != nil { + return fmt.Errorf("Failed to mark pending (locked) release as failed: %w", err) + } + return fmt.Errorf("marked release %s as failed to allow upgrade to succeed in next reconcile attempt", rel.Name) +} + func (r *Reconciler) reportOverrideEvents(obj runtime.Object) { for k, v := range r.overrideValues { r.eventRecorder.Eventf(obj, "Warning", "ValueOverridden", From a1569be27b20dd13da7bd39c6ec9710b92d4c65a Mon Sep 17 00:00:00 2001 From: Malte Isberner <2822367+misberner@users.noreply.github.com> Date: Thu, 1 Jul 2021 12:01:09 +0200 Subject: [PATCH 06/10] Allow stripping manifest from the CR status (#18) --- pkg/reconciler/reconciler.go | 26 +++++++++++++++++++++++--- pkg/reconciler/reconciler_test.go | 10 ++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/pkg/reconciler/reconciler.go b/pkg/reconciler/reconciler.go index 5fac798..44e6c8a 100644 --- a/pkg/reconciler/reconciler.go +++ b/pkg/reconciler/reconciler.go @@ -86,6 +86,8 @@ type Reconciler struct { maxHistory int skipPrimaryGVKSchemeRegistration bool + stripManifestFromStatus bool + annotSetupOnce sync.Once annotations map[string]struct{} installAnnotations map[string]annotation.Install @@ -266,6 +268,17 @@ func SkipDependentWatches(skip bool) Option { } } +// StripManifestFromStatus is an Option that configures whether the manifest +// should be removed from the automatically populated status. +// This is recommended if the manifest might return sensitive data (i.e., +// secrets). +func StripManifestFromStatus(strip bool) Option { + return func(r *Reconciler) error { + r.stripManifestFromStatus = strip + return nil + } +} + // SkipPrimaryGVKSchemeRegistration is an Option that allows to disable the default behaviour of // registering unstructured.Unstructured as underlying type for the GVK scheme. // @@ -606,7 +619,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (res ctrl. if errors.Is(err, driver.ErrReleaseNotFound) { u.UpdateStatus(updater.EnsureCondition(conditions.Deployed(corev1.ConditionFalse, "", ""))) } else if err == nil { - ensureDeployedRelease(&u, rel) + r.ensureDeployedRelease(&u, rel) } u.UpdateStatus(updater.EnsureCondition(conditions.Initialized(corev1.ConditionTrue, "", ""))) @@ -693,7 +706,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (res ctrl. } } - ensureDeployedRelease(&u, rel) + r.ensureDeployedRelease(&u, rel) u.UpdateStatus( updater.EnsureCondition(conditions.ReleaseFailed(corev1.ConditionFalse, "", "")), updater.EnsureCondition(conditions.Irreconcilable(corev1.ConditionFalse, "", "")), @@ -1049,7 +1062,7 @@ func (r *Reconciler) setupWatches(mgr ctrl.Manager, c controller.Controller) err return nil } -func ensureDeployedRelease(u *updater.Updater, rel *release.Release) { +func (r *Reconciler) ensureDeployedRelease(u *updater.Updater, rel *release.Release) { reason := conditions.ReasonInstallSuccessful message := "release was successfully installed" if rel.Version > 1 { @@ -1059,6 +1072,13 @@ func ensureDeployedRelease(u *updater.Updater, rel *release.Release) { if rel.Info != nil && len(rel.Info.Notes) > 0 { message = rel.Info.Notes } + + if r.stripManifestFromStatus { + relCopy := *rel + relCopy.Manifest = "" + rel = &relCopy + } + u.Update(updater.EnsureFinalizer(uninstallFinalizer)) u.UpdateStatus( updater.EnsureCondition(conditions.Deployed(corev1.ConditionTrue, reason, message)), diff --git a/pkg/reconciler/reconciler_test.go b/pkg/reconciler/reconciler_test.go index ca37d8b..ab01bd9 100644 --- a/pkg/reconciler/reconciler_test.go +++ b/pkg/reconciler/reconciler_test.go @@ -191,6 +191,16 @@ var _ = Describe("Reconciler", func() { Expect(r.skipDependentWatches).To(Equal(true)) }) }) + var _ = Describe("StripManifestFromStatus", func() { + It("should set to false", func() { + Expect(StripManifestFromStatus(false)(r)).To(Succeed()) + Expect(r.stripManifestFromStatus).To(Equal(false)) + }) + It("should set to true", func() { + Expect(StripManifestFromStatus(true)(r)).To(Succeed()) + Expect(r.stripManifestFromStatus).To(Equal(true)) + }) + }) var _ = Describe("WithMaxConcurrentReconciles", func() { It("should set the reconciler max concurrent reconciled", func() { Expect(WithMaxConcurrentReconciles(1)(r)).To(Succeed()) From f538f7316b6b7bd6c9d74b4ac14576dfed3b4928 Mon Sep 17 00:00:00 2001 From: Malte Isberner <2822367+misberner@users.noreply.github.com> Date: Tue, 6 Jul 2021 02:52:54 +0200 Subject: [PATCH 07/10] ROX-7242: Make the operator preserve custom statuses, and allow updating custom status through extensions (#17) --- pkg/extensions/types.go | 7 +- pkg/reconciler/internal/updater/updater.go | 39 +++++++++-- .../internal/updater/updater_test.go | 70 ++++++++++++++++++- pkg/reconciler/reconciler.go | 6 +- 4 files changed, 111 insertions(+), 11 deletions(-) diff --git a/pkg/extensions/types.go b/pkg/extensions/types.go index 689f91d..fe449f8 100644 --- a/pkg/extensions/types.go +++ b/pkg/extensions/types.go @@ -2,11 +2,16 @@ package extensions import ( "context" + "github.com/go-logr/logr" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) +// UpdateStatusFunc is a function that updates an unstructured status. If the status has been modified, +// true must be returned, false otherwise. +type UpdateStatusFunc func(*unstructured.Unstructured) bool + // ReconcileExtension is an arbitrary extension that can be implemented to run either before // or after the main Helm reconciliation action. // An error returned by a ReconcileExtension will cause the Reconcile to fail, unlike a hook error. -type ReconcileExtension func(context.Context, *unstructured.Unstructured, logr.Logger) error +type ReconcileExtension func(context.Context, *unstructured.Unstructured, func(UpdateStatusFunc), logr.Logger) error diff --git a/pkg/reconciler/internal/updater/updater.go b/pkg/reconciler/internal/updater/updater.go index f55c79a..9e66930 100644 --- a/pkg/reconciler/internal/updater/updater.go +++ b/pkg/reconciler/internal/updater/updater.go @@ -26,6 +26,7 @@ import ( "k8s.io/client-go/util/retry" "sigs.k8s.io/controller-runtime/pkg/client" + "github.com/operator-framework/helm-operator/pkg/extensions" "github.com/operator-framework/helm-operator-plugins/internal/sdk/controllerutil" "github.com/operator-framework/helm-operator-plugins/pkg/internal/status" ) @@ -53,6 +54,21 @@ func (u *Updater) UpdateStatus(fs ...UpdateStatusFunc) { u.updateStatusFuncs = append(u.updateStatusFuncs, fs...) } +func (u *Updater) UpdateStatusCustom(f extensions.UpdateStatusFunc) { + updateFn := func(status *helmAppStatus) bool { + status.updateStatusObject() + + unstructuredStatus := unstructured.Unstructured{Object: status.StatusObject} + if !f(&unstructuredStatus) { + return false + } + _ = runtime.DefaultUnstructuredConverter.FromUnstructured(unstructuredStatus.Object, status) + status.StatusObject = unstructuredStatus.Object + return true + } + u.UpdateStatus(updateFn) +} + func (u *Updater) Apply(ctx context.Context, obj *unstructured.Unstructured) error { backoff := retry.DefaultRetry @@ -66,11 +82,8 @@ func (u *Updater) Apply(ctx context.Context, obj *unstructured.Unstructured) err needsStatusUpdate = f(st) || needsStatusUpdate } if needsStatusUpdate { - uSt, err := runtime.DefaultUnstructuredConverter.ToUnstructured(st) - if err != nil { - return err - } - obj.Object["status"] = uSt + st.updateStatusObject() + obj.Object["status"] = st.StatusObject return u.client.Status().Update(ctx, obj) } return nil @@ -149,10 +162,25 @@ func RemoveDeployedRelease() UpdateStatusFunc { } type helmAppStatus struct { + StatusObject map[string]interface{} `json:"-"` + Conditions status.Conditions `json:"conditions"` DeployedRelease *helmAppRelease `json:"deployedRelease,omitempty"` } +func (s *helmAppStatus) updateStatusObject() { + unstructuredHelmAppStatus, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(s) + if s.StatusObject == nil { + s.StatusObject = make(map[string]interface{}) + } + s.StatusObject["conditions"] = unstructuredHelmAppStatus["conditions"] + if deployedRelease := unstructuredHelmAppStatus["deployedRelease"]; deployedRelease != nil { + s.StatusObject["deployedRelease"] = deployedRelease + } else { + delete(s.StatusObject, "deployedRelease") + } +} + type helmAppRelease struct { Name string `json:"name,omitempty"` Manifest string `json:"manifest,omitempty"` @@ -175,6 +203,7 @@ func statusFor(obj *unstructured.Unstructured) *helmAppStatus { case map[string]interface{}: out := &helmAppStatus{} _ = runtime.DefaultUnstructuredConverter.FromUnstructured(s, out) + out.StatusObject = s return out default: return &helmAppStatus{} diff --git a/pkg/reconciler/internal/updater/updater_test.go b/pkg/reconciler/internal/updater/updater_test.go index 74569cd..7128d6e 100644 --- a/pkg/reconciler/internal/updater/updater_test.go +++ b/pkg/reconciler/internal/updater/updater_test.go @@ -86,6 +86,71 @@ var _ = Describe("Updater", func() { Expect((obj.Object["status"].(map[string]interface{}))["conditions"]).To(HaveLen(1)) Expect(obj.GetResourceVersion()).NotTo(Equal(resourceVersion)) }) + + It("should support a mix of standard and custom status updates", func() { + u.UpdateStatus(EnsureCondition(conditions.Deployed(corev1.ConditionTrue, "", ""))) + u.UpdateStatusCustom(func(uSt *unstructured.Unstructured) bool { + Expect(unstructured.SetNestedMap(uSt.Object, map[string]interface{}{"bar": "baz"}, "foo")).To(Succeed()) + return true + }) + u.UpdateStatus(EnsureCondition(conditions.Irreconcilable(corev1.ConditionFalse, "", ""))) + u.UpdateStatusCustom(func(uSt *unstructured.Unstructured) bool { + Expect(unstructured.SetNestedField(uSt.Object, "quux", "foo", "qux")).To(Succeed()) + return true + }) + u.UpdateStatus(EnsureCondition(conditions.Initialized(corev1.ConditionTrue, "", ""))) + + Expect(u.Apply(context.TODO(), obj)).To(Succeed()) + Expect(client.Get(context.TODO(), types.NamespacedName{Namespace: "testNamespace", Name: "testDeployment"}, obj)).To(Succeed()) + Expect((obj.Object["status"].(map[string]interface{}))["conditions"]).To(HaveLen(3)) + _, found, err := unstructured.NestedFieldNoCopy(obj.Object, "status", "deployedRelease") + Expect(found).To(BeFalse()) + Expect(err).To(Not(HaveOccurred())) + + val, found, err := unstructured.NestedString(obj.Object, "status", "foo", "bar") + Expect(val).To(Equal("baz")) + Expect(found).To(BeTrue()) + Expect(err).To(Not(HaveOccurred())) + + val, found, err = unstructured.NestedString(obj.Object, "status", "foo", "qux") + Expect(val).To(Equal("quux")) + Expect(found).To(BeTrue()) + Expect(err).To(Not(HaveOccurred())) + }) + + It("should preserve any custom status across multiple apply calls", func() { + u.UpdateStatusCustom(func(uSt *unstructured.Unstructured) bool { + Expect(unstructured.SetNestedMap(uSt.Object, map[string]interface{}{"bar": "baz"}, "foo")).To(Succeed()) + return true + }) + Expect(u.Apply(context.TODO(), obj)).To(Succeed()) + + Expect(client.Get(context.TODO(), types.NamespacedName{Namespace: "testNamespace", Name: "testDeployment"}, obj)).To(Succeed()) + + _, found, err := unstructured.NestedFieldNoCopy(obj.Object, "status", "deployedRelease") + Expect(found).To(BeFalse()) + Expect(err).To(Not(HaveOccurred())) + + val, found, err := unstructured.NestedString(obj.Object, "status", "foo", "bar") + Expect(val).To(Equal("baz")) + Expect(found).To(BeTrue()) + Expect(err).To(Succeed()) + + u.UpdateStatus(EnsureCondition(conditions.Deployed(corev1.ConditionTrue, "", ""))) + Expect(u.Apply(context.TODO(), obj)).To(Succeed()) + + Expect(client.Get(context.TODO(), types.NamespacedName{Namespace: "testNamespace", Name: "testDeployment"}, obj)).To(Succeed()) + Expect((obj.Object["status"].(map[string]interface{}))["conditions"]).To(HaveLen(1)) + + _, found, err = unstructured.NestedFieldNoCopy(obj.Object, "status", "deployedRelease") + Expect(found).To(BeFalse()) + Expect(err).To(Not(HaveOccurred())) + + val, found, err = unstructured.NestedString(obj.Object, "status", "foo", "bar") + Expect(val).To(Equal("baz")) + Expect(found).To(BeTrue()) + Expect(err).To(Succeed()) + }) }) }) @@ -241,8 +306,9 @@ var _ = Describe("statusFor", func() { }) It("should handle map[string]interface{}", func() { - obj.Object["status"] = map[string]interface{}{} - Expect(statusFor(obj)).To(Equal(&helmAppStatus{})) + uSt := map[string]interface{}{} + obj.Object["status"] = uSt + Expect(statusFor(obj)).To(Equal(&helmAppStatus{StatusObject: uSt})) }) It("should handle arbitrary types", func() { diff --git a/pkg/reconciler/reconciler.go b/pkg/reconciler/reconciler.go index 44e6c8a..07649d0 100644 --- a/pkg/reconciler/reconciler.go +++ b/pkg/reconciler/reconciler.go @@ -624,7 +624,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (res ctrl. u.UpdateStatus(updater.EnsureCondition(conditions.Initialized(corev1.ConditionTrue, "", ""))) for _, ext := range r.preExtensions { - if err := ext(ctx, obj, r.log); err != nil { + if err := ext(ctx, obj, u.UpdateStatusCustom, r.log); err != nil { u.UpdateStatus( updater.EnsureCondition(conditions.Irreconcilable(corev1.ConditionTrue, conditions.ReasonReconcileError, err)), updater.EnsureConditionUnknown(conditions.TypeReleaseFailed), @@ -697,7 +697,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (res ctrl. } for _, ext := range r.postExtensions { - if err := ext(ctx, obj, r.log); err != nil { + if err := ext(ctx, obj, u.UpdateStatusCustom, r.log); err != nil { u.UpdateStatus( updater.EnsureCondition(conditions.Irreconcilable(corev1.ConditionTrue, conditions.ReasonReconcileError, err)), updater.EnsureConditionUnknown(conditions.TypeReleaseFailed), @@ -967,7 +967,7 @@ func (r *Reconciler) doUninstall(ctx context.Context, actionClient helmclient.Ac } for _, ext := range r.postExtensions { - if err := ext(ctx, obj, r.log); err != nil { + if err := ext(ctx, obj, u.UpdateStatusCustom, r.log); err != nil { u.UpdateStatus( updater.EnsureCondition(conditions.Irreconcilable(corev1.ConditionTrue, conditions.ReasonReconcileError, err)), updater.EnsureConditionUnknown(conditions.TypeReleaseFailed), From 3c9e47ea62053d7efc25ffbb881b09250f30af4f Mon Sep 17 00:00:00 2001 From: Marcin Owsiany Date: Tue, 14 Dec 2021 06:55:05 +0100 Subject: [PATCH 08/10] ROX- 8130: Add WithExtraWatch option. (#22) --- pkg/reconciler/reconciler.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/pkg/reconciler/reconciler.go b/pkg/reconciler/reconciler.go index 07649d0..d5bcd34 100644 --- a/pkg/reconciler/reconciler.go +++ b/pkg/reconciler/reconciler.go @@ -43,6 +43,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/predicate" + "sigs.k8s.io/controller-runtime/pkg/predicate" ctrlpredicate "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/source" @@ -80,6 +81,7 @@ type Reconciler struct { selectorPredicate predicate.Predicate overrideValues map[string]string skipDependentWatches bool + extraWatches []watchDescription maxConcurrentReconciles int reconcilePeriod time.Duration markFailedAfter time.Duration @@ -95,6 +97,12 @@ type Reconciler struct { uninstallAnnotations map[string]annotation.Uninstall } +type watchDescription struct { + src source.Source + predicates []predicate.Predicate + handler handler.EventHandler +} + // New creates a new Reconciler that reconciles custom resources that define a // Helm release. New takes variadic Option arguments that are used to configure // the Reconciler. @@ -526,6 +534,22 @@ func WithValueMapper(m values.Mapper) Option { } } +// WithExtraWatch is an Option that adds an extra event watch. +// Use this if you want your controller to respond to events other than coming from the primary custom resource, +// the helm release secret, or resources created by your helm chart. +// The meaning of the arguments is the same as for sigs.k8s.io/controller-runtime/pkg/controller.Controller Watch +// function. +func WithExtraWatch(src source.Source, handler handler.EventHandler, predicates ...predicate.Predicate) Option { + return func(r *Reconciler) error { + r.extraWatches = append(r.extraWatches, watchDescription{ + src: src, + predicates: predicates, + handler: handler, + }) + return nil + } +} + // WithSelector is an Option that configures the reconciler to creates a // predicate that is used to filter resources based on the specified selector func WithSelector(s metav1.LabelSelector) Option { @@ -1056,6 +1080,12 @@ func (r *Reconciler) setupWatches(mgr ctrl.Manager, c controller.Controller) err return err } + for _, w := range r.extraWatches { + if err := c.Watch(w.src, w.handler, w.predicates...); err != nil { + return err + } + } + if !r.skipDependentWatches { r.postHooks = append([]hook.PostHook{internalhook.NewDependentResourceWatcher(c, mgr.GetRESTMapper())}, r.postHooks...) } From 8746db12d78e0b4494d55151b2a307359ec878ed Mon Sep 17 00:00:00 2001 From: Simon Baeumer Date: Thu, 5 May 2022 09:23:24 +0200 Subject: [PATCH 09/10] Fix updater test to use existing fields from deployment's status field --- pkg/reconciler/internal/updater/updater.go | 2 +- .../internal/updater/updater_test.go | 28 +++++++++++-------- pkg/reconciler/reconciler.go | 7 ++--- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/pkg/reconciler/internal/updater/updater.go b/pkg/reconciler/internal/updater/updater.go index 9e66930..b1e09fd 100644 --- a/pkg/reconciler/internal/updater/updater.go +++ b/pkg/reconciler/internal/updater/updater.go @@ -26,8 +26,8 @@ import ( "k8s.io/client-go/util/retry" "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/operator-framework/helm-operator/pkg/extensions" "github.com/operator-framework/helm-operator-plugins/internal/sdk/controllerutil" + "github.com/operator-framework/helm-operator-plugins/pkg/extensions" "github.com/operator-framework/helm-operator-plugins/pkg/internal/status" ) diff --git a/pkg/reconciler/internal/updater/updater_test.go b/pkg/reconciler/internal/updater/updater_test.go index 7128d6e..7b7791d 100644 --- a/pkg/reconciler/internal/updater/updater_test.go +++ b/pkg/reconciler/internal/updater/updater_test.go @@ -32,7 +32,11 @@ import ( "github.com/operator-framework/helm-operator-plugins/pkg/reconciler/internal/conditions" ) -const testFinalizer = "testFinalizer" +const ( + testFinalizer = "testFinalizer" + availableReplicasStatus = int64(3) + replicasStatus = int64(5) +) var _ = Describe("Updater", func() { var ( @@ -90,12 +94,12 @@ var _ = Describe("Updater", func() { It("should support a mix of standard and custom status updates", func() { u.UpdateStatus(EnsureCondition(conditions.Deployed(corev1.ConditionTrue, "", ""))) u.UpdateStatusCustom(func(uSt *unstructured.Unstructured) bool { - Expect(unstructured.SetNestedMap(uSt.Object, map[string]interface{}{"bar": "baz"}, "foo")).To(Succeed()) + Expect(unstructured.SetNestedField(uSt.Object, replicasStatus, "replicas")).To(Succeed()) return true }) u.UpdateStatus(EnsureCondition(conditions.Irreconcilable(corev1.ConditionFalse, "", ""))) u.UpdateStatusCustom(func(uSt *unstructured.Unstructured) bool { - Expect(unstructured.SetNestedField(uSt.Object, "quux", "foo", "qux")).To(Succeed()) + Expect(unstructured.SetNestedField(uSt.Object, availableReplicasStatus, "availableReplicas")).To(Succeed()) return true }) u.UpdateStatus(EnsureCondition(conditions.Initialized(corev1.ConditionTrue, "", ""))) @@ -107,20 +111,20 @@ var _ = Describe("Updater", func() { Expect(found).To(BeFalse()) Expect(err).To(Not(HaveOccurred())) - val, found, err := unstructured.NestedString(obj.Object, "status", "foo", "bar") - Expect(val).To(Equal("baz")) + val, found, err := unstructured.NestedInt64(obj.Object, "status", "replicas") + Expect(val).To(Equal(replicasStatus)) Expect(found).To(BeTrue()) Expect(err).To(Not(HaveOccurred())) - val, found, err = unstructured.NestedString(obj.Object, "status", "foo", "qux") - Expect(val).To(Equal("quux")) + val, found, err = unstructured.NestedInt64(obj.Object, "status", "availableReplicas") + Expect(val).To(Equal(availableReplicasStatus)) Expect(found).To(BeTrue()) Expect(err).To(Not(HaveOccurred())) }) It("should preserve any custom status across multiple apply calls", func() { u.UpdateStatusCustom(func(uSt *unstructured.Unstructured) bool { - Expect(unstructured.SetNestedMap(uSt.Object, map[string]interface{}{"bar": "baz"}, "foo")).To(Succeed()) + Expect(unstructured.SetNestedField(uSt.Object, int64(5), "replicas")).To(Succeed()) return true }) Expect(u.Apply(context.TODO(), obj)).To(Succeed()) @@ -131,8 +135,8 @@ var _ = Describe("Updater", func() { Expect(found).To(BeFalse()) Expect(err).To(Not(HaveOccurred())) - val, found, err := unstructured.NestedString(obj.Object, "status", "foo", "bar") - Expect(val).To(Equal("baz")) + val, found, err := unstructured.NestedInt64(obj.Object, "status", "replicas") + Expect(val).To(Equal(replicasStatus)) Expect(found).To(BeTrue()) Expect(err).To(Succeed()) @@ -146,8 +150,8 @@ var _ = Describe("Updater", func() { Expect(found).To(BeFalse()) Expect(err).To(Not(HaveOccurred())) - val, found, err = unstructured.NestedString(obj.Object, "status", "foo", "bar") - Expect(val).To(Equal("baz")) + val, found, err = unstructured.NestedInt64(obj.Object, "status", "replicas") + Expect(val).To(Equal(replicasStatus)) Expect(found).To(BeTrue()) Expect(err).To(Succeed()) }) diff --git a/pkg/reconciler/reconciler.go b/pkg/reconciler/reconciler.go index d5bcd34..b5acd51 100644 --- a/pkg/reconciler/reconciler.go +++ b/pkg/reconciler/reconciler.go @@ -43,13 +43,13 @@ import ( "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/predicate" - "sigs.k8s.io/controller-runtime/pkg/predicate" ctrlpredicate "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/source" "github.com/operator-framework/helm-operator-plugins/internal/sdk/controllerutil" "github.com/operator-framework/helm-operator-plugins/pkg/annotation" helmclient "github.com/operator-framework/helm-operator-plugins/pkg/client" + "github.com/operator-framework/helm-operator-plugins/pkg/extensions" "github.com/operator-framework/helm-operator-plugins/pkg/hook" "github.com/operator-framework/helm-operator-plugins/pkg/reconciler/internal/conditions" "github.com/operator-framework/helm-operator-plugins/pkg/reconciler/internal/diff" @@ -57,7 +57,6 @@ import ( "github.com/operator-framework/helm-operator-plugins/pkg/reconciler/internal/updater" internalvalues "github.com/operator-framework/helm-operator-plugins/pkg/reconciler/internal/values" "github.com/operator-framework/helm-operator-plugins/pkg/values" - "github.com/joelanford/helm-operator/pkg/extensions" ) const uninstallFinalizer = "uninstall-helm-release" @@ -81,10 +80,10 @@ type Reconciler struct { selectorPredicate predicate.Predicate overrideValues map[string]string skipDependentWatches bool - extraWatches []watchDescription + extraWatches []watchDescription maxConcurrentReconciles int reconcilePeriod time.Duration - markFailedAfter time.Duration + markFailedAfter time.Duration maxHistory int skipPrimaryGVKSchemeRegistration bool From 07aae8a0505b6b014d36c6471984a61ae69c6fd9 Mon Sep 17 00:00:00 2001 From: Moritz Clasmeier Date: Wed, 8 Jun 2022 09:28:09 +0200 Subject: [PATCH 10/10] Fix reconciler log format message (#25) Fix log format string interpolation --- pkg/reconciler/reconciler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/reconciler/reconciler.go b/pkg/reconciler/reconciler.go index b5acd51..51bf61d 100644 --- a/pkg/reconciler/reconciler.go +++ b/pkg/reconciler/reconciler.go @@ -594,7 +594,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (res ctrl. obj.SetGroupVersionKind(*r.gvk) err = r.client.Get(ctx, req.NamespacedName, obj) if apierrors.IsNotFound(err) { - log.V(1).Info("Resource %s/%s not found, nothing to do", req.NamespacedName.Namespace, req.NamespacedName.Name) + log.V(1).Info(fmt.Sprintf("Resource %s/%s not found, nothing to do", req.NamespacedName.Namespace, req.NamespacedName.Name)) return ctrl.Result{}, nil } if err != nil {