Skip to content

Commit

Permalink
feat(helm): Require Charts to be locally available (#370)
Browse files Browse the repository at this point in the history
This modifies the `helmTemplate` native function in a way that it
expectes a relative path instead of the `repo/name` identifier for
Charts.

Before, this feature relied on the system wide Helm cache, which is
independent from the Tanka project and not controlled by git at all.

This does not play well with out aim for hermeticity and strong
reproducibility.

To overcome this, `helmTemplate` resolves the chart **relative to the
calling file**. This suggests to keep Helm Charts as close to the
consuming source code as possible.
  • Loading branch information
sh0rez authored Sep 8, 2020
1 parent 6920b8c commit e578e5e
Showing 1 changed file with 29 additions and 5 deletions.
34 changes: 29 additions & 5 deletions pkg/helm/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"

jsonnet "github.com/google/go-jsonnet"
Expand All @@ -18,9 +19,14 @@ import (

// TemplateOpts defines additional parameters that can be passed to the
// Helm.Template action
// TODO: Isolate between Helm.Template and NativeFunc
type TemplateOpts struct {
Values map[string]interface{}
Flags []string
// general
Values map[string]interface{} `json:"values"`
Flags []string `json:"flags"`

// native func related
CalledFrom string `json:"calledFrom"`
}

func confToArgs(conf TemplateOpts) ([]string, []string, error) {
Expand Down Expand Up @@ -93,19 +99,23 @@ func (h ExecHelm) Template(name, chart string, opts TemplateOpts) (manifest.List
}

// NativeFunc returns a jsonnet native function that provides the same
// functionality as `Helm.Template` of this package
// functionality as `Helm.Template` of this package. Charts are required to be
// present on the local filesystem, at a relative location to the file that
// calls `helm.template()` / `std.native('helmTemplate')`. This guarantees
// hermeticity
func NativeFunc() *jsonnet.NativeFunction {
return &jsonnet.NativeFunction{
Name: "helmTemplate",
// Lines up with `helm template [NAME] [CHART] [flags]` except 'conf' is a bit more elaborate
// Similar to `helm template [NAME] [CHART] [flags]` except 'conf' is a
// bit more elaborate and chart is a local path
Params: ast.Identifiers{"name", "chart", "conf"},
Func: func(data []interface{}) (interface{}, error) {
name, ok := data[0].(string)
if !ok {
return nil, fmt.Errorf("First argument 'name' must be of 'string' type, got '%T' instead", data[0])
}

chart, ok := data[1].(string)
chartpath, ok := data[1].(string)
if !ok {
return nil, fmt.Errorf("Second argument 'chart' must be of 'string' type, got '%T' instead", data[1])
}
Expand All @@ -120,6 +130,20 @@ func NativeFunc() *jsonnet.NativeFunction {
return "", err
}

// Charts are only allowed at relative paths. Use conf.CalledFrom to find the callers directory
if conf.CalledFrom == "" {
// TODO: rephrase and move lengthy explanation to website
return nil, fmt.Errorf("helmTemplate: 'conf.calledFrom' is unset or empty.\nHowever, Tanka must know where helmTemplate was called from, to resolve the Helm Chart relative to that.\n")
}
callerDir := filepath.Dir(conf.CalledFrom)

// resolve the Chart relative to the caller
chart := filepath.Join(callerDir, chartpath)
if _, err := os.Stat(chart); err != nil {
// TODO: add website link for explanation
return nil, fmt.Errorf("helmTemplate: Failed to find a Chart at '%s': %s", chart, err)
}

// TODO: Define Template on the Helm interface instead
var h ExecHelm
list, err := h.Template(name, chart, conf)
Expand Down

0 comments on commit e578e5e

Please sign in to comment.