Skip to content
This repository has been archived by the owner on Jun 29, 2022. It is now read-only.

pkg/components/util: use Helm post-renderer instead of Manifests field #727

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
6 changes: 1 addition & 5 deletions pkg/components/util/helm.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
18 changes: 11 additions & 7 deletions pkg/components/util/helm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
}
Expand Down Expand Up @@ -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")
}
}
Expand All @@ -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")
}
}
Expand All @@ -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")
}

Expand Down
37 changes: 37 additions & 0 deletions pkg/components/util/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package util

import (
"bytes"
"context"
"fmt"

Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down