From 1d2f40c80166e78d58f7f430062d4af6c7c8bff0 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Wed, 16 May 2018 12:35:30 -0700 Subject: [PATCH] ref(cmd): test template cmd using golden files --- cmd/helm/template.go | 62 ++++---- cmd/helm/template_test.go | 140 ++++-------------- .../testdata/output/template-absolute.txt | 23 +++ .../testdata/output/template-kube-version.txt | 59 ++++++++ .../output/template-name-template.txt | 59 ++++++++ cmd/helm/testdata/output/template-name.txt | 59 ++++++++ cmd/helm/testdata/output/template-no-args.txt | 3 + cmd/helm/testdata/output/template-notes.txt | 62 ++++++++ cmd/helm/testdata/output/template-set.txt | 23 +++ .../testdata/output/template-values-files.txt | 59 ++++++++ cmd/helm/testdata/output/template.txt | 59 ++++++++ internal/test/test.go | 2 +- 12 files changed, 462 insertions(+), 148 deletions(-) create mode 100644 cmd/helm/testdata/output/template-absolute.txt create mode 100644 cmd/helm/testdata/output/template-kube-version.txt create mode 100644 cmd/helm/testdata/output/template-name-template.txt create mode 100644 cmd/helm/testdata/output/template-name.txt create mode 100644 cmd/helm/testdata/output/template-no-args.txt create mode 100644 cmd/helm/testdata/output/template-notes.txt create mode 100644 cmd/helm/testdata/output/template-set.txt create mode 100644 cmd/helm/testdata/output/template-values-files.txt create mode 100644 cmd/helm/testdata/output/template.txt diff --git a/cmd/helm/template.go b/cmd/helm/template.go index f59c4161c..3674fa841 100644 --- a/cmd/helm/template.go +++ b/cmd/helm/template.go @@ -30,6 +30,7 @@ import ( "github.com/pkg/errors" "github.com/spf13/cobra" + "k8s.io/helm/cmd/helm/require" "k8s.io/helm/pkg/chartutil" "k8s.io/helm/pkg/engine" "k8s.io/helm/pkg/hapi/release" @@ -80,10 +81,8 @@ func newTemplateCmd(out io.Writer) *cobra.Command { Use: "template CHART", Short: fmt.Sprintf("locally render templates"), Long: templateDesc, + Args: require.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return errors.New("chart is required") - } // verify chart path exists if _, err := os.Stat(args[0]); err == nil { if o.chartPath, err = filepath.Abs(args[0]); err != nil { @@ -135,8 +134,7 @@ func (o *templateOptions) run(out io.Writer) error { // verify that output-dir exists if provided if o.outputDir != "" { - _, err = os.Stat(o.outputDir) - if os.IsNotExist(err) { + if _, err := os.Stat(o.outputDir); os.IsNotExist(err) { return errors.Errorf("output-dir '%s' does not exist", o.outputDir) } } @@ -174,12 +172,10 @@ func (o *templateOptions) run(out io.Writer) error { Namespace: getNamespace(), } - err = chartutil.ProcessRequirementsEnabled(c, config) - if err != nil { + if err := chartutil.ProcessRequirementsEnabled(c, config); err != nil { return err } - err = chartutil.ProcessRequirementsImportValues(c) - if err != nil { + if err := chartutil.ProcessRequirementsImportValues(c); err != nil { return err } @@ -207,10 +203,11 @@ func (o *templateOptions) run(out io.Writer) error { } rendered, err := renderer.Render(c, vals) - listManifests := []tiller.Manifest{} if err != nil { return err } + + listManifests := []tiller.Manifest{} // extract kind and name re := regexp.MustCompile("kind:(.*)\n") for k, v := range rendered { @@ -235,6 +232,7 @@ func (o *templateOptions) run(out io.Writer) error { } return false } + if settings.Debug { rel := &release.Release{ Name: o.releaseName, @@ -247,41 +245,34 @@ func (o *templateOptions) run(out io.Writer) error { } for _, m := range tiller.SortByKind(listManifests) { - if len(o.renderFiles) > 0 && !in(m.Name, rf) { - continue - } - data := m.Content b := filepath.Base(m.Name) - if !o.showNotes && b == "NOTES.txt" { + switch { + case len(o.renderFiles) > 0 && !in(m.Name, rf): continue - } - if strings.HasPrefix(b, "_") { + case !o.showNotes && b == "NOTES.txt": continue - } - - if o.outputDir != "" { + case strings.HasPrefix(b, "_"): + continue + case whitespaceRegex.MatchString(m.Content): // blank template after execution - if whitespaceRegex.MatchString(data) { - continue - } - err = writeToFile(o.outputDir, m.Name, data) - if err != nil { + continue + case o.outputDir != "": + if err := writeToFile(out, o.outputDir, m.Name, m.Content); err != nil { return err } - continue + default: + fmt.Fprintf(out, "---\n# Source: %s\n", m.Name) + fmt.Fprintln(out, m.Content) } - fmt.Printf("---\n# Source: %s\n", m.Name) - fmt.Println(data) } return nil } // write the to / -func writeToFile(outputDir, name, data string) error { +func writeToFile(out io.Writer, outputDir, name, data string) error { outfileName := strings.Join([]string{outputDir, name}, string(filepath.Separator)) - err := ensureDirectoryForFile(outfileName) - if err != nil { + if err := ensureDirectoryForFile(outfileName); err != nil { return err } @@ -292,21 +283,18 @@ func writeToFile(outputDir, name, data string) error { defer f.Close() - _, err = f.WriteString(fmt.Sprintf("##---\n# Source: %s\n%s", name, data)) - - if err != nil { + if _, err = f.WriteString(fmt.Sprintf("##---\n# Source: %s\n%s", name, data)); err != nil { return err } - fmt.Printf("wrote %s\n", outfileName) + fmt.Fprintf(out, "wrote %s\n", outfileName) return nil } // check if the directory exists to create file. creates if don't exists func ensureDirectoryForFile(file string) error { baseDir := path.Dir(file) - _, err := os.Stat(baseDir) - if err != nil && !os.IsNotExist(err) { + if _, err := os.Stat(baseDir); err != nil && !os.IsNotExist(err) { return err } diff --git a/cmd/helm/template_test.go b/cmd/helm/template_test.go index 82eadcad0..9bc0d5de8 100644 --- a/cmd/helm/template_test.go +++ b/cmd/helm/template_test.go @@ -17,13 +17,7 @@ limitations under the License. package main import ( - "bufio" - "bytes" - "fmt" - "io" - "os" "path/filepath" - "strings" "testing" ) @@ -34,127 +28,53 @@ func TestTemplateCmd(t *testing.T) { if err != nil { t.Fatal(err) } - tests := []struct { - name string - desc string - args []string - expectKey string - expectValue string - }{ + tests := []cmdTestCase{ { - name: "check_name", - desc: "check for a known name in chart", - args: []string{chartPath}, - expectKey: "subchart1/templates/service.yaml", - expectValue: "protocol: TCP\n name: nginx", + name: "check name", + cmd: "template " + chartPath, + golden: "output/template.txt", }, { - name: "check_set_name", - desc: "verify --set values exist", - args: []string{chartPath, "-x", "templates/service.yaml", "--set", "service.name=apache"}, - expectKey: "subchart1/templates/service.yaml", - expectValue: "protocol: TCP\n name: apache", + name: "check set name", + cmd: "template -x templates/service.yaml --set service.name=apache " + chartPath, + golden: "output/template-set.txt", }, { - name: "check_execute", - desc: "verify --execute single template", - args: []string{chartPath, "-x", "templates/service.yaml", "--set", "service.name=apache"}, - expectKey: "subchart1/templates/service.yaml", - expectValue: "protocol: TCP\n name: apache", + name: "check execute absolute", + cmd: "template -x " + absChartPath + "/templates/service.yaml --set service.name=apache " + chartPath, + golden: "output/template-absolute.txt", }, { - name: "check_execute_absolute", - desc: "verify --execute single template", - args: []string{chartPath, "-x", absChartPath + "/" + "templates/service.yaml", "--set", "service.name=apache"}, - expectKey: "subchart1/templates/service.yaml", - expectValue: "protocol: TCP\n name: apache", + name: "check release name", + cmd: "template --name test " + chartPath, + golden: "output/template-name.txt", }, { - name: "check_release_name", - desc: "verify --release exists", - args: []string{chartPath, "--name", "test"}, - expectKey: "subchart1/templates/service.yaml", - expectValue: "release-name: \"test\"", + name: "check notes", + cmd: "template --notes " + chartPath, + golden: "output/template-notes.txt", }, { - name: "check_notes", - desc: "verify --notes shows notes", - args: []string{chartPath, "--notes", "true"}, - expectKey: "subchart1/templates/NOTES.txt", - expectValue: "Sample notes for subchart1", + name: "check values files", + cmd: "template --values " + chartPath + "/charts/subchartA/values.yaml " + chartPath, + golden: "output/template-values-files.txt", }, { - name: "check_values_files", - desc: "verify --values files values exist", - args: []string{chartPath, "--values", chartPath + "/charts/subchartA/values.yaml"}, - expectKey: "subchart1/templates/service.yaml", - expectValue: "name: apache", + name: "check name template", + cmd: `template --name-template='foobar-{{ b64enc "abc" }}-baz' ` + chartPath, + golden: "output/template-name-template.txt", }, { - name: "check_name_template", - desc: "verify --name-template result exists", - args: []string{chartPath, "--name-template", "foobar-{{ b64enc \"abc\" }}-baz"}, - expectKey: "subchart1/templates/service.yaml", - expectValue: "release-name: \"foobar-YWJj-baz\"", + name: "check kube version", + cmd: "template --kube-version 1.6 " + chartPath, + golden: "output/template-kube-version.txt", }, { - name: "check_kube_version", - desc: "verify --kube-version overrides the kubernetes version", - args: []string{chartPath, "--kube-version", "1.6"}, - expectKey: "subchart1/templates/service.yaml", - expectValue: "kube-version/major: \"1\"\n kube-version/minor: \"6\"\n kube-version/gitversion: \"v1.6.0\"", + name: "check no args", + cmd: "template", + wantError: true, + golden: "output/template-no-args.txt", }, } - - var buf bytes.Buffer - for _, tt := range tests { - t.Run(tt.name, func(T *testing.T) { - // capture stdout - old := os.Stdout - r, w, _ := os.Pipe() - os.Stdout = w - // execute template command - out := bytes.NewBuffer(nil) - cmd := newTemplateCmd(out) - cmd.SetArgs(tt.args) - err := cmd.Execute() - if err != nil { - t.Errorf("expected: %v, got %v", tt.expectValue, err) - } - // restore stdout - w.Close() - os.Stdout = old - var b bytes.Buffer - io.Copy(&b, r) - r.Close() - // scan yaml into map[]yaml - scanner := bufio.NewScanner(&b) - next := false - lastKey := "" - m := map[string]string{} - for scanner.Scan() { - if scanner.Text() == "---" { - next = true - } else if next { - // remove '# Source: ' - head := "# Source: " - lastKey = scanner.Text()[len(head):] - next = false - } else { - m[lastKey] = m[lastKey] + scanner.Text() + "\n" - } - } - if err := scanner.Err(); err != nil { - fmt.Fprintln(os.Stderr, "reading standard input:", err) - } - if v, ok := m[tt.expectKey]; ok { - if !strings.Contains(v, tt.expectValue) { - t.Errorf("failed to match expected value %s in %s", tt.expectValue, v) - } - } else { - t.Errorf("could not find key %s", tt.expectKey) - } - buf.Reset() - }) - } + runTestCmd(t, tests) } diff --git a/cmd/helm/testdata/output/template-absolute.txt b/cmd/helm/testdata/output/template-absolute.txt new file mode 100644 index 000000000..3360f08bd --- /dev/null +++ b/cmd/helm/testdata/output/template-absolute.txt @@ -0,0 +1,23 @@ +--- +# Source: subchart1/templates/service.yaml +apiVersion: v1 +kind: Service +metadata: + name: subchart1 + labels: + chart: "subchart1-0.1.0" + namespace: "default" + 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 new file mode 100644 index 000000000..5b26fe084 --- /dev/null +++ b/cmd/helm/testdata/output/template-kube-version.txt @@ -0,0 +1,59 @@ +--- +# 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 +kind: Service +metadata: + name: subchart1 + labels: + chart: "subchart1-0.1.0" + namespace: "default" + release-name: "RELEASE-NAME" + kube-version/major: "1" + kube-version/minor: "6" + kube-version/gitversion: "v1.6.0" +spec: + type: ClusterIP + ports: + - port: 80 + targetPort: 80 + protocol: TCP + 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 new file mode 100644 index 000000000..2ec6cbe33 --- /dev/null +++ b/cmd/helm/testdata/output/template-name-template.txt @@ -0,0 +1,59 @@ +--- +# 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 +kind: Service +metadata: + name: subchart1 + labels: + chart: "subchart1-0.1.0" + namespace: "default" + release-name: "foobar-YWJj-baz" + 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: nginx + selector: + app: subchart1 + diff --git a/cmd/helm/testdata/output/template-name.txt b/cmd/helm/testdata/output/template-name.txt new file mode 100644 index 000000000..78cd777bc --- /dev/null +++ b/cmd/helm/testdata/output/template-name.txt @@ -0,0 +1,59 @@ +--- +# 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 +kind: Service +metadata: + name: subchart1 + labels: + chart: "subchart1-0.1.0" + namespace: "default" + release-name: "test" + 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: nginx + selector: + app: subchart1 + diff --git a/cmd/helm/testdata/output/template-no-args.txt b/cmd/helm/testdata/output/template-no-args.txt new file mode 100644 index 000000000..6a8a99505 --- /dev/null +++ b/cmd/helm/testdata/output/template-no-args.txt @@ -0,0 +1,3 @@ +Error: "helm template" requires 1 argument + +Usage: helm template CHART [flags] diff --git a/cmd/helm/testdata/output/template-notes.txt b/cmd/helm/testdata/output/template-notes.txt new file mode 100644 index 000000000..8a20426ff --- /dev/null +++ b/cmd/helm/testdata/output/template-notes.txt @@ -0,0 +1,62 @@ +--- +# 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 +kind: Service +metadata: + name: subchart1 + labels: + chart: "subchart1-0.1.0" + namespace: "default" + 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: 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 new file mode 100644 index 000000000..3360f08bd --- /dev/null +++ b/cmd/helm/testdata/output/template-set.txt @@ -0,0 +1,23 @@ +--- +# Source: subchart1/templates/service.yaml +apiVersion: v1 +kind: Service +metadata: + name: subchart1 + labels: + chart: "subchart1-0.1.0" + namespace: "default" + 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-values-files.txt b/cmd/helm/testdata/output/template-values-files.txt new file mode 100644 index 000000000..83acc98f4 --- /dev/null +++ b/cmd/helm/testdata/output/template-values-files.txt @@ -0,0 +1,59 @@ +--- +# 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 +kind: Service +metadata: + name: subchart1 + labels: + chart: "subchart1-0.1.0" + namespace: "default" + 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.txt b/cmd/helm/testdata/output/template.txt new file mode 100644 index 000000000..829abac03 --- /dev/null +++ b/cmd/helm/testdata/output/template.txt @@ -0,0 +1,59 @@ +--- +# 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 +kind: Service +metadata: + name: subchart1 + labels: + chart: "subchart1-0.1.0" + namespace: "default" + 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: nginx + selector: + app: subchart1 + diff --git a/internal/test/test.go b/internal/test/test.go index a61c32a83..4ddfc9446 100644 --- a/internal/test/test.go +++ b/internal/test/test.go @@ -71,7 +71,7 @@ func compare(actual []byte, filename string) error { return errors.Wrapf(err, "unable to read testdata %s", filename) } if !bytes.Equal(expected, actual) { - return errors.Errorf("does not match golden file %s\n\nWANT:\n%q\n\nGOT:\n%q\n", filename, expected, actual) + return errors.Errorf("does not match golden file %s\n\nWANT:\n%s\n\nGOT:\n%s\n", filename, expected, actual) } return nil }