From 01a598f53110a3a6b55701b73f075000a75c0823 Mon Sep 17 00:00:00 2001 From: vaikas-google <vaikas@google.com> Date: Mon, 1 Aug 2016 19:19:27 -0700 Subject: [PATCH] Add name-template flag for installation that allows the user to specify a template used for release name generation --- cmd/helm/install.go | 29 ++++++++++++++ cmd/helm/install_test.go | 84 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) diff --git a/cmd/helm/install.go b/cmd/helm/install.go index e80527b48..e71a3ed41 100644 --- a/cmd/helm/install.go +++ b/cmd/helm/install.go @@ -17,6 +17,7 @@ limitations under the License. package main import ( + "bytes" "fmt" "io" "io/ioutil" @@ -24,6 +25,9 @@ import ( "path/filepath" "strings" + "html/template" + + "github.com/Masterminds/sprig" "github.com/ghodss/yaml" "github.com/spf13/cobra" @@ -63,6 +67,7 @@ type installCmd struct { out io.Writer client helm.Interface values *values + nameTemplate string } func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command { @@ -100,6 +105,7 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command { f.BoolVar(&inst.disableHooks, "no-hooks", false, "prevent hooks from running during install") f.BoolVar(&inst.reuseName, "reuse-name", false, "force Tiller to re-use the given name, even if that name is already used. This is unsafe in production") f.Var(inst.values, "set", "set values on the command line. Separate values with commas: key1=val1,key2=val2") + f.StringVar(&inst.nameTemplate, "name-template", "", "specify template used to name the release") return cmd } @@ -113,6 +119,16 @@ func (i *installCmd) run() error { return err } + // If template is specified, try to run the template. + if i.nameTemplate != "" { + i.name, err = generateName(i.nameTemplate) + if err != nil { + return err + } + // Print the final name so the user knows what the final name of the release is. + fmt.Printf("final name: %s\n", i.name) + } + res, err := i.client.InstallRelease( i.chartPath, i.namespace, @@ -249,3 +265,16 @@ func locateChartPath(name string) (string, error) { return name, fmt.Errorf("file %q not found", origname) } + +func generateName(nameTemplate string) (string, error) { + t, err := template.New("name-template").Funcs(sprig.FuncMap()).Parse(nameTemplate) + if err != nil { + return "", err + } + var b bytes.Buffer + err = t.Execute(&b, nil) + if err != nil { + return "", err + } + return b.String(), nil +} diff --git a/cmd/helm/install_test.go b/cmd/helm/install_test.go index 41663a337..24b05df4d 100644 --- a/cmd/helm/install_test.go +++ b/cmd/helm/install_test.go @@ -19,6 +19,7 @@ package main import ( "fmt" "io" + "regexp" "strings" "testing" @@ -65,6 +66,14 @@ func TestInstall(t *testing.T) { expected: "aeneas", resp: releaseMock(&releaseOptions{name: "aeneas"}), }, + // Install, using the name-template + { + name: "install with name-template", + args: []string{"testdata/testcharts/alpine"}, + flags: []string{"--name-template", "{{upper \"foobar\"}}"}, + expected: "FOOBAR", + resp: releaseMock(&releaseOptions{name: "FOOBAR"}), + }, } runReleaseCases(t, tests, func(c *fakeReleaseClient, out io.Writer) *cobra.Command { @@ -113,3 +122,78 @@ sailor: sinbad t.Errorf("Expected String() to be \n%s\nGot\n%s\n", y, out) } } + +type nameTemplateTestCase struct { + tpl string + expected string + expectedErrorStr string +} + +func TestNameTemplate(t *testing.T) { + testCases := []nameTemplateTestCase{ + // Just a straight up nop please + { + tpl: "foobar", + expected: "foobar", + expectedErrorStr: "", + }, + // Random numbers at the end for fun & profit + { + tpl: "foobar-{{randNumeric 6}}", + expected: "foobar-[0-9]{6}$", + expectedErrorStr: "", + }, + // Random numbers in the middle for fun & profit + { + tpl: "foobar-{{randNumeric 4}}-baz", + expected: "foobar-[0-9]{4}-baz$", + expectedErrorStr: "", + }, + // No such function + { + tpl: "foobar-{{randInt}}", + expected: "", + expectedErrorStr: "function \"randInt\" not defined", + }, + // Invalid template + { + tpl: "foobar-{{", + expected: "", + expectedErrorStr: "unexpected unclosed action", + }, + } + + for _, tc := range testCases { + + n, err := generateName(tc.tpl) + if err != nil { + if tc.expectedErrorStr == "" { + t.Errorf("Was not expecting error, but got: %v", err) + continue + } + re, compErr := regexp.Compile(tc.expectedErrorStr) + if compErr != nil { + t.Errorf("Expected error string failed to compile: %v", compErr) + continue + } + if !re.MatchString(err.Error()) { + t.Errorf("Error didn't match for %s expected %s but got %v", tc.tpl, tc.expectedErrorStr, err) + continue + } + } + if err == nil && tc.expectedErrorStr != "" { + t.Errorf("Was expecting error %s but didn't get an error back", tc.expectedErrorStr) + } + + if tc.expected != "" { + re, err := regexp.Compile(tc.expected) + if err != nil { + t.Errorf("Expected string failed to compile: %v", err) + continue + } + if !re.MatchString(n) { + t.Errorf("Returned name didn't match for %s expected %s but got %s", tc.tpl, tc.expected, n) + } + } + } +} -- GitLab