From 1ee645590f274bd80ba42e91a26f8e21e2381980 Mon Sep 17 00:00:00 2001 From: Mateusz Gozdek Date: Tue, 21 Jul 2020 23:50:12 +0200 Subject: [PATCH] pkg/components/util: use Helm post-renderer instead of Manifests field As Manifests filed in Chart object is a custom field, added to our fork of Helm. Now Helm offers post-renderer feature, which allows to further customize the manfiests after Helm's templating engine and before the installation of the manifests. This allows us to use post-renderer feature and drop one of maintained custom patches, which means less maintenance for us. Signed-off-by: Mateusz Gozdek --- pkg/components/util/helm.go | 6 +----- pkg/components/util/helm_test.go | 18 ++++++++++------ pkg/components/util/install.go | 37 ++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 12 deletions(-) diff --git a/pkg/components/util/helm.go b/pkg/components/util/helm.go index 573a8c302..52f5b2210 100644 --- a/pkg/components/util/helm.go +++ b/pkg/components/util/helm.go @@ -214,11 +214,7 @@ func chartFromManifests(metadata components.Metadata, manifests map[string]strin Name: p, } - // Apply rendered manifests to Manifests slice, which does not run through the rendering engine - // again when the chart is being installed. This is required, as some charts use complex escaping - // syntax, which breaks if the templates are evaluated twice. This, for example, breaks - // the prometheus-operator chart. - ch.Manifests = append(ch.Manifests, f) + ch.Templates = append(ch.Templates, f) } // If we collected any CRDs, put them in the special file in the dedicated crds/ directory. diff --git a/pkg/components/util/helm_test.go b/pkg/components/util/helm_test.go index 4c3f0d092..0acd1b791 100644 --- a/pkg/components/util/helm_test.go +++ b/pkg/components/util/helm_test.go @@ -106,11 +106,11 @@ metadata: t.Fatalf("Chart should be created, got: %v", err) } - if len(chart.Manifests) != 1 { //nolint:gomnd + if len(chart.Templates) != 1 { t.Fatalf("Manifest file with the namespace should still be added, as it may contain other objects") } - if len(chart.Manifests[0].Data) != 0 { + if len(chart.Templates[0].Data) != 0 { t.Fatalf("Namespace object should be removed from chart") } } @@ -140,7 +140,11 @@ metadata: t.Fatalf("Chart should be created, got: %v", err) } - if len(chart.Manifests[0].Data) == 0 { + if len(chart.Templates) != 1 { + t.Fatalf("templates should include exactly one object") + } + + if len(chart.Templates[0].Data) == 0 { t.Fatalf("Other objects should be retained in the file containing Namespace object") } } @@ -165,7 +169,7 @@ metadata: t.Fatalf("Chart should be created, got: %v", err) } - if len(chart.Manifests[0].Data) == 0 { + if len(chart.Templates[0].Data) == 0 { t.Fatalf("Only Namespace object with matching namespace name should be filtered") } } @@ -188,15 +192,15 @@ metadata: t.Fatalf("Chart should be created, got: %v", err) } - if len(chart.Manifests) != 1 { //nolint:gomnd + if len(chart.Templates) != 1 { t.Fatalf("Manifest file with the CRDs should still be added, as it may contain other objects") } - if len(chart.Manifests[0].Data) != 0 { + if len(chart.Templates[0].Data) != 0 { t.Fatalf("CRD object should be removed from the manifests file") } - if len(chart.Files) != 1 { //nolint:gomnd + if len(chart.Files) != 1 { t.Fatalf("CRD object should be added to Files field") } diff --git a/pkg/components/util/install.go b/pkg/components/util/install.go index 2b6b5e82e..1e23e8b31 100644 --- a/pkg/components/util/install.go +++ b/pkg/components/util/install.go @@ -15,6 +15,7 @@ package util import ( + "bytes" "context" "fmt" @@ -104,10 +105,45 @@ type helmAction struct { wait bool } +// postRender is a Lokomotive post-renderer for Helm, which allows bypassing Helm templating engine +// by copying manifests from Templates field and then including them into the chart. +// +// As we render original charts twice (once when we render manifests ourselves to be able to print them +// to the user and then again via Helm, when it installs/upgrades/uninstalls the release. +// +// This is required as some charts use complex escaping syntax, which breaks if the templates +// are evaluated twice. This, for example, breaks the prometheus-operator chart. +type postRender struct { + manifests bytes.Buffer +} + +// Run implements postrender.PostRenderer interface by ignoring given templates from Helm and returning +// manifests defined at creation time. +func (pr postRender) Run(renderedManifests *bytes.Buffer) (modifiedManifests *bytes.Buffer, err error) { + return &pr.manifests, nil +} + +// chartTemplatesToPostRender creates PostRenderer from given chart templates and resets +// chart's Templates field so it does not run twice trough rendering engine. +func chartTemplatesToPostRender(c *chart.Chart) postRender { + var buf bytes.Buffer + + for _, template := range c.Templates { + fmt.Fprintf(&buf, "\n---\n# %s\n%s", template.Name, template.Data) + } + + c.Templates = nil + + return postRender{ + manifests: buf, + } +} + func install(helmAction *helmAction, namespace string) error { install := action.NewInstall(helmAction.actionConfig) install.ReleaseName = helmAction.releaseName install.Namespace = namespace + install.PostRenderer = chartTemplatesToPostRender(helmAction.chart) // Currently, we install components one-by-one, in the order how they are // defined in the configuration and we do not support any dependencies between @@ -133,6 +169,7 @@ func upgrade(helmAction *helmAction) error { upgrade := action.NewUpgrade(helmAction.actionConfig) upgrade.Wait = helmAction.wait upgrade.RecreateResources = true + upgrade.PostRenderer = chartTemplatesToPostRender(helmAction.chart) if _, err := upgrade.Run(helmAction.releaseName, helmAction.chart, map[string]interface{}{}); err != nil { return fmt.Errorf("upgrading release failed: %w", err)