Skip to content

Add a WithValueTranslator option to Reconciller. #6

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions pkg/reconciler/internal/values/values.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,11 @@ func (v *Values) ApplyOverrides(in map[string]string) error {
}

var DefaultMapper = values.MapperFunc(func(v chartutil.Values) chartutil.Values { return v })

var DefaultTranslator = values.TranslatorFunc(func(u *unstructured.Unstructured) (chartutil.Values, error) {
internalValues, err := FromUnstructured(u)
if err != nil {
return chartutil.Values{}, err
}
return internalValues.Map(), err
})
23 changes: 20 additions & 3 deletions pkg/reconciler/reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ const uninstallFinalizer = "uninstall-helm-release"
type Reconciler struct {
client client.Client
actionClientGetter helmclient.ActionClientGetter
valueTranslator values.Translator
valueMapper values.Mapper
eventRecorder record.EventRecorder
preHooks []hook.PreHook
Expand Down Expand Up @@ -362,8 +363,20 @@ func WithPostHook(h hook.PostHook) Option {
}
}

// 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.
func WithValueTranslator(t values.Translator) Option {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you mention the difference to the Mapper? It could get confusing as they have similar use-cases.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, reading this PR I don't really understand why a custom translator is required in addition to the mapper. Things that come to mind:

  • access to metadata (name, ...) and status fields
  • ability to use Go types by converting the *unstructured.Unstructured into the Go type for a CR.

However, having translator + mapper seems confusing. Perspectively, I think it would make more sense to introduce translator as the first-class citizen, and drop the valueMapper field. WithValueMapper could then return a function that sets the valueTranslator accordingly (relying on FromUnstructured).

Or am I completely off, and the difference is actually whether it gets executed pre/post the application of overrides?

Copy link
Collaborator Author

@porridge porridge May 24, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SimonBaeumer added a couple of sentences.

@misberner the rationale is in the PR description:

This lets the user inject code which produces helm values based on the fetched object itself (unlike Mapper which can only see Values).
This way the custom code can convert the object to a typed one and work with proper structs rather than a tree of maps to interface{}.

Basically it is so that we can have write type-safe code at least to read the CRs.

I'd agree about dropping the Mapper API eventually, but:

  • it's out of scope of this PR, I'm trying to diverge from upstream as little as possible,
  • in general it would need more discussion with upstream, for example there are also these ApplyOverrides and CoalesceValues steps in getValues which I don't fully understand, so there might be use cases where Mapper is a better tool

return func(r *Reconciler) error {
r.valueTranslator = t
return nil
}
}

// WithValueMapper is an Option that configures a function that maps values
// from a custom resource spec to the values passed to Helm
// from a custom resource spec to the values passed to Helm.
// Use this if you want to apply a transformation on the values obtained from your custom resource, before
// they are passed to Helm.
func WithValueMapper(m values.Mapper) Option {
return func(r *Reconciler) error {
r.valueMapper = m
Expand Down Expand Up @@ -522,14 +535,15 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (res ctrl.
}

func (r *Reconciler) getValues(obj *unstructured.Unstructured) (chartutil.Values, error) {
crVals, err := internalvalues.FromUnstructured(obj)
vals, err := r.valueTranslator.Translate(obj)
if err != nil {
return chartutil.Values{}, err
}
crVals := internalvalues.New(vals)
if err := crVals.ApplyOverrides(r.overrideValues); err != nil {
return chartutil.Values{}, err
}
vals := r.valueMapper.Map(crVals.Map())
vals = r.valueMapper.Map(crVals.Map())
vals, err = chartutil.CoalesceValues(r.chrt, vals)
if err != nil {
return chartutil.Values{}, err
Expand Down Expand Up @@ -736,6 +750,9 @@ func (r *Reconciler) addDefaults(mgr ctrl.Manager, controllerName string) {
if r.eventRecorder == nil {
r.eventRecorder = mgr.GetEventRecorderFor(controllerName)
}
if r.valueTranslator == nil {
r.valueTranslator = internalvalues.DefaultTranslator
}
if r.valueMapper == nil {
r.valueMapper = internalvalues.DefaultMapper
}
Expand Down
13 changes: 13 additions & 0 deletions pkg/values/values.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@ package values

import (
"helm.sh/helm/v3/pkg/chartutil"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)

// TODO: Consider deprecating Mapper and overrides in favour of Translator.

type Mapper interface {
Map(chartutil.Values) chartutil.Values
}
Expand All @@ -29,3 +32,13 @@ type MapperFunc func(chartutil.Values) chartutil.Values
func (m MapperFunc) Map(v chartutil.Values) chartutil.Values {
return m(v)
}

type Translator interface {
Translate(unstructured *unstructured.Unstructured) (chartutil.Values, error)
}

type TranslatorFunc func(*unstructured.Unstructured) (chartutil.Values, error)

func (t TranslatorFunc) Translate(u *unstructured.Unstructured) (chartutil.Values, error) {
return t(u)
}