diff --git a/cmd/helm/template.go b/cmd/helm/template.go index 36fbec939..62cc05e26 100644 --- a/cmd/helm/template.go +++ b/cmd/helm/template.go @@ -42,35 +42,25 @@ import ( "k8s.io/helm/pkg/tiller/environment" ) -//const defaultDirectoryPermission = 0755 - -var ( - //whitespaceRegex = regexp.MustCompile(`^\s*$`) - - // defaultKubeVersion is the default value of --kube-version flag - defaultKubeVersion = fmt.Sprintf("%s.%s", chartutil.DefaultKubeVersion.Major, chartutil.DefaultKubeVersion.Minor) -) +// defaultKubeVersion is the default value of --kube-version flag +var defaultKubeVersion = fmt.Sprintf("%s.%s", chartutil.DefaultKubeVersion.Major, chartutil.DefaultKubeVersion.Minor) const templateDesc = ` Render chart templates locally and display the output. -This does not require Tiller. However, any values that would normally be -looked up or retrieved in-cluster will be faked locally. Additionally, none -of the server-side testing of chart validity (e.g. whether an API is supported) -is done. +This does not require a Kubernetes connection. However, any values that would normally +be retrieved in-cluster will be faked locally. Additionally, no validation is +performed on the resulting manifest files. As a result, there is no assurance that a +file generated from this command will be valid to Kubernetes. -To render just one template in a chart, use '-x': - - $ helm template mychart -x templates/deployment.yaml ` type templateOptions struct { - nameTemplate string // --name-template - showNotes bool // --notes - releaseName string // --name - renderFiles []string // --execute - kubeVersion string // --kube-version - outputDir string // --output-dir + nameTemplate string // --name-template + showNotes bool // --notes + releaseName string // --name + kubeVersion string // --kube-version + outputDir string // --output-dir valuesOptions @@ -101,7 +91,6 @@ func newTemplateCmd(out io.Writer) *cobra.Command { f := cmd.Flags() f.BoolVar(&o.showNotes, "notes", false, "show the computed NOTES.txt file as well") f.StringVarP(&o.releaseName, "name", "", "RELEASE-NAME", "release name") - f.StringArrayVarP(&o.renderFiles, "execute", "x", []string{}, "only execute the given templates") f.StringVar(&o.nameTemplate, "name-template", "", "specify template used to name the release") f.StringVar(&o.kubeVersion, "kube-version", defaultKubeVersion, "kubernetes version used as Capabilities.KubeVersion.Major/Minor") f.StringVar(&o.outputDir, "output-dir", "", "writes the executed templates to files in output-dir instead of stdout") @@ -111,28 +100,6 @@ func newTemplateCmd(out io.Writer) *cobra.Command { } func (o *templateOptions) run(out io.Writer) error { - // verify specified templates exist relative to chart - rf := []string{} - var af string - var err error - if len(o.renderFiles) > 0 { - for _, f := range o.renderFiles { - if !filepath.IsAbs(f) { - af, err = filepath.Abs(filepath.Join(o.chartPath, f)) - if err != nil { - return errors.Wrap(err, "could not resolve template path") - } - } else { - af = f - } - rf = append(rf, af) - - if _, err := os.Stat(af); err != nil { - return errors.Wrap(err, "could not resolve template path") - } - } - } - // get combined values and create config config, err := o.mergedValues() if err != nil { @@ -187,15 +154,15 @@ func (o *templateOptions) run(out io.Writer) error { return err } - if o.showNotes { - fmt.Fprintln(out, rel.Info.Notes) - } - if o.outputDir != "" { return o.writeAsFiles(rel) } fmt.Fprintln(out, rel.Manifest) + if o.showNotes { + fmt.Fprintf(out, "---\n# Source: %s/templates/NOTES.txt\n", c.Name()) + fmt.Fprintln(out, rel.Info.Notes) + } return nil /* diff --git a/cmd/helm/template_test.go b/cmd/helm/template_test.go index 417030d1c..8c025f632 100644 --- a/cmd/helm/template_test.go +++ b/cmd/helm/template_test.go @@ -17,17 +17,12 @@ limitations under the License. package main import ( - "path/filepath" "testing" ) var chartPath = "./../../pkg/chartutil/testdata/subpop/charts/subchart1" func TestTemplateCmd(t *testing.T) { - absChartPath, err := filepath.Abs(chartPath) - if err != nil { - t.Fatal(err) - } tests := []cmdTestCase{ { name: "check name", @@ -35,15 +30,10 @@ func TestTemplateCmd(t *testing.T) { golden: "output/template.txt", }, { - name: "check set name", - cmd: "template -x templates/service.yaml --set service.name=apache " + chartPath, + name: "check set name", // + cmd: "template --set service.name=apache " + chartPath, golden: "output/template-set.txt", }, - { - name: "check execute absolute", - cmd: "template -x " + absChartPath + "/templates/service.yaml --set service.name=apache " + chartPath, - golden: "output/template-absolute.txt", - }, { name: "check release name", cmd: "template --name test " + chartPath, diff --git a/cmd/helm/testdata/output/status.yaml b/cmd/helm/testdata/output/status.yaml index a3990a954..969bfb26f 100644 --- a/cmd/helm/testdata/output/status.yaml +++ b/cmd/helm/testdata/output/status.yaml @@ -1,7 +1,7 @@ info: - deleted: 0001-01-01T00:00:00Z - first_deployed: 0001-01-01T00:00:00Z - last_deployed: 2016-01-16T00:00:00Z + deleted: "0001-01-01T00:00:00Z" + first_deployed: "0001-01-01T00:00:00Z" + last_deployed: "2016-01-16T00:00:00Z" resources: | resource A resource B diff --git a/cmd/helm/testdata/output/template-absolute.txt b/cmd/helm/testdata/output/template-absolute.txt deleted file mode 100644 index eede3b0b2..000000000 --- a/cmd/helm/testdata/output/template-absolute.txt +++ /dev/null @@ -1,22 +0,0 @@ ---- -# Source: subchart1/templates/service.yaml -apiVersion: v1 -kind: Service -metadata: - name: subchart1 - labels: - chart: "subchart1-0.1.0" - release-name: "RELEASE-NAME" - kube-version/major: "1" - kube-version/minor: "9" - kube-version/gitversion: "v1.9.0" -spec: - type: ClusterIP - ports: - - port: 80 - targetPort: 80 - protocol: TCP - name: apache - selector: - app: subchart1 - diff --git a/cmd/helm/testdata/output/template-kube-version.txt b/cmd/helm/testdata/output/template-kube-version.txt index 21c9b7aa7..ec3d67972 100644 --- a/cmd/helm/testdata/output/template-kube-version.txt +++ b/cmd/helm/testdata/output/template-kube-version.txt @@ -1,3 +1,4 @@ + --- # Source: subchart1/charts/subcharta/templates/service.yaml apiVersion: v1 @@ -15,7 +16,6 @@ spec: name: apache selector: app: subcharta - --- # Source: subchart1/charts/subchartb/templates/service.yaml apiVersion: v1 @@ -33,7 +33,6 @@ spec: name: nginx selector: app: subchartb - --- # Source: subchart1/templates/service.yaml apiVersion: v1 @@ -55,4 +54,3 @@ spec: name: nginx selector: app: subchart1 - diff --git a/cmd/helm/testdata/output/template-name-template.txt b/cmd/helm/testdata/output/template-name-template.txt index 27e9afa72..16de6da86 100644 --- a/cmd/helm/testdata/output/template-name-template.txt +++ b/cmd/helm/testdata/output/template-name-template.txt @@ -1,3 +1,4 @@ + --- # Source: subchart1/charts/subcharta/templates/service.yaml apiVersion: v1 @@ -15,7 +16,6 @@ spec: name: apache selector: app: subcharta - --- # Source: subchart1/charts/subchartb/templates/service.yaml apiVersion: v1 @@ -33,7 +33,6 @@ spec: name: nginx selector: app: subchartb - --- # Source: subchart1/templates/service.yaml apiVersion: v1 @@ -55,4 +54,3 @@ spec: name: nginx selector: app: subchart1 - diff --git a/cmd/helm/testdata/output/template-name.txt b/cmd/helm/testdata/output/template-name.txt index a7fc5c286..8bf493fc3 100644 --- a/cmd/helm/testdata/output/template-name.txt +++ b/cmd/helm/testdata/output/template-name.txt @@ -1,3 +1,4 @@ + --- # Source: subchart1/charts/subcharta/templates/service.yaml apiVersion: v1 @@ -15,7 +16,6 @@ spec: name: apache selector: app: subcharta - --- # Source: subchart1/charts/subchartb/templates/service.yaml apiVersion: v1 @@ -33,7 +33,6 @@ spec: name: nginx selector: app: subchartb - --- # Source: subchart1/templates/service.yaml apiVersion: v1 @@ -55,4 +54,3 @@ spec: name: nginx selector: app: subchart1 - diff --git a/cmd/helm/testdata/output/template-notes.txt b/cmd/helm/testdata/output/template-notes.txt index 812e36802..30a2e7f4d 100644 --- a/cmd/helm/testdata/output/template-notes.txt +++ b/cmd/helm/testdata/output/template-notes.txt @@ -1,3 +1,4 @@ + --- # Source: subchart1/charts/subcharta/templates/service.yaml apiVersion: v1 @@ -15,7 +16,6 @@ spec: name: apache selector: app: subcharta - --- # Source: subchart1/charts/subchartb/templates/service.yaml apiVersion: v1 @@ -33,7 +33,6 @@ spec: name: nginx selector: app: subchartb - --- # Source: subchart1/templates/service.yaml apiVersion: v1 @@ -55,7 +54,6 @@ spec: name: nginx selector: app: subchart1 - --- # Source: subchart1/templates/NOTES.txt Sample notes for subchart1 diff --git a/cmd/helm/testdata/output/template-set.txt b/cmd/helm/testdata/output/template-set.txt index eede3b0b2..b7656e86f 100644 --- a/cmd/helm/testdata/output/template-set.txt +++ b/cmd/helm/testdata/output/template-set.txt @@ -1,3 +1,38 @@ + +--- +# Source: subchart1/charts/subcharta/templates/service.yaml +apiVersion: v1 +kind: Service +metadata: + name: subcharta + labels: + chart: "subcharta-0.1.0" +spec: + type: ClusterIP + ports: + - port: 80 + targetPort: 80 + protocol: TCP + name: apache + selector: + app: subcharta +--- +# Source: subchart1/charts/subchartb/templates/service.yaml +apiVersion: v1 +kind: Service +metadata: + name: subchartb + labels: + chart: "subchartb-0.1.0" +spec: + type: ClusterIP + ports: + - port: 80 + targetPort: 80 + protocol: TCP + name: nginx + selector: + app: subchartb --- # Source: subchart1/templates/service.yaml apiVersion: v1 @@ -19,4 +54,3 @@ spec: name: apache selector: app: subchart1 - diff --git a/cmd/helm/testdata/output/template-values-files.txt b/cmd/helm/testdata/output/template-values-files.txt index bcb93148a..b7656e86f 100644 --- a/cmd/helm/testdata/output/template-values-files.txt +++ b/cmd/helm/testdata/output/template-values-files.txt @@ -1,3 +1,4 @@ + --- # Source: subchart1/charts/subcharta/templates/service.yaml apiVersion: v1 @@ -15,7 +16,6 @@ spec: name: apache selector: app: subcharta - --- # Source: subchart1/charts/subchartb/templates/service.yaml apiVersion: v1 @@ -33,7 +33,6 @@ spec: name: nginx selector: app: subchartb - --- # Source: subchart1/templates/service.yaml apiVersion: v1 @@ -55,4 +54,3 @@ spec: name: apache selector: app: subchart1 - diff --git a/cmd/helm/testdata/output/template.txt b/cmd/helm/testdata/output/template.txt index c9fb07138..3553654dd 100644 --- a/cmd/helm/testdata/output/template.txt +++ b/cmd/helm/testdata/output/template.txt @@ -1,3 +1,4 @@ + --- # Source: subchart1/charts/subcharta/templates/service.yaml apiVersion: v1 @@ -15,7 +16,6 @@ spec: name: apache selector: app: subcharta - --- # Source: subchart1/charts/subchartb/templates/service.yaml apiVersion: v1 @@ -33,7 +33,6 @@ spec: name: nginx selector: app: subchartb - --- # Source: subchart1/templates/service.yaml apiVersion: v1 @@ -55,4 +54,3 @@ spec: name: nginx selector: app: subchart1 - diff --git a/internal/test/test.go b/internal/test/test.go index aa1791a07..e6b575324 100644 --- a/internal/test/test.go +++ b/internal/test/test.go @@ -23,6 +23,7 @@ import ( "path/filepath" "github.com/pkg/errors" + "github.com/stretchr/testify/assert" ) // UpdateGolden writes out the golden files with the latest values, rather than failing the test. @@ -32,6 +33,7 @@ var updateGolden = flag.Bool("update", false, "update golden files") type TestingT interface { Fatal(...interface{}) Fatalf(string, ...interface{}) + Errorf(string, ...interface{}) HelperT } @@ -40,6 +42,45 @@ type HelperT interface { Helper() } +// GoldenFile executes tests that run against canonical "golden" test files +type GoldenFile struct { + t TestingT + isUpdate bool + is *assert.Assertions +} + +func New(t TestingT) *GoldenFile { + return &GoldenFile{ + t: t, + isUpdate: *updateGolden, + is: assert.New(t), + } +} + +func (g *GoldenFile) AssertString(actual, filename string) { + g.t.Helper() + g.compareStr(actual, path(filename)) +} + +func (g *GoldenFile) compareStr(actual string, filename string) { + if err := g.update(filename, []byte(actual)); err != nil { + g.t.Fatal(err) + } + + expected, err := ioutil.ReadFile(filename) + if err != nil { + g.t.Fatalf("unable to read testdata %s: %s", filename, err) + } + g.is.Equal(string(expected), actual, "does not match golden file %s", filename) +} + +func (g *GoldenFile) update(filename string, in []byte) error { + if !*updateGolden { + return nil + } + return ioutil.WriteFile(filename, normalize(in), 0666) +} + // AssertGoldenBytes asserts that the give actual content matches the contents of the given filename func AssertGoldenBytes(t TestingT, actual []byte, filename string) { t.Helper() @@ -51,11 +92,29 @@ func AssertGoldenBytes(t TestingT, actual []byte, filename string) { // AssertGoldenString asserts that the given string matches the contents of the given file. func AssertGoldenString(t TestingT, actual, filename string) { + New(t).AssertString(actual, filename) + /* + t.Helper() + + if err := compare([]byte(actual), path(filename)); err != nil { + t.Fatalf("%v", err) + } + */ +} + +// AssertGolden compares the given actual data with the contents of a file.AssertGolden// +// +// Mismatches produce diffs of actual vs desired. +func AssertGolden(t TestingT, actual, filename string) { t.Helper() + is := assert.New(t) - if err := compare([]byte(actual), path(filename)); err != nil { - t.Fatalf("%v", err) + expected, err := ioutil.ReadFile(filename) + if err != nil { + t.Fatalf("unable to read testdata %s: %s", filename, err) } + // This generates diffs and similarly helpful output for us. + is.Equal(string(expected), actual, "does not match golden file %s", filename) } func path(filename string) string {