Merge pull request #4071 from adamreese/dev-v3-template

ref(cmd): test template cmd using golden files
pull/4079/head
Adam Reese 6 years ago committed by GitHub
commit 03dacfe4f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -30,6 +30,7 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/helm/cmd/helm/require"
"k8s.io/helm/pkg/chartutil" "k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/engine" "k8s.io/helm/pkg/engine"
"k8s.io/helm/pkg/hapi/release" "k8s.io/helm/pkg/hapi/release"
@ -80,10 +81,8 @@ func newTemplateCmd(out io.Writer) *cobra.Command {
Use: "template CHART", Use: "template CHART",
Short: fmt.Sprintf("locally render templates"), Short: fmt.Sprintf("locally render templates"),
Long: templateDesc, Long: templateDesc,
Args: require.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return errors.New("chart is required")
}
// verify chart path exists // verify chart path exists
if _, err := os.Stat(args[0]); err == nil { if _, err := os.Stat(args[0]); err == nil {
if o.chartPath, err = filepath.Abs(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 // verify that output-dir exists if provided
if o.outputDir != "" { if o.outputDir != "" {
_, err = os.Stat(o.outputDir) if _, err := os.Stat(o.outputDir); os.IsNotExist(err) {
if os.IsNotExist(err) {
return errors.Errorf("output-dir '%s' does not exist", o.outputDir) 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(), Namespace: getNamespace(),
} }
err = chartutil.ProcessRequirementsEnabled(c, config) if err := chartutil.ProcessRequirementsEnabled(c, config); err != nil {
if err != nil {
return err return err
} }
err = chartutil.ProcessRequirementsImportValues(c) if err := chartutil.ProcessRequirementsImportValues(c); err != nil {
if err != nil {
return err return err
} }
@ -207,10 +203,11 @@ func (o *templateOptions) run(out io.Writer) error {
} }
rendered, err := renderer.Render(c, vals) rendered, err := renderer.Render(c, vals)
listManifests := []tiller.Manifest{}
if err != nil { if err != nil {
return err return err
} }
listManifests := []tiller.Manifest{}
// extract kind and name // extract kind and name
re := regexp.MustCompile("kind:(.*)\n") re := regexp.MustCompile("kind:(.*)\n")
for k, v := range rendered { for k, v := range rendered {
@ -235,6 +232,7 @@ func (o *templateOptions) run(out io.Writer) error {
} }
return false return false
} }
if settings.Debug { if settings.Debug {
rel := &release.Release{ rel := &release.Release{
Name: o.releaseName, Name: o.releaseName,
@ -247,41 +245,34 @@ func (o *templateOptions) run(out io.Writer) error {
} }
for _, m := range tiller.SortByKind(listManifests) { for _, m := range tiller.SortByKind(listManifests) {
if len(o.renderFiles) > 0 && !in(m.Name, rf) {
continue
}
data := m.Content
b := filepath.Base(m.Name) b := filepath.Base(m.Name)
if !o.showNotes && b == "NOTES.txt" { switch {
case len(o.renderFiles) > 0 && !in(m.Name, rf):
continue continue
} case !o.showNotes && b == "NOTES.txt":
if strings.HasPrefix(b, "_") {
continue continue
} case strings.HasPrefix(b, "_"):
continue
if o.outputDir != "" { case whitespaceRegex.MatchString(m.Content):
// blank template after execution // blank template after execution
if whitespaceRegex.MatchString(data) { continue
continue case o.outputDir != "":
} if err := writeToFile(out, o.outputDir, m.Name, m.Content); err != nil {
err = writeToFile(o.outputDir, m.Name, data)
if err != nil {
return err 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 return nil
} }
// write the <data> to <output-dir>/<name> // write the <data> to <output-dir>/<name>
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)) outfileName := strings.Join([]string{outputDir, name}, string(filepath.Separator))
err := ensureDirectoryForFile(outfileName) if err := ensureDirectoryForFile(outfileName); err != nil {
if err != nil {
return err return err
} }
@ -292,21 +283,18 @@ func writeToFile(outputDir, name, data string) error {
defer f.Close() defer f.Close()
_, err = f.WriteString(fmt.Sprintf("##---\n# Source: %s\n%s", name, data)) if _, err = f.WriteString(fmt.Sprintf("##---\n# Source: %s\n%s", name, data)); err != nil {
if err != nil {
return err return err
} }
fmt.Printf("wrote %s\n", outfileName) fmt.Fprintf(out, "wrote %s\n", outfileName)
return nil return nil
} }
// check if the directory exists to create file. creates if don't exists // check if the directory exists to create file. creates if don't exists
func ensureDirectoryForFile(file string) error { func ensureDirectoryForFile(file string) error {
baseDir := path.Dir(file) baseDir := path.Dir(file)
_, err := os.Stat(baseDir) if _, err := os.Stat(baseDir); err != nil && !os.IsNotExist(err) {
if err != nil && !os.IsNotExist(err) {
return err return err
} }

@ -17,13 +17,7 @@ limitations under the License.
package main package main
import ( import (
"bufio"
"bytes"
"fmt"
"io"
"os"
"path/filepath" "path/filepath"
"strings"
"testing" "testing"
) )
@ -34,127 +28,53 @@ func TestTemplateCmd(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
tests := []struct { tests := []cmdTestCase{
name string
desc string
args []string
expectKey string
expectValue string
}{
{ {
name: "check_name", name: "check name",
desc: "check for a known name in chart", cmd: "template " + chartPath,
args: []string{chartPath}, golden: "output/template.txt",
expectKey: "subchart1/templates/service.yaml",
expectValue: "protocol: TCP\n name: nginx",
}, },
{ {
name: "check_set_name", name: "check set name",
desc: "verify --set values exist", cmd: "template -x templates/service.yaml --set service.name=apache " + chartPath,
args: []string{chartPath, "-x", "templates/service.yaml", "--set", "service.name=apache"}, golden: "output/template-set.txt",
expectKey: "subchart1/templates/service.yaml",
expectValue: "protocol: TCP\n name: apache",
}, },
{ {
name: "check_execute", name: "check execute absolute",
desc: "verify --execute single template", cmd: "template -x " + absChartPath + "/templates/service.yaml --set service.name=apache " + chartPath,
args: []string{chartPath, "-x", "templates/service.yaml", "--set", "service.name=apache"}, golden: "output/template-absolute.txt",
expectKey: "subchart1/templates/service.yaml",
expectValue: "protocol: TCP\n name: apache",
}, },
{ {
name: "check_execute_absolute", name: "check release name",
desc: "verify --execute single template", cmd: "template --name test " + chartPath,
args: []string{chartPath, "-x", absChartPath + "/" + "templates/service.yaml", "--set", "service.name=apache"}, golden: "output/template-name.txt",
expectKey: "subchart1/templates/service.yaml",
expectValue: "protocol: TCP\n name: apache",
}, },
{ {
name: "check_release_name", name: "check notes",
desc: "verify --release exists", cmd: "template --notes " + chartPath,
args: []string{chartPath, "--name", "test"}, golden: "output/template-notes.txt",
expectKey: "subchart1/templates/service.yaml",
expectValue: "release-name: \"test\"",
}, },
{ {
name: "check_notes", name: "check values files",
desc: "verify --notes shows notes", cmd: "template --values " + chartPath + "/charts/subchartA/values.yaml " + chartPath,
args: []string{chartPath, "--notes", "true"}, golden: "output/template-values-files.txt",
expectKey: "subchart1/templates/NOTES.txt",
expectValue: "Sample notes for subchart1",
}, },
{ {
name: "check_values_files", name: "check name template",
desc: "verify --values files values exist", cmd: `template --name-template='foobar-{{ b64enc "abc" }}-baz' ` + chartPath,
args: []string{chartPath, "--values", chartPath + "/charts/subchartA/values.yaml"}, golden: "output/template-name-template.txt",
expectKey: "subchart1/templates/service.yaml",
expectValue: "name: apache",
}, },
{ {
name: "check_name_template", name: "check kube version",
desc: "verify --name-template result exists", cmd: "template --kube-version 1.6 " + chartPath,
args: []string{chartPath, "--name-template", "foobar-{{ b64enc \"abc\" }}-baz"}, golden: "output/template-kube-version.txt",
expectKey: "subchart1/templates/service.yaml",
expectValue: "release-name: \"foobar-YWJj-baz\"",
}, },
{ {
name: "check_kube_version", name: "check no args",
desc: "verify --kube-version overrides the kubernetes version", cmd: "template",
args: []string{chartPath, "--kube-version", "1.6"}, wantError: true,
expectKey: "subchart1/templates/service.yaml", golden: "output/template-no-args.txt",
expectValue: "kube-version/major: \"1\"\n kube-version/minor: \"6\"\n kube-version/gitversion: \"v1.6.0\"",
}, },
} }
runTestCmd(t, tests)
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[<path>]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()
})
}
} }

@ -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

@ -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

@ -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

@ -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

@ -0,0 +1,3 @@
Error: "helm template" requires 1 argument
Usage: helm template CHART [flags]

@ -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

@ -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

@ -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

@ -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

@ -71,7 +71,7 @@ func compare(actual []byte, filename string) error {
return errors.Wrapf(err, "unable to read testdata %s", filename) return errors.Wrapf(err, "unable to read testdata %s", filename)
} }
if !bytes.Equal(expected, actual) { 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 return nil
} }

Loading…
Cancel
Save