ref: Use action.Install to run a helm template command

Now that there is no Tiller, it does not make sense to have a completely forked set of logic for `helm template`. This change rewrites template to use the Install action as a dry-run, but with Kubernetes fake fixtures instead of with the real thing.

Signed-off-by: Matt Butcher <matt.butcher@microsoft.com>
pull/5171/head
Matt Butcher 7 years ago
parent b0f9e1a39f
commit 1968c074bd
No known key found for this signature in database
GPG Key ID: DCD5F5E5EF32C345

@ -42,33 +42,23 @@ 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
@ -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
/*

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

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

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

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

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

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

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

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

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

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

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

Loading…
Cancel
Save