Skip to content

Commit

Permalink
tiltfile: helm() should return CRDs in the new helm3 format. Fixes #3605
Browse files Browse the repository at this point in the history
  • Loading branch information
nicks committed Jul 28, 2020
1 parent 672b4fe commit da6662d
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 7 deletions.
52 changes: 51 additions & 1 deletion internal/tiltfile/files.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ import (
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"

"github.com/tilt-dev/tilt/internal/k8s"
tiltfile_io "github.com/tilt-dev/tilt/internal/tiltfile/io"
Expand Down Expand Up @@ -180,7 +183,9 @@ func (s *tiltfileState) helm(thread *starlark.Thread, fn *starlark.Builtin, args
name = "chart"
}

if version == helmV3 {
if version == helmV3_1andAbove {
cmd = []string{"helm", "template", name, localPath, "--include-crds"}
} else if version == helmV3_0 {
cmd = []string{"helm", "template", name, localPath}
} else {
cmd = []string{"helm", "template", localPath, "--name", name}
Expand Down Expand Up @@ -214,6 +219,16 @@ func (s *tiltfileState) helm(thread *starlark.Thread, fn *starlark.Builtin, args

yaml := filterHelmTestYAML(string(stdout))

if version == helmV3_0 {
// Helm v3.0 has a bug where it doesn't include CRDs in the template output
// https://github.com/tilt-dev/tilt/issues/3605
crds, err := getHelmCRDs(localPath)
if err != nil {
return nil, err
}
yaml = strings.Join(append([]string{yaml}, crds...), "\n---\n")
}

if namespace != "" {
// helm template --namespace doesn't inject the namespace, nor provide
// YAML that defines the namespace, so we have to do both ourselves :\
Expand All @@ -235,3 +250,38 @@ func (s *tiltfileState) helm(thread *starlark.Thread, fn *starlark.Builtin, args

return tiltfile_io.NewBlob(yaml, fmt.Sprintf("helm: %s", localPath)), nil
}

// NOTE(nick): This isn't perfect. For example, it doesn't handle chart deps
// properly. When possible, prefer Helm 3.1's --include-crds
func getHelmCRDs(path string) ([]string, error) {
crdPath := filepath.Join(path, "crds")
result := []string{}
err := filepath.Walk(crdPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
isYAML := info != nil && info.Mode().IsRegular() && hasYAMLExtension(path)
if !isYAML {
return nil
}
contents, err := ioutil.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
return nil
}
return err
}
result = append(result, string(contents))
return nil
})

if err != nil && !os.IsNotExist(err) {
return nil, err
}
return result, nil
}

func hasYAMLExtension(fname string) bool {
ext := filepath.Ext(fname)
return strings.EqualFold(ext, ".yaml") || strings.EqualFold(ext, ".yml")
}
9 changes: 6 additions & 3 deletions internal/tiltfile/helm.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,15 @@ type helmVersion int
const (
unknownHelmVersion helmVersion = iota
helmV2
helmV3
helmV3_0
helmV3_1andAbove
)

func parseVersion(version string) helmVersion {
if strings.HasPrefix(version, "v3") {
return helmV3
if strings.HasPrefix(version, "v3.0.") {
return helmV3_0
} else if strings.HasPrefix(version, "v3.") {
return helmV3_1andAbove
} else if strings.HasPrefix(version, "Client: v2") {
return helmV2
}
Expand Down
66 changes: 63 additions & 3 deletions internal/tiltfile/helm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ k8s_yaml(yml)
}

const exampleHelmV2VersionOutput = `Client: v2.12.3geecf22f`
const exampleHelmV3VersionOutput = `v3.0.0`
const exampleHelmV3_0VersionOutput = `v3.0.0`
const exampleHelmV3_1VersionOutput = `v3.1.0`
const exampleHelmV3_2VersionOutput = `v3.2.4`

func TestParseHelmV2Version(t *testing.T) {
expected := helmV2
Expand All @@ -66,8 +68,22 @@ func TestParseHelmV2Version(t *testing.T) {
}

func TestParseHelmV3Version(t *testing.T) {
expected := helmV3
actual := parseVersion(exampleHelmV3VersionOutput)
expected := helmV3_0
actual := parseVersion(exampleHelmV3_0VersionOutput)

assert.Equal(t, expected, actual)
}

func TestParseHelmV3_1Version(t *testing.T) {
expected := helmV3_1andAbove
actual := parseVersion(exampleHelmV3_1VersionOutput)

assert.Equal(t, expected, actual)
}

func TestParseHelmV3_2Version(t *testing.T) {
expected := helmV3_1andAbove
actual := parseVersion(exampleHelmV3_2VersionOutput)

assert.Equal(t, expected, actual)
}
Expand Down Expand Up @@ -139,3 +155,47 @@ k8s_yaml(helm('./helm'))
assert.NotContains(t, yaml, "RELEASE-NAME")
assert.Contains(t, yaml, "name: chart-grafana")
}

func TestHelm3CRD(t *testing.T) {
f := newFixture(t)
defer f.TearDown()

f.file("helm/Chart.yaml", `apiVersion: v1
description: crd chart
name: crd
version: 0.1.0`)

f.file("helm/templates/service-account.yaml", `apiVersion: v1
kind: ServiceAccount
metadata:
name: crd-sa`)

// Only works in Helm3
// https://helm.sh/docs/chart_best_practices/custom_resource_definitions/
f.file("helm/crds/um.yaml", `apiVersion: tilt.dev/v1alpha1
kind: UselessMachine
metadata:
name: bobo
spec:
image: bobo`)

f.file("Tiltfile", `
k8s_yaml(helm('./helm'))
`)

f.load()

manifests := f.loadResult.Manifests
require.Equal(t, 1, len(manifests))

m := manifests[0]
yaml := m.K8sTarget().YAML
v, err := getHelmVersion()
assert.NoError(t, err)
assert.Contains(t, yaml, "kind: ServiceAccount")
if v == helmV3_0 || v == helmV3_1andAbove {
assert.Contains(t, yaml, "kind: UselessMachine")
} else {
assert.NotContains(t, yaml, "kind: UselessMachine")
}
}

0 comments on commit da6662d

Please sign in to comment.