From fbe80437a499e6b5c7301588522b03dde593ce10 Mon Sep 17 00:00:00 2001 From: Justin Scott Date: Tue, 9 Jan 2018 00:51:03 -0800 Subject: [PATCH 01/71] fix(helm): fix importValues warnings from disabled charts ImportValues incorrectly processes charts that should be disabled. This patch excludes those charts. Closes #2620 --- pkg/chartutil/requirements.go | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/pkg/chartutil/requirements.go b/pkg/chartutil/requirements.go index ce761a6fc..546160b0c 100644 --- a/pkg/chartutil/requirements.go +++ b/pkg/chartutil/requirements.go @@ -394,6 +394,21 @@ func processImportValues(c *chart.Chart) error { b := make(map[string]interface{}, 0) // import values from each dependency if specified in import-values for _, r := range reqs.Dependencies { + // only process raw requirement that is found in chart's dependencies (enabled) + found := false + name := r.Name + for _, v := range c.Dependencies { + if v.Metadata.Name == r.Name { + found = true + } + if v.Metadata.Name == r.Alias { + found = true + name = r.Alias + } + } + if !found { + continue + } if len(r.ImportValues) > 0 { var outiv []interface{} for _, riv := range r.ImportValues { @@ -404,7 +419,7 @@ func processImportValues(c *chart.Chart) error { "parent": iv["parent"].(string), } outiv = append(outiv, nm) - s := r.Name + "." + nm["child"] + s := name + "." + nm["child"] // get child table vv, err := cvals.Table(s) if err != nil { @@ -420,7 +435,7 @@ func processImportValues(c *chart.Chart) error { "parent": ".", } outiv = append(outiv, nm) - s := r.Name + "." + nm["child"] + s := name + "." + nm["child"] vm, err := cvals.Table(s) if err != nil { log.Printf("Warning: ImportValues missing table: %v", err) From 3014b9277f0be853593820d7eefe0f063f6852a3 Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Mon, 12 Mar 2018 12:16:31 -0700 Subject: [PATCH 02/71] add back the default 5 second connection timeout When Helm v2.8.2 was released, we made a change to the default connection timeout by supplying a value passed from pkg/helm/environment. This broke support for third party clients relying on pkg/helm because now the default connection timeout is zero. Adding a default 5 second timeout back retains old behaviour, while not breaking backwards compatibility because the connection timeout can still be configured. --- pkg/helm/client.go | 2 ++ pkg/helm/client_test.go | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 pkg/helm/client_test.go diff --git a/pkg/helm/client.go b/pkg/helm/client.go index 372613b2a..2fb1e54e4 100644 --- a/pkg/helm/client.go +++ b/pkg/helm/client.go @@ -44,6 +44,8 @@ type Client struct { // NewClient creates a new client. func NewClient(opts ...Option) *Client { var c Client + // set some sane defaults + c.Option(ConnectTimeout(5)) return c.Option(opts...) } diff --git a/pkg/helm/client_test.go b/pkg/helm/client_test.go new file mode 100644 index 000000000..95e044499 --- /dev/null +++ b/pkg/helm/client_test.go @@ -0,0 +1,34 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package helm + +import ( + "testing" + "time" +) + +func TestNewClient(t *testing.T) { + helmClient := NewClient() + if helmClient.opts.connectTimeout != 5*time.Second { + t.Errorf("expected default timeout duration to be 5 seconds, got %v", helmClient.opts.connectTimeout) + } + + helmClient = NewClient(ConnectTimeout(60)) + if helmClient.opts.connectTimeout != time.Minute { + t.Errorf("expected timeout duration to be 1 minute, got %v", helmClient.opts.connectTimeout) + } +} From a930eb7ff4dcba13831174496dda2445e81ebeaa Mon Sep 17 00:00:00 2001 From: Arash Deshmeh Date: Sat, 6 Jan 2018 12:19:29 -0500 Subject: [PATCH 03/71] feat: add --set and --values options to 'helm package' When 'helm package --set stringsArray' is run, this will set/override values in the packaged chart. 'helm package --values valueFiles' uses one or more value files to achieve the same. Closes #3141 Signed-off-by: Arash Deshmeh --- cmd/helm/package.go | 18 +++++ cmd/helm/package_test.go | 152 ++++++++++++++++++++++++++++++++++++++ docs/helm/helm_package.md | 4 +- 3 files changed, 173 insertions(+), 1 deletion(-) diff --git a/cmd/helm/package.go b/cmd/helm/package.go index ed44382c7..4b2e6b398 100644 --- a/cmd/helm/package.go +++ b/cmd/helm/package.go @@ -53,6 +53,8 @@ type packageCmd struct { save bool sign bool path string + valueFiles valueFiles + values []string key string keyring string version string @@ -95,6 +97,8 @@ func newPackageCmd(out io.Writer) *cobra.Command { } f := cmd.Flags() + f.VarP(&pkg.valueFiles, "values", "f", "specify values in a YAML file or a URL(can specify multiple)") + f.StringArrayVar(&pkg.values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)") f.BoolVar(&pkg.save, "save", true, "save packaged chart to local chart repository") f.BoolVar(&pkg.sign, "sign", false, "use a PGP private key to sign this package") f.StringVar(&pkg.key, "key", "", "name of the key to use when signing. Used if --sign is true") @@ -133,6 +137,20 @@ func (p *packageCmd) run() error { return err } + overrideVals, err := vals(p.valueFiles, p.values) + if err != nil { + return err + } + combinedVals, err := chartutil.CoalesceValues(ch, &chart.Config{Raw: string(overrideVals)}) + if err != nil { + return err + } + newVals, err := combinedVals.YAML() + if err != nil { + return err + } + ch.Values = &chart.Config{Raw: newVals} + // If version is set, modify the version. if len(p.version) != 0 { if err := setVersion(ch, p.version); err != nil { diff --git a/cmd/helm/package_test.go b/cmd/helm/package_test.go index 4404586e0..4a2df3f54 100644 --- a/cmd/helm/package_test.go +++ b/cmd/helm/package_test.go @@ -21,6 +21,7 @@ import ( "os" "path/filepath" "regexp" + "strings" "testing" "github.com/spf13/cobra" @@ -122,6 +123,13 @@ func TestPackage(t *testing.T) { hasfile: "chart-missing-deps-0.1.0.tgz", err: true, }, + { + name: "package --values does-not-exist", + args: []string{"testdata/testcharts/alpine"}, + flags: map[string]string{"values": "does-not-exist"}, + expect: "does-not-exist: no such file or directory", + err: true, + }, } // Because these tests are destructive, we run them in a tempdir. @@ -245,6 +253,150 @@ func TestSetAppVersion(t *testing.T) { } } +func TestPackageValues(t *testing.T) { + testCases := []struct { + desc string + args []string + valuefilesContents []string + flags map[string]string + expected []string + }{ + { + desc: "helm package, single values file", + args: []string{"testdata/testcharts/alpine"}, + valuefilesContents: []string{"Name: chart-name-foo"}, + expected: []string{"Name: chart-name-foo"}, + }, + { + desc: "helm package, multiple values files", + args: []string{"testdata/testcharts/alpine"}, + valuefilesContents: []string{"Name: chart-name-foo", "foo: bar"}, + expected: []string{"Name: chart-name-foo", "foo: bar"}, + }, + { + desc: "helm package, with set option", + args: []string{"testdata/testcharts/alpine"}, + flags: map[string]string{"set": "Name=chart-name-foo"}, + expected: []string{"Name: chart-name-foo"}, + }, + { + desc: "helm package, set takes precedence over value file", + args: []string{"testdata/testcharts/alpine"}, + valuefilesContents: []string{"Name: chart-name-foo"}, + flags: map[string]string{"set": "Name=chart-name-bar"}, + expected: []string{"Name: chart-name-bar"}, + }, + } + + thome, err := tempHelmHome(t) + if err != nil { + t.Fatal(err) + } + cleanup := resetEnv() + defer func() { + os.RemoveAll(thome.String()) + cleanup() + }() + + settings.Home = thome + + for _, tc := range testCases { + var files []string + for _, contents := range tc.valuefilesContents { + f, err := createValuesFile(contents) + if err != nil { + t.Errorf("%q unexpected error creating temporary values file: %q", tc.desc, err) + } + defer os.RemoveAll(filepath.Dir(f)) + files = append(files, f) + } + valueFiles := strings.Join(files, ",") + + expected, err := chartutil.ReadValues([]byte(strings.Join(tc.expected, "\n"))) + if err != nil { + t.Errorf("unexpected error parsing values: %q", err) + } + + runAndVerifyPackageCommandValues(t, tc.args, tc.flags, valueFiles, expected) + } +} + +func runAndVerifyPackageCommandValues(t *testing.T, args []string, flags map[string]string, valueFiles string, expected chartutil.Values) { + outputDir, err := ioutil.TempDir("", "helm-package") + if err != nil { + t.Errorf("unexpected error creating temporary output directory: %q", err) + } + defer os.RemoveAll(outputDir) + + if len(flags) == 0 { + flags = make(map[string]string) + } + flags["destination"] = outputDir + + if len(valueFiles) > 0 { + flags["values"] = valueFiles + } + + cmd := newPackageCmd(&bytes.Buffer{}) + setFlags(cmd, flags) + err = cmd.RunE(cmd, args) + if err != nil { + t.Errorf("unexpected error: %q", err) + } + + outputFile := filepath.Join(outputDir, "alpine-0.1.0.tgz") + verifyOutputChartExists(t, outputFile) + + var actual chartutil.Values + actual, err = getChartValues(outputFile) + if err != nil { + t.Errorf("unexpected error extracting chart values: %q", err) + } + + verifyValues(t, actual, expected) +} + +func createValuesFile(data string) (string, error) { + outputDir, err := ioutil.TempDir("", "values-file") + if err != nil { + return "", err + } + + outputFile := filepath.Join(outputDir, "values.yaml") + if err = ioutil.WriteFile(outputFile, []byte(data), 0755); err != nil { + os.RemoveAll(outputFile) + return "", err + } + + return outputFile, nil +} + +func getChartValues(chartPath string) (chartutil.Values, error) { + + chart, err := chartutil.Load(chartPath) + if err != nil { + return nil, err + } + + return chartutil.ReadValues([]byte(chart.Values.Raw)) +} + +func verifyValues(t *testing.T, actual, expected chartutil.Values) { + for key, value := range expected.AsMap() { + if got := actual[key]; got != value { + t.Errorf("Expected %q, got %q (%v)", value, got, actual) + } + } +} + +func verifyOutputChartExists(t *testing.T, chartPath string) { + if chartFile, err := os.Stat(chartPath); err != nil { + t.Errorf("expected file %q, got err %q", chartPath, err) + } else if chartFile.Size() == 0 { + t.Errorf("file %q has zero bytes.", chartPath) + } +} + func setFlags(cmd *cobra.Command, flags map[string]string) { dest := cmd.Flags() for f, v := range flags { diff --git a/docs/helm/helm_package.md b/docs/helm/helm_package.md index c51aa7032..daceef32c 100644 --- a/docs/helm/helm_package.md +++ b/docs/helm/helm_package.md @@ -29,7 +29,9 @@ helm package [flags] [CHART_PATH] [...] --key string name of the key to use when signing. Used if --sign is true --keyring string location of a public keyring (default "~/.gnupg/pubring.gpg") --save save packaged chart to local chart repository (default true) + --set stringArray set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) --sign use a PGP private key to sign this package + -f, --values valueFiles specify values in a YAML file or a URL(can specify multiple) (default []) --version string set the version on the chart to this semver version ``` @@ -47,4 +49,4 @@ helm package [flags] [CHART_PATH] [...] ### SEE ALSO * [helm](helm.md) - The Helm package manager for Kubernetes. -###### Auto generated by spf13/cobra on 8-Mar-2018 +###### Auto generated by spf13/cobra on 14-Mar-2018 From a43ddcc191f851e343075142a3b0f9a456e95be0 Mon Sep 17 00:00:00 2001 From: Arash Deshmeh Date: Sun, 18 Mar 2018 20:42:05 -0400 Subject: [PATCH 04/71] fix output leak from tiller release install test Signed-off-by: Arash Deshmeh --- pkg/tiller/release_install_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/tiller/release_install_test.go b/pkg/tiller/release_install_test.go index 793a36e94..8e2238e74 100644 --- a/pkg/tiller/release_install_test.go +++ b/pkg/tiller/release_install_test.go @@ -468,7 +468,6 @@ func TestInstallRelease_KubeVersion(t *testing.T) { }, } _, err := rs.InstallRelease(c, req) - fmt.Println(err) if err != nil { t.Fatalf("Expected valid range. Got %q", err) } From 87c64e7987f348c0a99a4f6e12d6ec187723b7cc Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Mon, 19 Mar 2018 15:53:15 -0700 Subject: [PATCH 05/71] fix `helm get manifest` context deadline exceeded error --- cmd/helm/get_manifest.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cmd/helm/get_manifest.go b/cmd/helm/get_manifest.go index 1c42830f0..c01febfb4 100644 --- a/cmd/helm/get_manifest.go +++ b/cmd/helm/get_manifest.go @@ -55,9 +55,7 @@ func newGetManifestCmd(client helm.Interface, out io.Writer) *cobra.Command { return errReleaseRequired } get.release = args[0] - if get.client == nil { - get.client = helm.NewClient(helm.Host(settings.TillerHost)) - } + get.client = ensureHelmClient(get.client) return get.run() }, } From afed555572a1476dcb7c3bebc2701268cfde1de8 Mon Sep 17 00:00:00 2001 From: Daisuke Maki Date: Tue, 20 Mar 2018 09:44:20 +0900 Subject: [PATCH 06/71] Add actionable message in the error See #3697 --- cmd/helm/install.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/helm/install.go b/cmd/helm/install.go index da1ee52d7..6d8d68c6e 100644 --- a/cmd/helm/install.go +++ b/cmd/helm/install.go @@ -443,7 +443,7 @@ func locateChartPath(repoURL, name, version string, verify bool, keyring, return filename, err } - return filename, fmt.Errorf("failed to download %q", name) + return filename, fmt.Errorf("failed to download %q (hint: running `helm repo update` may help)", name) } func generateName(nameTemplate string) (string, error) { From 1d4883bf3c85ea43ed071dff4e02cc47bb66f44f Mon Sep 17 00:00:00 2001 From: Alexey igrychev Date: Wed, 21 Mar 2018 17:19:53 +0300 Subject: [PATCH 07/71] Implement before-hook-creation delete policy Existing helm.sh/hook-delete-policy annotation variables (hook-failed, hook-succeeded) do not allow to leave failed jobs for debugging without blocking the next job launching: every failed job must be deleted manually before the next related release is launching (installing, updating or rolling back). New policy, before-hook-creation, removes the hook from previous release if there is one before the new hook is launched and can be used with another variable. --- _proto/hapi/release/hook.proto | 1 + docs/charts_hooks.md | 17 +++++++- pkg/hooks/hooks.go | 5 ++- pkg/proto/hapi/release/hook.pb.go | 68 ++++++++++++++++--------------- pkg/tiller/hooks.go | 35 ++++++++-------- pkg/tiller/release_server.go | 39 ++++++++++-------- 6 files changed, 97 insertions(+), 68 deletions(-) diff --git a/_proto/hapi/release/hook.proto b/_proto/hapi/release/hook.proto index 22c1fedef..f0332ecb8 100644 --- a/_proto/hapi/release/hook.proto +++ b/_proto/hapi/release/hook.proto @@ -38,6 +38,7 @@ message Hook { enum DeletePolicy { SUCCEEDED = 0; FAILED = 1; + BEFORE_HOOK_CREATION = 2; } string name = 1; // Kind is the Kubernetes kind. diff --git a/docs/charts_hooks.md b/docs/charts_hooks.md index 5fc9462e4..af6d0f4f9 100644 --- a/docs/charts_hooks.md +++ b/docs/charts_hooks.md @@ -180,4 +180,19 @@ It is also possible to define policies that determine when to delete correspondi "helm.sh/hook-delete-policy": hook-succeeded ``` -When using `"helm.sh/hook-delete-policy"` annotation, you can choose its value from `"hook-succeeded"` and `"hook-failed"`. The value `"hook-succeeded"` specifies Tiller should delete the hook after the hook is successfully executed, while the value `"hook-failed"`specifies Tiller should delete the hook if the hook failed during execution. +You can choose one or more defined annotation values: +* `"hook-succeeded"` specifies Tiller should delete the hook after the hook is successfully executed. +* `"hook-failed"` specifies Tiller should delete the hook if the hook failed during execution. +* `"before-hook-creation"` specifies Tiller should delete the previous hook before the new hook is launched. + +### Automatically delete hook from previous release + +When helm release being updated it is possible, that hook resource already exists in cluster. By default helm will try to create resource and fail with `"... already exists"` error. + +One might choose `"helm.sh/hook-delete-policy": "before-hook-creation"` over `"helm.sh/hook-delete-policy": "hook-succeeded,hook-failed"` because: + +* It is convinient to keep failed hook job resource in kubernetes for example for manual debug. +* It may be necessary to keep succeeded hook resource in kubernetes for some reason. +* At the same time it is not desireable to do manual resource deletion before helm release upgrade. + +`"helm.sh/hook-delete-policy": "before-hook-creation"` annotation on hook causes tiller to remove the hook from previous release if there is one before the new hook is launched and can be used with another policies. diff --git a/pkg/hooks/hooks.go b/pkg/hooks/hooks.go index ed2d946a4..80f838368 100644 --- a/pkg/hooks/hooks.go +++ b/pkg/hooks/hooks.go @@ -45,8 +45,9 @@ const ( // Type of policy for deleting the hook const ( - HookSucceeded = "hook-succeeded" - HookFailed = "hook-failed" + HookSucceeded = "hook-succeeded" + HookFailed = "hook-failed" + BeforeHookCreation = "before-hook-creation" ) // FilterTestHooks filters the list of hooks are returns only testing hooks. diff --git a/pkg/proto/hapi/release/hook.pb.go b/pkg/proto/hapi/release/hook.pb.go index 508448280..00fa5c188 100644 --- a/pkg/proto/hapi/release/hook.pb.go +++ b/pkg/proto/hapi/release/hook.pb.go @@ -89,17 +89,20 @@ func (Hook_Event) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []i type Hook_DeletePolicy int32 const ( - Hook_SUCCEEDED Hook_DeletePolicy = 0 - Hook_FAILED Hook_DeletePolicy = 1 + Hook_SUCCEEDED Hook_DeletePolicy = 0 + Hook_FAILED Hook_DeletePolicy = 1 + Hook_BEFORE_HOOK_CREATION Hook_DeletePolicy = 2 ) var Hook_DeletePolicy_name = map[int32]string{ 0: "SUCCEEDED", 1: "FAILED", + 2: "BEFORE_HOOK_CREATION", } var Hook_DeletePolicy_value = map[string]int32{ - "SUCCEEDED": 0, - "FAILED": 1, + "SUCCEEDED": 0, + "FAILED": 1, + "BEFORE_HOOK_CREATION": 2, } func (x Hook_DeletePolicy) String() string { @@ -196,32 +199,33 @@ func init() { func init() { proto.RegisterFile("hapi/release/hook.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 428 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x91, 0xd1, 0x6e, 0xda, 0x30, - 0x14, 0x86, 0x9b, 0x12, 0x12, 0x38, 0x50, 0xea, 0x59, 0xd3, 0x66, 0x71, 0x53, 0xc4, 0x15, 0xbb, - 0x09, 0x53, 0xa7, 0x3d, 0x40, 0x4a, 0xce, 0xd6, 0xaa, 0x11, 0x20, 0x27, 0x68, 0xd2, 0x6e, 0xa2, - 0x74, 0xb8, 0x10, 0x11, 0xe2, 0x88, 0x98, 0x4d, 0x7b, 0xa6, 0xbd, 0xce, 0x1e, 0x68, 0xb2, 0x09, - 0x59, 0xa5, 0xed, 0xee, 0x9c, 0xef, 0x7c, 0x76, 0xce, 0x1f, 0xc3, 0xdb, 0x6d, 0x5a, 0x66, 0xd3, - 0x83, 0xc8, 0x45, 0x5a, 0x89, 0xe9, 0x56, 0xca, 0x9d, 0x57, 0x1e, 0xa4, 0x92, 0xb4, 0xaf, 0x07, - 0x5e, 0x3d, 0x18, 0xde, 0x6c, 0xa4, 0xdc, 0xe4, 0x62, 0x6a, 0x66, 0x4f, 0xc7, 0xe7, 0xa9, 0xca, - 0xf6, 0xa2, 0x52, 0xe9, 0xbe, 0x3c, 0xe9, 0xe3, 0x5f, 0x36, 0xd8, 0xf7, 0x52, 0xee, 0x28, 0x05, - 0xbb, 0x48, 0xf7, 0x82, 0x59, 0x23, 0x6b, 0xd2, 0xe5, 0xa6, 0xd6, 0x6c, 0x97, 0x15, 0x6b, 0x76, - 0x79, 0x62, 0xba, 0xd6, 0xac, 0x4c, 0xd5, 0x96, 0xb5, 0x4e, 0x4c, 0xd7, 0x74, 0x08, 0x9d, 0x7d, - 0x5a, 0x64, 0xcf, 0xa2, 0x52, 0xcc, 0x36, 0xbc, 0xe9, 0xe9, 0x7b, 0x70, 0xc4, 0x77, 0x51, 0xa8, - 0x8a, 0xb5, 0x47, 0xad, 0xc9, 0xe0, 0x96, 0x79, 0x2f, 0x17, 0xf4, 0xf4, 0xb7, 0x3d, 0xd4, 0x02, - 0xaf, 0x3d, 0xfa, 0x11, 0x3a, 0x79, 0x5a, 0xa9, 0xe4, 0x70, 0x2c, 0x98, 0x33, 0xb2, 0x26, 0xbd, - 0xdb, 0xa1, 0x77, 0x8a, 0xe1, 0x9d, 0x63, 0x78, 0xf1, 0x39, 0x06, 0x77, 0xb5, 0xcb, 0x8f, 0x05, - 0x7d, 0x03, 0xce, 0x0f, 0x91, 0x6d, 0xb6, 0x8a, 0xb9, 0x23, 0x6b, 0xd2, 0xe6, 0x75, 0x47, 0xef, - 0xe1, 0x7a, 0x2d, 0x72, 0xa1, 0x44, 0x52, 0xca, 0x3c, 0xfb, 0x96, 0x89, 0x8a, 0x75, 0xcc, 0x26, - 0x37, 0xff, 0xd9, 0x24, 0x30, 0xe6, 0x52, 0x8b, 0x3f, 0xf9, 0x60, 0xfd, 0xb7, 0xcb, 0x44, 0x35, - 0xfe, 0x6d, 0x41, 0xdb, 0xac, 0x4a, 0x7b, 0xe0, 0xae, 0xe6, 0x8f, 0xf3, 0xc5, 0x97, 0x39, 0xb9, - 0xa0, 0xd7, 0xd0, 0x5b, 0x72, 0x4c, 0x1e, 0xe6, 0x51, 0xec, 0x87, 0x21, 0xb1, 0x28, 0x81, 0xfe, - 0x72, 0x11, 0xc5, 0x0d, 0xb9, 0xa4, 0x03, 0x00, 0xad, 0x04, 0x18, 0x62, 0x8c, 0xa4, 0x65, 0x8e, - 0x68, 0xa3, 0x06, 0xf6, 0xf9, 0x8e, 0xd5, 0xf2, 0x33, 0xf7, 0x03, 0x24, 0xed, 0xe6, 0x8e, 0x33, - 0x71, 0x0c, 0xe1, 0x98, 0xf0, 0x45, 0x18, 0xde, 0xf9, 0xb3, 0x47, 0xe2, 0xd2, 0x57, 0x70, 0x65, - 0x9c, 0x06, 0x75, 0x28, 0x83, 0xd7, 0x1c, 0x43, 0xf4, 0x23, 0x4c, 0x62, 0x8c, 0xe2, 0x24, 0x5a, - 0xcd, 0x66, 0x18, 0x45, 0xa4, 0xfb, 0xcf, 0xe4, 0x93, 0xff, 0x10, 0xae, 0x38, 0x12, 0x18, 0xbf, - 0x83, 0xfe, 0xcb, 0xd8, 0xf4, 0x0a, 0xba, 0xe6, 0x18, 0x06, 0x18, 0x90, 0x0b, 0x0a, 0xe0, 0x68, - 0x17, 0x03, 0x62, 0xdd, 0x75, 0xbf, 0xba, 0xf5, 0xef, 0x7a, 0x72, 0xcc, 0x5b, 0x7c, 0xf8, 0x13, - 0x00, 0x00, 0xff, 0xff, 0xb9, 0x8a, 0xe1, 0xaf, 0x89, 0x02, 0x00, 0x00, + // 445 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x91, 0x51, 0x8f, 0x9a, 0x40, + 0x10, 0x80, 0x8f, 0x13, 0x41, 0x47, 0xcf, 0xdb, 0x6e, 0x9a, 0x76, 0xe3, 0xcb, 0x19, 0x9f, 0x7c, + 0xc2, 0xe6, 0x9a, 0xfe, 0x00, 0x84, 0xb9, 0x6a, 0x24, 0x60, 0x16, 0x4c, 0x93, 0xbe, 0x10, 0xae, + 0xee, 0x29, 0x11, 0x81, 0x08, 0xb6, 0xe9, 0x0f, 0xec, 0x3f, 0xe8, 0x0f, 0x6a, 0x76, 0x45, 0x7b, + 0x49, 0xfb, 0x36, 0xf3, 0xcd, 0x37, 0xc3, 0x0c, 0x0b, 0xef, 0x77, 0x49, 0x99, 0x4e, 0x8f, 0x22, + 0x13, 0x49, 0x25, 0xa6, 0xbb, 0xa2, 0xd8, 0x5b, 0xe5, 0xb1, 0xa8, 0x0b, 0xda, 0x97, 0x05, 0xab, + 0x29, 0x0c, 0x1f, 0xb6, 0x45, 0xb1, 0xcd, 0xc4, 0x54, 0xd5, 0x9e, 0x4f, 0x2f, 0xd3, 0x3a, 0x3d, + 0x88, 0xaa, 0x4e, 0x0e, 0xe5, 0x59, 0x1f, 0xff, 0xd2, 0x41, 0x9f, 0x17, 0xc5, 0x9e, 0x52, 0xd0, + 0xf3, 0xe4, 0x20, 0x98, 0x36, 0xd2, 0x26, 0x5d, 0xae, 0x62, 0xc9, 0xf6, 0x69, 0xbe, 0x61, 0xb7, + 0x67, 0x26, 0x63, 0xc9, 0xca, 0xa4, 0xde, 0xb1, 0xd6, 0x99, 0xc9, 0x98, 0x0e, 0xa1, 0x73, 0x48, + 0xf2, 0xf4, 0x45, 0x54, 0x35, 0xd3, 0x15, 0xbf, 0xe6, 0xf4, 0x03, 0x18, 0xe2, 0xbb, 0xc8, 0xeb, + 0x8a, 0xb5, 0x47, 0xad, 0xc9, 0xe0, 0x91, 0x59, 0xaf, 0x17, 0xb4, 0xe4, 0xb7, 0x2d, 0x94, 0x02, + 0x6f, 0x3c, 0xfa, 0x09, 0x3a, 0x59, 0x52, 0xd5, 0xf1, 0xf1, 0x94, 0x33, 0x63, 0xa4, 0x4d, 0x7a, + 0x8f, 0x43, 0xeb, 0x7c, 0x86, 0x75, 0x39, 0xc3, 0x8a, 0x2e, 0x67, 0x70, 0x53, 0xba, 0xfc, 0x94, + 0xd3, 0x77, 0x60, 0xfc, 0x10, 0xe9, 0x76, 0x57, 0x33, 0x73, 0xa4, 0x4d, 0xda, 0xbc, 0xc9, 0xe8, + 0x1c, 0xee, 0x37, 0x22, 0x13, 0xb5, 0x88, 0xcb, 0x22, 0x4b, 0xbf, 0xa5, 0xa2, 0x62, 0x1d, 0xb5, + 0xc9, 0xc3, 0x7f, 0x36, 0x71, 0x95, 0xb9, 0x92, 0xe2, 0x4f, 0x3e, 0xd8, 0xfc, 0xcd, 0x52, 0x51, + 0x8d, 0x7f, 0x6b, 0xd0, 0x56, 0xab, 0xd2, 0x1e, 0x98, 0x6b, 0x7f, 0xe9, 0x07, 0x5f, 0x7c, 0x72, + 0x43, 0xef, 0xa1, 0xb7, 0xe2, 0x18, 0x2f, 0xfc, 0x30, 0xb2, 0x3d, 0x8f, 0x68, 0x94, 0x40, 0x7f, + 0x15, 0x84, 0xd1, 0x95, 0xdc, 0xd2, 0x01, 0x80, 0x54, 0x5c, 0xf4, 0x30, 0x42, 0xd2, 0x52, 0x2d, + 0xd2, 0x68, 0x80, 0x7e, 0x99, 0xb1, 0x5e, 0x7d, 0xe6, 0xb6, 0x8b, 0xa4, 0x7d, 0x9d, 0x71, 0x21, + 0x86, 0x22, 0x1c, 0x63, 0x1e, 0x78, 0xde, 0xcc, 0x76, 0x96, 0xc4, 0xa4, 0x6f, 0xe0, 0x4e, 0x39, + 0x57, 0xd4, 0xa1, 0x0c, 0xde, 0x72, 0xf4, 0xd0, 0x0e, 0x31, 0x8e, 0x30, 0x8c, 0xe2, 0x70, 0xed, + 0x38, 0x18, 0x86, 0xa4, 0xfb, 0x4f, 0xe5, 0xc9, 0x5e, 0x78, 0x6b, 0x8e, 0x04, 0xc6, 0x0e, 0xf4, + 0x5f, 0x9f, 0x4d, 0xef, 0xa0, 0xab, 0xda, 0xd0, 0x45, 0x97, 0xdc, 0x50, 0x00, 0x43, 0xba, 0xe8, + 0x12, 0x4d, 0x0e, 0x99, 0xe1, 0x53, 0xc0, 0x31, 0x9e, 0x07, 0xc1, 0x32, 0x76, 0x38, 0xda, 0xd1, + 0x22, 0xf0, 0xc9, 0xed, 0xac, 0xfb, 0xd5, 0x6c, 0x7e, 0xe4, 0xb3, 0xa1, 0x5e, 0xe9, 0xe3, 0x9f, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x13, 0x64, 0x75, 0x6c, 0xa3, 0x02, 0x00, 0x00, } diff --git a/pkg/tiller/hooks.go b/pkg/tiller/hooks.go index 6ac2cc636..e1e965d08 100644 --- a/pkg/tiller/hooks.go +++ b/pkg/tiller/hooks.go @@ -46,8 +46,9 @@ var events = map[string]release.Hook_Event{ // deletePolices represents a mapping between the key in the annotation for label deleting policy and its real meaning var deletePolices = map[string]release.Hook_DeletePolicy{ - hooks.HookSucceeded: release.Hook_SUCCEEDED, - hooks.HookFailed: release.Hook_FAILED, + hooks.HookSucceeded: release.Hook_SUCCEEDED, + hooks.HookFailed: release.Hook_FAILED, + hooks.BeforeHookCreation: release.Hook_BEFORE_HOOK_CREATION, } // Manifest represents a manifest file, which has a name and some content. @@ -189,21 +190,14 @@ func (file *manifestFile) sort(result *result) error { result.hooks = append(result.hooks, h) - isKnownDeletePolices := false - dps, ok := entry.Metadata.Annotations[hooks.HookDeleteAnno] - if ok { - for _, dp := range strings.Split(dps, ",") { - dp = strings.ToLower(strings.TrimSpace(dp)) - p, exist := deletePolices[dp] - if exist { - isKnownDeletePolices = true - h.DeletePolicies = append(h.DeletePolicies, p) - } + operateAnnotationValues(entry, hooks.HookDeleteAnno, func(value string) { + policy, exist := deletePolices[value] + if exist { + h.DeletePolicies = append(h.DeletePolicies, policy) + } else { + log.Printf("info: skipping unknown hook delete policy: %q", value) } - if !isKnownDeletePolices { - log.Printf("info: skipping unknown hook delete policy: %q", dps) - } - } + }) } return nil @@ -228,3 +222,12 @@ func calculateHookWeight(entry util.SimpleHead) int32 { return int32(hw) } + +func operateAnnotationValues(entry util.SimpleHead, annotation string, operate func(p string)) { + if dps, ok := entry.Metadata.Annotations[annotation]; ok { + for _, dp := range strings.Split(dps, ",") { + dp = strings.ToLower(strings.TrimSpace(dp)) + operate(dp) + } + } +} diff --git a/pkg/tiller/release_server.go b/pkg/tiller/release_server.go index a96c64938..eaa6515bb 100644 --- a/pkg/tiller/release_server.go +++ b/pkg/tiller/release_server.go @@ -347,6 +347,9 @@ func (s *ReleaseServer) execHook(hs []*release.Hook, name, namespace, hook strin executingHooks = sortByHookWeight(executingHooks) for _, h := range executingHooks { + if err := s.deleteHookIfShouldBeDeletedByDeletePolicy(h, hooks.BeforeHookCreation, name, namespace, hook, kubeCli); err != nil { + return err + } b := bytes.NewBufferString(h.Manifest) if err := kubeCli.Create(namespace, b, timeout, false); err != nil { @@ -356,18 +359,13 @@ func (s *ReleaseServer) execHook(hs []*release.Hook, name, namespace, hook strin // No way to rewind a bytes.Buffer()? b.Reset() b.WriteString(h.Manifest) + if err := kubeCli.WatchUntilReady(namespace, b, timeout, false); err != nil { s.Log("warning: Release %s %s %s could not complete: %s", name, hook, h.Path, err) // If a hook is failed, checkout the annotation of the hook to determine whether the hook should be deleted // under failed condition. If so, then clear the corresponding resource object in the hook - if hookShouldBeDeleted(h, hooks.HookFailed) { - b.Reset() - b.WriteString(h.Manifest) - s.Log("deleting %s hook %s for release %s due to %q policy", hook, h.Name, name, hooks.HookFailed) - if errHookDelete := kubeCli.Delete(namespace, b); errHookDelete != nil { - s.Log("warning: Release %s %s %S could not be deleted: %s", name, hook, h.Path, errHookDelete) - return errHookDelete - } + if err := s.deleteHookIfShouldBeDeletedByDeletePolicy(h, hooks.HookFailed, name, namespace, hook, kubeCli); err != nil { + return err } return err } @@ -377,13 +375,8 @@ func (s *ReleaseServer) execHook(hs []*release.Hook, name, namespace, hook strin // If all hooks are succeeded, checkout the annotation of each hook to determine whether the hook should be deleted // under succeeded condition. If so, then clear the corresponding resource object in each hook for _, h := range executingHooks { - b := bytes.NewBufferString(h.Manifest) - if hookShouldBeDeleted(h, hooks.HookSucceeded) { - s.Log("deleting %s hook %s for release %s due to %q policy", hook, h.Name, name, hooks.HookSucceeded) - if errHookDelete := kubeCli.Delete(namespace, b); errHookDelete != nil { - s.Log("warning: Release %s %s %S could not be deleted: %s", name, hook, h.Path, errHookDelete) - return errHookDelete - } + if err := s.deleteHookIfShouldBeDeletedByDeletePolicy(h, hooks.HookSucceeded, name, namespace, hook, kubeCli); err != nil { + return err } h.LastRun = timeconv.Now() } @@ -409,11 +402,23 @@ func validateReleaseName(releaseName string) error { return nil } +func (s *ReleaseServer) deleteHookIfShouldBeDeletedByDeletePolicy(h *release.Hook, policy string, name, namespace, hook string, kubeCli environment.KubeClient) error { + b := bytes.NewBufferString(h.Manifest) + if hookHasDeletePolicy(h, policy) { + s.Log("deleting %s hook %s for release %s due to %q policy", hook, h.Name, name, policy) + if errHookDelete := kubeCli.Delete(namespace, b); errHookDelete != nil { + s.Log("warning: Release %s %s %S could not be deleted: %s", name, hook, h.Path, errHookDelete) + return errHookDelete + } + } + return nil +} + // hookShouldBeDeleted determines whether the defined hook deletion policy matches the hook deletion polices // supported by helm. If so, mark the hook as one should be deleted. -func hookShouldBeDeleted(hook *release.Hook, policy string) bool { +func hookHasDeletePolicy(h *release.Hook, policy string) bool { if dp, ok := deletePolices[policy]; ok { - for _, v := range hook.DeletePolicies { + for _, v := range h.DeletePolicies { if dp == v { return true } From 332dc83c46b1cb135bc42b7afa1c9b50629e0e15 Mon Sep 17 00:00:00 2001 From: Morgan Parry Date: Wed, 13 Dec 2017 15:50:25 +0000 Subject: [PATCH 08/71] (fix) Handle caFile alone being set for repos Previously it was only respected if certFile and keyFile were also specified. However, these are independent features. --- pkg/getter/httpgetter.go | 15 +++---------- pkg/getter/httpgetter_test.go | 20 +++++++++++++---- pkg/tlsutil/tls.go | 42 ++++++++++++++++++++++++++++------- 3 files changed, 53 insertions(+), 24 deletions(-) diff --git a/pkg/getter/httpgetter.go b/pkg/getter/httpgetter.go index dd462ce5f..d04b1076c 100644 --- a/pkg/getter/httpgetter.go +++ b/pkg/getter/httpgetter.go @@ -23,7 +23,6 @@ import ( "strings" "k8s.io/helm/pkg/tlsutil" - "k8s.io/helm/pkg/urlutil" "k8s.io/helm/pkg/version" ) @@ -80,19 +79,11 @@ func newHTTPGetter(URL, CertFile, KeyFile, CAFile string) (Getter, error) { // NewHTTPGetter constructs a valid http/https client as HttpGetter func NewHTTPGetter(URL, CertFile, KeyFile, CAFile string) (*HttpGetter, error) { var client HttpGetter - if CertFile != "" && KeyFile != "" { - tlsConf, err := tlsutil.NewClientTLS(CertFile, KeyFile, CAFile) + if (CertFile != "" && KeyFile != "") || CAFile != "" { + tlsConf, err := tlsutil.NewTLSConfig(URL, CertFile, KeyFile, CAFile) if err != nil { - return &client, fmt.Errorf("can't create TLS config for client: %s", err.Error()) + return &client, fmt.Errorf("can't create TLS config: %s", err.Error()) } - tlsConf.BuildNameToCertificate() - - sni, err := urlutil.ExtractHostname(URL) - if err != nil { - return &client, err - } - tlsConf.ServerName = sni - client.client = &http.Client{ Transport: &http.Transport{ TLSClientConfig: tlsConf, diff --git a/pkg/getter/httpgetter_test.go b/pkg/getter/httpgetter_test.go index fe3fde22a..ec730f401 100644 --- a/pkg/getter/httpgetter_test.go +++ b/pkg/getter/httpgetter_test.go @@ -28,7 +28,7 @@ func TestHTTPGetter(t *testing.T) { } if hg, ok := g.(*HttpGetter); !ok { - t.Fatal("Expected newHTTPGetter to produce an httpGetter") + t.Fatal("Expected newHTTPGetter to produce an HttpGetter") } else if hg.client != http.DefaultClient { t.Fatal("Expected newHTTPGetter to return a default HTTP client.") } @@ -37,12 +37,24 @@ func TestHTTPGetter(t *testing.T) { cd := "../../testdata" join := filepath.Join ca, pub, priv := join(cd, "ca.pem"), join(cd, "crt.pem"), join(cd, "key.pem") - g, err = newHTTPGetter("http://example.com/", pub, priv, ca) + g, err = newHTTPGetter("https://example.com/", pub, priv, ca) if err != nil { t.Fatal(err) } + if hg, ok := g.(*HttpGetter); !ok { + t.Fatal("Expected newHTTPGetter to produce an HttpGetter") + } else if hg.client == http.DefaultClient { + t.Fatal("Expected newHTTPGetter to return a non-default HTTP client") + } - if _, ok := g.(*HttpGetter); !ok { - t.Fatal("Expected newHTTPGetter to produce an httpGetter") + // Test with SSL, caFile only + g, err = newHTTPGetter("https://example.com/", "", "", ca) + if err != nil { + t.Fatal(err) + } + if hg, ok := g.(*HttpGetter); !ok { + t.Fatal("Expected newHTTPGetter to produce an HttpGetter") + } else if hg.client == http.DefaultClient { + t.Fatal("Expected newHTTPGetter to return a non-default HTTP client") } } diff --git a/pkg/tlsutil/tls.go b/pkg/tlsutil/tls.go index df698fd4e..c166a1662 100644 --- a/pkg/tlsutil/tls.go +++ b/pkg/tlsutil/tls.go @@ -21,17 +21,20 @@ import ( "crypto/x509" "fmt" "io/ioutil" + "k8s.io/helm/pkg/urlutil" ) -// NewClientTLS returns tls.Config appropriate for client auth. -func NewClientTLS(certFile, keyFile, caFile string) (*tls.Config, error) { - cert, err := CertFromFilePair(certFile, keyFile) - if err != nil { - return nil, err - } - config := tls.Config{ - Certificates: []tls.Certificate{*cert}, +func newTLSConfigCommon(certFile, keyFile, caFile string) (*tls.Config, error) { + config := tls.Config{} + + if certFile != "" && keyFile != "" { + cert, err := CertFromFilePair(certFile, keyFile) + if err != nil { + return nil, err + } + config.Certificates = []tls.Certificate{*cert} } + if caFile != "" { cp, err := CertPoolFromFile(caFile) if err != nil { @@ -39,9 +42,32 @@ func NewClientTLS(certFile, keyFile, caFile string) (*tls.Config, error) { } config.RootCAs = cp } + return &config, nil } +// NewClientTLS returns tls.Config appropriate for client auth. +func NewClientTLS(certFile, keyFile, caFile string) (*tls.Config, error) { + return newTLSConfigCommon(certFile, keyFile, caFile) +} + +// NewTLSConfig returns tls.Config appropriate for client and/or server auth. +func NewTLSConfig(url, certFile, keyFile, caFile string) (*tls.Config, error) { + config, err := newTLSConfigCommon(certFile, keyFile, caFile) + if err != nil { + return nil, err + } + config.BuildNameToCertificate() + + serverName, err := urlutil.ExtractHostname(url) + if err != nil { + return nil, err + } + config.ServerName = serverName + + return config, nil +} + // CertPoolFromFile returns an x509.CertPool containing the certificates // in the given PEM-encoded file. // Returns an error if the file could not be read, a certificate could not From 141e0155903f85d245e156746fadfca4bf6734fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helgi=20=C3=9Eorbj=C3=B6rnsson?= Date: Wed, 21 Mar 2018 17:28:27 -0700 Subject: [PATCH 09/71] Create index.yaml if missing when running repo index --merge Fixes #3682 --- cmd/helm/repo_index.go | 14 +++++++++++--- cmd/helm/repo_index_test.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/cmd/helm/repo_index.go b/cmd/helm/repo_index.go index 95e4b8aac..540057eb8 100644 --- a/cmd/helm/repo_index.go +++ b/cmd/helm/repo_index.go @@ -19,6 +19,7 @@ package main import ( "fmt" "io" + "os" "path/filepath" "github.com/spf13/cobra" @@ -86,9 +87,16 @@ func index(dir, url, mergeTo string) error { return err } if mergeTo != "" { - i2, err := repo.LoadIndexFile(mergeTo) - if err != nil { - return fmt.Errorf("Merge failed: %s", err) + // if index.yaml is missing then create an empty one to merge into + var i2 *repo.IndexFile + if _, err := os.Stat(mergeTo); os.IsNotExist(err) { + i2 = repo.NewIndexFile() + i2.WriteFile(mergeTo, 0755) + } else { + i2, err = repo.LoadIndexFile(mergeTo) + if err != nil { + return fmt.Errorf("Merge failed: %s", err) + } } i.Merge(i2) } diff --git a/cmd/helm/repo_index_test.go b/cmd/helm/repo_index_test.go index bd1010dd7..4d6313f6c 100644 --- a/cmd/helm/repo_index_test.go +++ b/cmd/helm/repo_index_test.go @@ -112,6 +112,36 @@ func TestRepoIndexCmd(t *testing.T) { if vs[0].Version != expectedVersion { t.Errorf("expected %q, got %q", expectedVersion, vs[0].Version) } + + // test that index.yaml gets generated on merge even when it doesn't exist + if err := os.Remove(destIndex); err != nil { + t.Fatal(err) + } + + c.ParseFlags([]string{"--merge", destIndex}) + if err := c.RunE(c, []string{dir}); err != nil { + t.Error(err) + } + + _, err = repo.LoadIndexFile(destIndex) + if err != nil { + t.Fatal(err) + } + + // verify it didn't create an empty index.yaml and the merged happened + if len(index.Entries) != 2 { + t.Errorf("expected 2 entries, got %d: %#v", len(index.Entries), index.Entries) + } + + vs = index.Entries["compressedchart"] + if len(vs) != 3 { + t.Errorf("expected 3 versions, got %d: %#v", len(vs), vs) + } + + expectedVersion = "0.3.0" + if vs[0].Version != expectedVersion { + t.Errorf("expected %q, got %q", expectedVersion, vs[0].Version) + } } func linkOrCopy(old, new string) error { From 502db80b263a9498535f42c71e36adcdbacad4fa Mon Sep 17 00:00:00 2001 From: James Strachan Date: Thu, 22 Mar 2018 08:22:09 +0000 Subject: [PATCH 10/71] add a link to Jenkins X and how it uses helm for promoting applications through environments via GitOps --- docs/related.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/related.md b/docs/related.md index c14eb3880..dfe0b1c1f 100644 --- a/docs/related.md +++ b/docs/related.md @@ -69,6 +69,7 @@ Tools layered on top of Helm or Tiller. - [ChartMuseum](https://github.com/chartmuseum/chartmuseum) - Helm Chart Repository with support for Amazon S3 and Google Cloud Storage - [Helm.NET](https://github.com/qmfrederik/helm) - A .NET client for Tiller's API - [Codefresh](https://codefresh.io) - Kubernetes native CI/CD and management platform with UI dashboards for managing Helm charts and releases +- [Jenkins X](http://jenkins-x.io/) - open source automated C/CD for Kubernetes which uses Helm for [promoting](http://jenkins-x.io/about/features/#promotion) applications through [environments via GitOps](http://jenkins-x.io/about/features/#environments) ## Helm Included From 908addd6fc778515fc0b2ed54a5050149554b961 Mon Sep 17 00:00:00 2001 From: Ferenc- Date: Thu, 22 Mar 2018 14:51:45 +0100 Subject: [PATCH 11/71] Fix HTML parsing when setting TAG with wget Now the parsing part is the same as in the curl scenario --- scripts/get | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/get b/scripts/get index 6654fd226..3c4fb9f7d 100755 --- a/scripts/get +++ b/scripts/get @@ -80,7 +80,7 @@ checkDesiredVersion() { if type "curl" > /dev/null; then TAG=$(curl -SsL $release_url | awk '/\/tag\//' | grep -v no-underline | cut -d '"' -f 2 | awk '{n=split($NF,a,"/");print a[n]}' | awk 'a !~ $0{print}; {a=$0}') elif type "wget" > /dev/null; then - TAG=$(wget -q -O - $release_url | awk '/\/tag\//' | cut -d '"' -f 2 | awk '{n=split($NF,a,"/");print a[n]}' | awk 'a !~ $0{print}; {a=$0}') + TAG=$(wget -q -O - $release_url | awk '/\/tag\//' | grep -v no-underline | cut -d '"' -f 2 | awk '{n=split($NF,a,"/");print a[n]}' | awk 'a !~ $0{print}; {a=$0}') fi if [ "x$TAG" == "x" ]; then echo "Cannot determine ${DESIRED_VERSION} tag." From bddb6591d46ce176e87668ed458da54fa0da5eaf Mon Sep 17 00:00:00 2001 From: fqsghostcloud Date: Thu, 22 Mar 2018 23:21:32 +0800 Subject: [PATCH 12/71] fix Syntax error fix Syntax error --- docs/chart_repository.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/chart_repository.md b/docs/chart_repository.md index 5a24249b8..01d457e63 100644 --- a/docs/chart_repository.md +++ b/docs/chart_repository.md @@ -275,7 +275,7 @@ fantastic-charts https://fantastic-charts.storage.googleapis.com If the charts are backed by HTTP basic authentication, you can also supply the username and password here: -``console +```console $ helm repo add fantastic-charts https://fantastic-charts.storage.googleapis.com --username my-username --password my-password $ helm repo list fantastic-charts https://fantastic-charts.storage.googleapis.com From 49cda9a81d4453f2e9b67f701dd1db9a48561bb4 Mon Sep 17 00:00:00 2001 From: Justin Scott Date: Thu, 22 Mar 2018 12:23:11 -0700 Subject: [PATCH 13/71] Add issue_template.md which asks for version info. --- .github/issue_template.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .github/issue_template.md diff --git a/.github/issue_template.md b/.github/issue_template.md new file mode 100644 index 000000000..f4a3a8219 --- /dev/null +++ b/.github/issue_template.md @@ -0,0 +1,9 @@ + + +Helm version: + +Kubernetes version: + +Cloud Provider/Platform (AKS, GKE, Minikube etc.): + + From 537bfbb75ef25cc3f3e18607d98a7c2633aab9f0 Mon Sep 17 00:00:00 2001 From: Justin Scott Date: Thu, 22 Mar 2018 13:21:03 -0700 Subject: [PATCH 14/71] Update to specify version command output. --- .github/issue_template.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/issue_template.md b/.github/issue_template.md index f4a3a8219..48f48e5b6 100644 --- a/.github/issue_template.md +++ b/.github/issue_template.md @@ -1,8 +1,8 @@ -Helm version: +Output of `helm version`: -Kubernetes version: +Output of `kubectl version`: Cloud Provider/Platform (AKS, GKE, Minikube etc.): From 6abbee0d44f2890ad76cb5f2bd09a79a540a63d9 Mon Sep 17 00:00:00 2001 From: James Strachan Date: Fri, 23 Mar 2018 06:33:25 +0000 Subject: [PATCH 15/71] fixed typo + moved into Helm Included section --- docs/related.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/related.md b/docs/related.md index dfe0b1c1f..997e3f01d 100644 --- a/docs/related.md +++ b/docs/related.md @@ -69,7 +69,6 @@ Tools layered on top of Helm or Tiller. - [ChartMuseum](https://github.com/chartmuseum/chartmuseum) - Helm Chart Repository with support for Amazon S3 and Google Cloud Storage - [Helm.NET](https://github.com/qmfrederik/helm) - A .NET client for Tiller's API - [Codefresh](https://codefresh.io) - Kubernetes native CI/CD and management platform with UI dashboards for managing Helm charts and releases -- [Jenkins X](http://jenkins-x.io/) - open source automated C/CD for Kubernetes which uses Helm for [promoting](http://jenkins-x.io/about/features/#promotion) applications through [environments via GitOps](http://jenkins-x.io/about/features/#environments) ## Helm Included @@ -79,6 +78,7 @@ Platforms, distributions, and services that include Helm support. - [Cabin](http://www.skippbox.com/cabin/) - Mobile App for Managing Kubernetes - [Qstack](https://qstack.com) - [Fabric8](https://fabric8.io) - Integrated development platform for Kubernetes +- [Jenkins X](http://jenkins-x.io/) - open source automated CI/CD for Kubernetes which uses Helm for [promoting](http://jenkins-x.io/about/features/#promotion) applications through [environments via GitOps](http://jenkins-x.io/about/features/#environments) ## Misc From 9d0dc294d76d1401809b586a75e7bec000f4aa22 Mon Sep 17 00:00:00 2001 From: Russell Morrisey Date: Fri, 23 Mar 2018 15:43:54 -0400 Subject: [PATCH 16/71] Updated to mention the --purge flag on delete See: https://github.com/kubernetes/helm/issues/2332 --- docs/install_faq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/install_faq.md b/docs/install_faq.md index ff9ee89e6..f2eae5b48 100644 --- a/docs/install_faq.md +++ b/docs/install_faq.md @@ -224,7 +224,7 @@ I am trying to remove stuff. **Q: When I delete the Tiller deployment, how come all the releases are still there?** Releases are stored in ConfigMaps inside of the `kube-system` namespace. You will -have to manually delete them to get rid of the record. +have to manually delete them to get rid of the record, or use ```helm delete --purge```. **Q: I want to delete my local Helm. Where are all its files?** From 31c03a24845a29fd4bf32c5ad934424acc9bf18e Mon Sep 17 00:00:00 2001 From: AdamDang Date: Sun, 25 Mar 2018 15:51:57 +0800 Subject: [PATCH 17/71] Update tiller_ssl.md "that" is duplicated. --- docs/tiller_ssl.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tiller_ssl.md b/docs/tiller_ssl.md index db1603068..d7f0166c4 100644 --- a/docs/tiller_ssl.md +++ b/docs/tiller_ssl.md @@ -251,7 +251,7 @@ This configuration sends our client-side certificate to establish identity, uses the client key for encryption, and uses the CA certificate to validate the remote Tiller's identity. -Typing a line that that is cumbersome, though. The shortcut is to move the key, +Typing a line that is cumbersome, though. The shortcut is to move the key, cert, and CA into `$HELM_HOME`: ```console From 7c19cb7040810f0eaedb50ca29eafb108bd9611e Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Mon, 26 Mar 2018 22:15:36 -0700 Subject: [PATCH 18/71] ref(*): kubernetes v1.10 support --- glide.lock | 184 +++++++++----------------------- glide.yaml | 20 ++-- pkg/kube/client.go | 14 ++- pkg/kube/client_test.go | 226 ++++++++++++---------------------------- 4 files changed, 133 insertions(+), 311 deletions(-) diff --git a/glide.lock b/glide.lock index 1ccdae61c..068f86372 100644 --- a/glide.lock +++ b/glide.lock @@ -1,71 +1,20 @@ -hash: d93f565214b112cf8560e9cd2da2f3ab7852a1f19544569fc112bd4fb2d1d506 -updated: 2018-03-08T14:06:06.497394911-08:00 +hash: 6837936360d447b64aa7a09d3c89c18ac5540b009a57fc4d3227af299bf40268 +updated: 2018-03-27T05:13:24.967040785Z imports: -- name: cloud.google.com/go - version: 3b1ae45394a234c385be014e9a488f2bb6eef821 - repo: https://github.com/GoogleCloudPlatform/google-cloud-go.git - subpackages: - - compute - - compute/metadata - - internal - name: github.com/aokoli/goutils version: 9c37978a95bd5c709a15883b6242714ea6709e64 - name: github.com/asaskevich/govalidator version: 7664702784775e51966f0885f5cd27435916517b -- name: github.com/aws/aws-sdk-go - version: 760741802ad40f49ae9fc4a69ef6706d2527d62e - subpackages: - - aws - - aws/awserr - - aws/awsutil - - aws/client - - aws/client/metadata - - aws/corehandlers - - aws/credentials - - aws/credentials/ec2rolecreds - - aws/credentials/endpointcreds - - aws/credentials/stscreds - - aws/defaults - - aws/ec2metadata - - aws/endpoints - - aws/request - - aws/session - - aws/signer/v4 - - internal/shareddefaults - - private/protocol - - private/protocol/ec2query - - private/protocol/json/jsonutil - - private/protocol/jsonrpc - - private/protocol/query - - private/protocol/query/queryutil - - private/protocol/rest - - private/protocol/xml/xmlutil - - service/autoscaling - - service/ec2 - - service/ecr - - service/elb - - service/elbv2 - - service/kms - - service/sts - name: github.com/Azure/go-ansiterm version: 19f72df4d05d31cbe1c56bfc8045c96babff6c7e subpackages: - winterm -- name: github.com/Azure/go-autorest - version: e14a70c556c8e0db173358d1a903dca345a8e75e - subpackages: - - autorest - - autorest/adal - - autorest/azure - - autorest/date - name: github.com/beorn7/perks version: 3ac7bf7a47d159a033b107610db8a1b6575507a4 subpackages: - quantile - name: github.com/BurntSushi/toml version: b26d9c308763d68093482582cea63d69be07a0f0 -- name: github.com/chai2010/gettext-go - version: bf70f2a70fb1b1f36d90d671a72795984eab0fcb - name: github.com/cpuguy83/go-md2man version: 71acacd42f85e5e82f70a55327789582a5200a90 subpackages: @@ -74,8 +23,6 @@ imports: version: 782f4967f2dc4564575ca782fe2d04090b5faca8 subpackages: - spew -- name: github.com/dgrijalva/jwt-go - version: 01aeca54ebda6e0fbfafd0a524d234159c05ec20 - name: github.com/docker/distribution version: edc3ab29cdff8694dd6feb85cfeb4b5f1b38ed9c subpackages: @@ -106,8 +53,9 @@ imports: - pkg/jsonmessage - pkg/longpath - pkg/mount + - pkg/parsers - pkg/stdcopy - - pkg/symlink + - pkg/sysinfo - pkg/system - pkg/term - pkg/term/windows @@ -124,10 +72,6 @@ imports: version: 449fdfce4d962303d702fec724ef0ad181c92528 subpackages: - spdy -- name: github.com/emicklei/go-restful - version: ff4f55a206334ef123e4f79bbf348980da81ca46 - subpackages: - - log - name: github.com/evanphx/json-patch version: 944e07253867aacae43c04b2e6a239005443f33a - name: github.com/exponent-io/jsonpath @@ -136,14 +80,12 @@ imports: version: f6a740d52f961c60348ebb109adde9f4635d7540 - name: github.com/ghodss/yaml version: 73d445a93680fa1a78ae23a5839bad48f32ba1ee -- name: github.com/go-ini/ini - version: 300e940a926eb277d3901b20bdfcc54928ad3642 - name: github.com/go-openapi/jsonpointer version: 46af16f9f7b149af66e5d1bd010e3574dc06de98 - name: github.com/go-openapi/jsonreference version: 13c6e3589ad90f49bd3e3bbe2c2cb3d7a4142272 - name: github.com/go-openapi/spec - version: 7abd5745472fff5eb3685386d5fb8bf38683154d + version: 1de3e0542de65ad8d75452a595886fdd0befb363 - name: github.com/go-openapi/swag version: f3f9494671f93fcff853e3c6e9e948b3eb71e590 - name: github.com/gobwas/glob @@ -185,15 +127,6 @@ imports: - OpenAPIv2 - compiler - extensions -- name: github.com/gophercloud/gophercloud - version: 8183543f90d1aef267a5ecc209f2e0715b355acb - subpackages: - - openstack - - openstack/identity/v2/tenants - - openstack/identity/v2/tokens - - openstack/identity/v3/tokens - - openstack/utils - - pagination - name: github.com/gosuri/uitable version: 36ee7e946282a3fb1cfecd476ddc9b35d8847e42 subpackages: @@ -217,12 +150,8 @@ imports: version: 6633656539c1639d9d78127b7d47c622b5d7b6dc - name: github.com/inconshreveable/mousetrap version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 -- name: github.com/jmespath/go-jmespath - version: 0b12d6b521d83fc7f755e7cfc1b1fbdd35a01a74 - name: github.com/json-iterator/go - version: 36b14963da70d11297d313183d7e6388c8510e1e -- name: github.com/juju/ratelimit - version: 5b9ff866471762aa2ab2dced63c9fb6f53921342 + version: 13f86432b882000a51c6e610c620974462691a97 - name: github.com/mailru/easyjson version: 2f5df55504ebc322e4d52d34df6a1f5b503bf26d subpackages: @@ -245,8 +174,6 @@ imports: - pbutil - name: github.com/mitchellh/go-wordwrap version: ad45545899c7b13c020ea92b2072220eefad42b8 -- name: github.com/naoina/go-stringutil - version: 6b638e95a32d0c1131db0e7fe83775cbea4a0d0b - name: github.com/opencontainers/go-digest version: a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb - name: github.com/opencontainers/image-spec @@ -301,6 +228,8 @@ imports: version: 81e90905daefcd6fd217b62423c0908922eadb30 subpackages: - cast5 + - ed25519 + - ed25519/internal/edwards25519 - openpgp - openpgp/armor - openpgp/clearsign @@ -315,20 +244,12 @@ imports: version: 1c05540f6879653db88113bc4a2b70aec4bd491f subpackages: - context - - context/ctxhttp - http2 - http2/hpack - idna - internal/timeseries - lex/httplex - trace -- name: golang.org/x/oauth2 - version: a6bd8cefa1811bd24b86f8902872e4e8225f74c4 - subpackages: - - google - - internal - - jws - - jwt - name: golang.org/x/sys version: 43eea11bc92608addb41b8a406b0407495c106f6 subpackages: @@ -353,18 +274,10 @@ imports: - unicode/bidi - unicode/norm - width -- name: google.golang.org/appengine - version: 12d5545dc1cfa6047a286d5e853841b6471f4c19 +- name: golang.org/x/time + version: f51c12702a4d776e4c1fa9b0fabab841babae631 subpackages: - - internal - - internal/app_identity - - internal/base - - internal/datastore - - internal/log - - internal/modules - - internal/remote_api - - internal/urlfetch - - urlfetch + - rate - name: google.golang.org/genproto version: 09f6ed296fc66555a25fe4ce95173148778dfa85 subpackages: @@ -390,20 +303,18 @@ imports: - status - tap - transport -- name: gopkg.in/gcfg.v1 - version: 27e4946190b4a327b539185f2b5b1f7c84730728 - subpackages: - - scanner - - token - - types - name: gopkg.in/inf.v0 version: 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4 -- name: gopkg.in/warnings.v0 - version: 8a331561fe74dadba6edfc59f3be66c22c3b065d +- name: gopkg.in/square/go-jose.v2 + version: f8f38de21b4dcd69d0413faf231983f5fd6634b1 + subpackages: + - cipher + - json + - jwt - name: gopkg.in/yaml.v2 version: 53feefa2559fb8dfa8d81baad31be332c97d6c77 - name: k8s.io/api - version: 006a217681ae70cbacdd66a5e2fca1a61a8ff28e + version: c699ec51538f0cfd4afa8bfcfe1e0779cafbe666 subpackages: - admission/v1beta1 - admissionregistration/v1alpha1 @@ -436,11 +347,11 @@ imports: - storage/v1alpha1 - storage/v1beta1 - name: k8s.io/apiextensions-apiserver - version: a5bbfd114a9b122acd741c61d88c84812375d9e1 + version: 898b0eda132e1aeac43a459785144ee4bf9b0a2e subpackages: - pkg/features - name: k8s.io/apimachinery - version: 68f9c3a1feb3140df59c67ced62d3a5df8e6c9c2 + version: 54101a56dda9a0962bc48751c058eb4c546dcbb9 subpackages: - pkg/api/equality - pkg/api/errors @@ -454,7 +365,7 @@ imports: - pkg/apis/meta/v1 - pkg/apis/meta/v1/unstructured - pkg/apis/meta/v1/validation - - pkg/apis/meta/v1alpha1 + - pkg/apis/meta/v1beta1 - pkg/conversion - pkg/conversion/queryparams - pkg/fields @@ -472,6 +383,7 @@ imports: - pkg/util/cache - pkg/util/clock - pkg/util/diff + - pkg/util/duration - pkg/util/errors - pkg/util/framer - pkg/util/httpstream @@ -496,7 +408,7 @@ imports: - third_party/forked/golang/netutil - third_party/forked/golang/reflect - name: k8s.io/apiserver - version: 2a1092aaa7202e8f9b188281ff9424a014ce61c2 + version: ea53f8588c655568158b4ff53f5ec6fa4ebfc332 subpackages: - pkg/apis/audit - pkg/authentication/authenticator @@ -507,7 +419,7 @@ imports: - pkg/util/feature - pkg/util/flag - name: k8s.io/client-go - version: 78700dec6369ba22221b72770783300f143df150 + version: 23781f4d6632d88e869066eaebb743857aa1ef9b subpackages: - discovery - discovery/fake @@ -635,15 +547,21 @@ imports: - listers/storage/v1 - listers/storage/v1alpha1 - listers/storage/v1beta1 + - pkg/apis/clientauthentication + - pkg/apis/clientauthentication/v1alpha1 - pkg/version - - plugin/pkg/client/auth - - plugin/pkg/client/auth/azure - - plugin/pkg/client/auth/gcp - - plugin/pkg/client/auth/oidc - - plugin/pkg/client/auth/openstack + - plugin/pkg/client/auth/exec - rest - rest/fake - rest/watch + - scale + - scale/scheme + - scale/scheme/appsint + - scale/scheme/appsv1beta1 + - scale/scheme/appsv1beta2 + - scale/scheme/autoscalingv1 + - scale/scheme/extensionsint + - scale/scheme/extensionsv1beta1 - testing - third_party/forked/golang/template - tools/auth @@ -670,13 +588,12 @@ imports: - util/retry - util/workqueue - name: k8s.io/kube-openapi - version: 39a7bf85c140f972372c2a0d1ee40adbf0c8bfe1 + version: 50ae88d24ede7b8bad68e23c805b5d3da5c8abaf subpackages: - - pkg/common - pkg/util/proto - pkg/util/proto/validation - name: k8s.io/kubernetes - version: 5fa2db2bd46ac79e5e00a4e6ed24191080aa463b + version: 923d8441db527e908cd7d266c4a9d18daa596d4c subpackages: - pkg/api/events - pkg/api/legacyscheme @@ -686,7 +603,6 @@ imports: - pkg/api/service - pkg/api/testapi - pkg/api/v1/pod - - pkg/api/v1/service - pkg/apis/admission - pkg/apis/admission/install - pkg/apis/admission/v1beta1 @@ -799,9 +715,8 @@ imports: - pkg/client/clientset_generated/internalclientset/typed/settings/internalversion/fake - pkg/client/clientset_generated/internalclientset/typed/storage/internalversion - pkg/client/clientset_generated/internalclientset/typed/storage/internalversion/fake - - pkg/client/unversioned + - pkg/client/conditions - pkg/cloudprovider - - pkg/cloudprovider/providers/aws - pkg/controller - pkg/controller/daemon - pkg/controller/daemon/util @@ -810,8 +725,8 @@ imports: - pkg/controller/statefulset - pkg/controller/volume/events - pkg/controller/volume/persistentvolume + - pkg/controller/volume/persistentvolume/metrics - pkg/credentialprovider - - pkg/credentialprovider/aws - pkg/features - pkg/fieldpath - pkg/kubectl @@ -830,6 +745,7 @@ imports: - pkg/kubectl/util/hash - pkg/kubectl/util/slice - pkg/kubectl/util/term + - pkg/kubectl/util/transport - pkg/kubectl/validation - pkg/kubelet/apis - pkg/kubelet/types @@ -837,6 +753,13 @@ imports: - pkg/printers - pkg/printers/internalversion - pkg/registry/rbac/validation + - pkg/scheduler/algorithm + - pkg/scheduler/algorithm/predicates + - pkg/scheduler/algorithm/priorities/util + - pkg/scheduler/api + - pkg/scheduler/schedulercache + - pkg/scheduler/util + - pkg/scheduler/volumebinder - pkg/security/apparmor - pkg/serviceaccount - pkg/util/file @@ -858,14 +781,9 @@ imports: - pkg/version - pkg/volume - pkg/volume/util - - pkg/watch/json - - plugin/pkg/scheduler/algorithm - - plugin/pkg/scheduler/algorithm/predicates - - plugin/pkg/scheduler/algorithm/priorities/util - - plugin/pkg/scheduler/api - - plugin/pkg/scheduler/schedulercache - - plugin/pkg/scheduler/util - - plugin/pkg/scheduler/volumebinder + - pkg/volume/util/fs + - pkg/volume/util/recyclerclient + - pkg/volume/util/types - name: k8s.io/utils version: aedf551cdb8b0119df3a19c65fde413a13b34997 subpackages: @@ -874,8 +792,6 @@ imports: - exec/testing - name: vbom.ml/util version: db5cfe13f5cc80a4990d98e2e1b0707a4d1a5394 - repo: https://github.com/fvbommel/util.git - vcs: git subpackages: - sortorder testImports: diff --git a/glide.yaml b/glide.yaml index 7d0fce978..7fcb16d0b 100644 --- a/glide.yaml +++ b/glide.yaml @@ -43,28 +43,20 @@ import: - package: github.com/evanphx/json-patch - package: github.com/BurntSushi/toml version: ~0.3.0 -- package: github.com/naoina/go-stringutil - version: ~0.1.0 -- package: github.com/chai2010/gettext-go - package: github.com/prometheus/client_golang version: 0.8.0 -- package: vbom.ml/util - repo: https://github.com/fvbommel/util.git - vcs: git +- package: github.com/grpc-ecosystem/go-grpc-prometheus - package: k8s.io/kubernetes - version: 1.9.2 + version: release-1.10 - package: k8s.io/client-go - version: ~6.0.0 + version: kubernetes-1.10.0 - package: k8s.io/api - version: kubernetes-1.9.2 + version: release-1.10 - package: k8s.io/apimachinery - version: kubernetes-1.9.2 + version: release-1.10 - package: k8s.io/apiserver - version: kubernetes-1.9.2 - -- package: cloud.google.com/go/compute - repo: https://github.com/GoogleCloudPlatform/google-cloud-go.git + version: release-1.10 testImports: - package: github.com/stretchr/testify diff --git a/pkg/kube/client.go b/pkg/kube/client.go index cd5227dd7..e4e2dc1bb 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -47,7 +47,7 @@ import ( "k8s.io/client-go/tools/clientcmd" batchinternal "k8s.io/kubernetes/pkg/apis/batch" "k8s.io/kubernetes/pkg/apis/core" - conditions "k8s.io/kubernetes/pkg/client/unversioned" + "k8s.io/kubernetes/pkg/client/conditions" "k8s.io/kubernetes/pkg/kubectl" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/resource" @@ -77,10 +77,12 @@ func New(config clientcmd.ClientConfig) *Client { return &Client{ Factory: cmdutil.NewFactory(config), SchemaCacheDir: clientcmd.RecommendedSchemaFile, - Log: func(_ string, _ ...interface{}) {}, + Log: nopLogger, } } +var nopLogger = func(_ string, _ ...interface{}) {} + // ResourceActorFunc performs an action on a single resource. type ResourceActorFunc func(*resource.Info) error @@ -205,7 +207,10 @@ func (c *Client) Get(namespace string, reader io.Reader) (string, error) { // an object type changes, so we can just rely on that. Problem is it doesn't seem to keep // track of tab widths. buf := new(bytes.Buffer) - p, _ := c.Printer(nil, printers.PrintOptions{}) + p, err := cmdutil.PrinterForOptions(&printers.PrintOptions{}) + if err != nil { + return "", err + } for t, ot := range objs { if _, err = buf.WriteString("==> " + t + "\n"); err != nil { return "", err @@ -608,7 +613,8 @@ func (c *Client) AsVersionedObject(obj runtime.Object) (runtime.Object, error) { return nil, err } versions := &runtime.VersionedObjects{} - err = runtime.DecodeInto(c.Decoder(true), json, versions) + decoder := unstructured.UnstructuredJSONScheme + err = runtime.DecodeInto(decoder, json, versions) return versions.First(), err } diff --git a/pkg/kube/client_test.go b/pkg/kube/client_test.go index a81b2c949..47049810a 100644 --- a/pkg/kube/client_test.go +++ b/pkg/kube/client_test.go @@ -18,8 +18,6 @@ package kube import ( "bytes" - "encoding/json" - "fmt" "io" "io/ioutil" "net/http" @@ -31,19 +29,20 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/dynamic" "k8s.io/client-go/rest/fake" + "k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/apis/core" "k8s.io/kubernetes/pkg/kubectl" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/resource" - "k8s.io/kubernetes/pkg/printers" - watchjson "k8s.io/kubernetes/pkg/watch/json" + "k8s.io/kubernetes/pkg/kubectl/scheme" ) +var unstructuredSerializer = dynamic.ContentConfig().NegotiatedSerializer + func objBody(codec runtime.Codec, obj runtime.Object) io.ReadCloser { return ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(codec, obj)))) } @@ -117,31 +116,21 @@ func (f *fakeReaperFactory) Reaper(mapping *meta.RESTMapping) (kubectl.Reaper, e return f.reaper, nil } -func newEventResponse(code int, e *watch.Event) (*http.Response, error) { - dispatchedEvent, err := encodeAndMarshalEvent(e) - if err != nil { - return nil, err - } - - header := http.Header{} - header.Set("Content-Type", runtime.ContentTypeJSON) - body := ioutil.NopCloser(bytes.NewReader(dispatchedEvent)) - return &http.Response{StatusCode: code, Header: header, Body: body}, nil +type testClient struct { + *Client + *cmdtesting.TestFactory } -func encodeAndMarshalEvent(e *watch.Event) ([]byte, error) { - encodedEvent, err := watchjson.Object(testapi.Default.Codec(), e) - if err != nil { - return nil, err +func newTestClient() *testClient { + tf := cmdtesting.NewTestFactory() + c := &Client{ + Factory: tf, + Log: nopLogger, + } + return &testClient{ + Client: c, + TestFactory: tf, } - - return json.Marshal(encodedEvent) -} - -func newTestClient(f cmdutil.Factory) *Client { - c := New(nil) - c.Factory = f - return c } func TestUpdate(t *testing.T) { @@ -153,10 +142,11 @@ func TestUpdate(t *testing.T) { var actions []string - f, tf, codec, _ := cmdtesting.NewAPIFactory() + tf := cmdtesting.NewTestFactory() + defer tf.Cleanup() tf.UnstructuredClient = &fake.RESTClient{ GroupVersion: schema.GroupVersion{Version: "v1"}, - NegotiatedSerializer: dynamic.ContentConfig().NegotiatedSerializer, + NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { p, m := req.URL.Path, req.Method actions = append(actions, p+":"+m) @@ -190,9 +180,11 @@ func TestUpdate(t *testing.T) { }), } + c := newTestClient() reaper := &fakeReaper{} - rf := &fakeReaperFactory{Factory: f, reaper: reaper} - c := newTestClient(rf) + rf := &fakeReaperFactory{Factory: tf, reaper: reaper} + c.Client.Factory = rf + codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...) if err := c.Update(core.NamespaceDefault, objBody(codec, &listA), objBody(codec, &listB), false, false, 0, false); err != nil { t.Fatal(err) } @@ -251,54 +243,35 @@ func TestBuild(t *testing.T) { }, } + c := newTestClient() for _, tt := range tests { - f, _, _, _ := cmdtesting.NewAPIFactory() - c := newTestClient(f) - - // Test for an invalid manifest - infos, err := c.Build(tt.namespace, tt.reader) - if err != nil && !tt.err { - t.Errorf("%q. Got error message when no error should have occurred: %v", tt.name, err) - } else if err != nil && strings.Contains(err.Error(), "--validate=false") { - t.Errorf("%q. error message was not scrubbed", tt.name) - } + t.Run(tt.name, func(t *testing.T) { + c.Cleanup() + + // Test for an invalid manifest + infos, err := c.Build(tt.namespace, tt.reader) + if err != nil && !tt.err { + t.Errorf("Got error message when no error should have occurred: %v", err) + } else if err != nil && strings.Contains(err.Error(), "--validate=false") { + t.Error("error message was not scrubbed") + } - if len(infos) != tt.count { - t.Errorf("%q. expected %d result objects, got %d", tt.name, tt.count, len(infos)) - } + if len(infos) != tt.count { + t.Errorf("expected %d result objects, got %d", tt.count, len(infos)) + } + }) } } -type testPrinter struct { - Objects []runtime.Object - Err error - printers.ResourcePrinter -} - -func (t *testPrinter) PrintObj(obj runtime.Object, out io.Writer) error { - t.Objects = append(t.Objects, obj) - fmt.Fprintf(out, "%#v", obj) - return t.Err -} - -func (t *testPrinter) HandledResources() []string { - return []string{} -} - -func (t *testPrinter) AfterPrint(io.Writer, string) error { - return t.Err -} - func TestGet(t *testing.T) { list := newPodList("starfish", "otter") - f, tf, _, _ := cmdtesting.NewAPIFactory() - tf.Printer = &testPrinter{} - tf.UnstructuredClient = &fake.RESTClient{ + c := newTestClient() + defer c.Cleanup() + c.TestFactory.UnstructuredClient = &fake.RESTClient{ GroupVersion: schema.GroupVersion{Version: "v1"}, - NegotiatedSerializer: dynamic.ContentConfig().NegotiatedSerializer, + NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { p, m := req.URL.Path, req.Method - //actions = append(actions, p+":"+m) t.Logf("got request %s %s", p, m) switch { case p == "/namespaces/default/pods/starfish" && m == "GET": @@ -311,7 +284,6 @@ func TestGet(t *testing.T) { } }), } - c := newTestClient(f) // Test Success data := strings.NewReader("kind: Pod\napiVersion: v1\nmetadata:\n name: otter") @@ -358,101 +330,37 @@ func TestPerform(t *testing.T) { } for _, tt := range tests { - results := []*resource.Info{} + t.Run(tt.name, func(t *testing.T) { + results := []*resource.Info{} - fn := func(info *resource.Info) error { - results = append(results, info) + fn := func(info *resource.Info) error { + results = append(results, info) - if info.Namespace != tt.namespace { - t.Errorf("%q. expected namespace to be '%s', got %s", tt.name, tt.namespace, info.Namespace) + if info.Namespace != tt.namespace { + t.Errorf("expected namespace to be '%s', got %s", tt.namespace, info.Namespace) + } + return nil } - return nil - } - f, _, _, _ := cmdtesting.NewAPIFactory() - c := newTestClient(f) - infos, err := c.Build(tt.namespace, tt.reader) - if err != nil && err.Error() != tt.errMessage { - t.Errorf("%q. Error while building manifests: %v", tt.name, err) - } - - err = perform(infos, fn) - if (err != nil) != tt.err { - t.Errorf("%q. expected error: %v, got %v", tt.name, tt.err, err) - } - if err != nil && err.Error() != tt.errMessage { - t.Errorf("%q. expected error message: %v, got %v", tt.name, tt.errMessage, err) - } - - if len(results) != tt.count { - t.Errorf("%q. expected %d result objects, got %d", tt.name, tt.count, len(results)) - } - } -} - -func TestWaitAndGetCompletedPodPhase(t *testing.T) { - tests := []struct { - podPhase core.PodPhase - expectedPhase core.PodPhase - err bool - errMessage string - }{ - { - podPhase: core.PodPending, - expectedPhase: core.PodUnknown, - err: true, - errMessage: "watch closed before Until timeout", - }, { - podPhase: core.PodRunning, - expectedPhase: core.PodUnknown, - err: true, - errMessage: "watch closed before Until timeout", - }, { - podPhase: core.PodSucceeded, - expectedPhase: core.PodSucceeded, - }, { - podPhase: core.PodFailed, - expectedPhase: core.PodFailed, - }, - } - - for _, tt := range tests { - f, tf, codec, ns := cmdtesting.NewAPIFactory() - actions := make(map[string]string) - - var testPodList core.PodList - testPodList.Items = append(testPodList.Items, newPodWithStatus("bestpod", core.PodStatus{Phase: tt.podPhase}, "test")) - - tf.Client = &fake.RESTClient{ - NegotiatedSerializer: ns, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - p, m := req.URL.Path, req.Method - actions[p] = m - switch { - case p == "/namespaces/test/pods/bestpod" && m == "GET": - return newResponse(200, &testPodList.Items[0]) - case p == "/namespaces/test/pods" && m == "GET": - event := watch.Event{Type: watch.Added, Object: &testPodList.Items[0]} - return newEventResponse(200, &event) - default: - t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) - return nil, nil - } - }), - } + c := newTestClient() + defer c.Cleanup() + infos, err := c.Build(tt.namespace, tt.reader) + if err != nil && err.Error() != tt.errMessage { + t.Errorf("Error while building manifests: %v", err) + } - c := newTestClient(f) + err = perform(infos, fn) + if (err != nil) != tt.err { + t.Errorf("expected error: %v, got %v", tt.err, err) + } + if err != nil && err.Error() != tt.errMessage { + t.Errorf("expected error message: %v, got %v", tt.errMessage, err) + } - phase, err := c.WaitAndGetCompletedPodPhase("test", objBody(codec, &testPodList), 1*time.Second) - if (err != nil) != tt.err { - t.Fatalf("Expected error but there was none.") - } - if err != nil && err.Error() != tt.errMessage { - t.Fatalf("Expected error %s, got %s", tt.errMessage, err.Error()) - } - if phase != tt.expectedPhase { - t.Fatalf("Expected pod phase %s, got %s", tt.expectedPhase, phase) - } + if len(results) != tt.count { + t.Errorf("expected %d result objects, got %d", tt.count, len(results)) + } + }) } } From 52b7abac26ca0088082121aa26215bf411ad928b Mon Sep 17 00:00:00 2001 From: Shredgar Date: Tue, 27 Mar 2018 14:52:32 +0200 Subject: [PATCH 19/71] Updated kubernetes_distros.md with DC/OS note. Updated kubernetes_distros.md with a note that Helm is running fine on Kubernetes on DC/OS --- docs/kubernetes_distros.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/kubernetes_distros.md b/docs/kubernetes_distros.md index 04a86d0b4..c30bc87f3 100644 --- a/docs/kubernetes_distros.md +++ b/docs/kubernetes_distros.md @@ -43,3 +43,9 @@ Helm works straightforward on OpenShift Online, OpenShift Dedicated, OpenShift C ## Platform9 Helm Client and Helm Server (Tiller) are pre-installed with [Platform9 Managed Kubernetes](https://platform9.com/managed-kubernetes/?utm_source=helm_distro_notes). Platform9 provides access to all official Helm charts through the App Catalog UI and native Kubernetes CLI. Additional repositories can be manually added. Further details are available in this [Platform9 App Catalog article](https://platform9.com/support/deploying-kubernetes-apps-platform9-managed-kubernetes/?utm_source=helm_distro_notes). + +## DC/OS ++ ++Helm (both client and server) has been tested and is working on Mesospheres DC/OS Kubernetes platform, and requires ++no additional configuration. + From 6e3a0a129172db913491a83443813ea0ae3861c5 Mon Sep 17 00:00:00 2001 From: Jonathan Hall Date: Tue, 27 Mar 2018 19:56:46 +0200 Subject: [PATCH 20/71] Remove outdated note. --- docs/provenance.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/docs/provenance.md b/docs/provenance.md index 1eb69f8f3..331074e8c 100644 --- a/docs/provenance.md +++ b/docs/provenance.md @@ -4,13 +4,6 @@ Helm has provenance tools which help chart users verify the integrity and origin of a package. Using industry-standard tools based on PKI, GnuPG, and well-respected package managers, Helm can generate and verify signature files. -**Note:** -Version 2.0.0-alpha.4 introduced a system for verifying the authenticity of charts. -While we do not anticipate that any major changes will be made to the file formats -or provenancing algorithms, this portion of Helm is not considered _frozen_ until -2.0.0-RC1 is released. The original plan for this feature can be found -[at issue 983](https://github.com/kubernetes/helm/issues/983). - ## Overview Integrity is established by comparing a chart to a provenance record. Provenance From 1a55161a53345816d724e1df9664f789113e1328 Mon Sep 17 00:00:00 2001 From: adshmh Date: Tue, 27 Mar 2018 16:06:04 -0400 Subject: [PATCH 21/71] Fix several golint warnings (#3756) * fix(helm): fix golint warning due to ApiVersionV1 constant name Signed-off-by: Arash Deshmeh * fix(helm): fix golint warning due to ResolveChartVersionAndGetRepo comment Signed-off-by: Arash Deshmeh * fix(helm): fix golint warnings on HttpGetter type and SetCredentials method missing a comment Signed-off-by: Arash Deshmeh * fix(helm):fix golint warning due to comment on FindChartInAuthRepoURL function Signed-off-by: Arash Deshmeh * fix(helm): fix golint warning due to RepoFile type name Signed-off-by: Arash Deshmeh * fix(helm): fix golint warning due to ParseString comment Signed-off-by: Arash Deshmeh --- pkg/chartutil/chartfile.go | 2 +- pkg/downloader/chart_downloader.go | 2 +- pkg/getter/httpgetter.go | 6 ++++-- pkg/repo/chartrepo.go | 6 +++--- pkg/repo/repo.go | 3 ++- pkg/strvals/parser.go | 2 +- 6 files changed, 12 insertions(+), 9 deletions(-) diff --git a/pkg/chartutil/chartfile.go b/pkg/chartutil/chartfile.go index 9897d66ff..c2879cdae 100644 --- a/pkg/chartutil/chartfile.go +++ b/pkg/chartutil/chartfile.go @@ -31,7 +31,7 @@ import ( // ApiVersionV1 is the API version number for version 1. // // This is ApiVersionV1 instead of APIVersionV1 to match the protobuf-generated name. -const ApiVersionV1 = "v1" +const ApiVersionV1 = "v1" // nolint // UnmarshalChartfile takes raw Chart.yaml data and unmarshals it. func UnmarshalChartfile(data []byte) (*chart.Metadata, error) { diff --git a/pkg/downloader/chart_downloader.go b/pkg/downloader/chart_downloader.go index 9fe89820e..9262ea547 100644 --- a/pkg/downloader/chart_downloader.go +++ b/pkg/downloader/chart_downloader.go @@ -151,7 +151,7 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, ge return u, nil, err } -// Same as the ResolveChartVersion method, but returns the chart repositoryy. +// ResolveChartVersionAndGetRepo is the same as the ResolveChartVersion method, but returns the chart repositoryy. func (c *ChartDownloader) ResolveChartVersionAndGetRepo(ref, version string) (*url.URL, *repo.ChartRepository, *getter.HttpGetter, error) { u, err := url.Parse(ref) if err != nil { diff --git a/pkg/getter/httpgetter.go b/pkg/getter/httpgetter.go index dd462ce5f..3c20e35e1 100644 --- a/pkg/getter/httpgetter.go +++ b/pkg/getter/httpgetter.go @@ -27,13 +27,15 @@ import ( "k8s.io/helm/pkg/version" ) -//httpGetter is the efault HTTP(/S) backend handler -type HttpGetter struct { +//HttpGetter is the efault HTTP(/S) backend handler +// TODO: change the name to HTTPGetter in Helm 3 +type HttpGetter struct { //nolint client *http.Client username string password string } +//SetCredentials sets the credentials for the getter func (g *HttpGetter) SetCredentials(username, password string) { g.username = username g.password = password diff --git a/pkg/repo/chartrepo.go b/pkg/repo/chartrepo.go index c76cc7913..bf03a68bb 100644 --- a/pkg/repo/chartrepo.go +++ b/pkg/repo/chartrepo.go @@ -196,9 +196,9 @@ func FindChartInRepoURL(repoURL, chartName, chartVersion, certFile, keyFile, caF return FindChartInAuthRepoURL(repoURL, "", "", chartName, chartVersion, certFile, keyFile, caFile, getters) } -// FindChartInRepoURL finds chart in chart repository pointed by repoURL -// without adding repo to repositories. -// Unlike the FindChartInRepoURL function, this function also receives credentials for the chart repository. +// FindChartInAuthRepoURL finds chart in chart repository pointed by repoURL +// without adding repo to repositories, like FindChartInRepoURL, +// but it also receives credentials for the chart repository. func FindChartInAuthRepoURL(repoURL, username, password, chartName, chartVersion, certFile, keyFile, caFile string, getters getter.Providers) (string, error) { // Download and write the index file to a temporary location diff --git a/pkg/repo/repo.go b/pkg/repo/repo.go index 194eace79..b5bba164e 100644 --- a/pkg/repo/repo.go +++ b/pkg/repo/repo.go @@ -31,7 +31,8 @@ import ( var ErrRepoOutOfDate = errors.New("repository file is out of date") // RepoFile represents the repositories.yaml file in $HELM_HOME -type RepoFile struct { +// TODO: change type name to File in Helm 3 to resolve linter warning +type RepoFile struct { // nolint APIVersion string `json:"apiVersion"` Generated time.Time `json:"generated"` Repositories []*Entry `json:"repositories"` diff --git a/pkg/strvals/parser.go b/pkg/strvals/parser.go index 8a999feb7..8a16adf7e 100644 --- a/pkg/strvals/parser.go +++ b/pkg/strvals/parser.go @@ -50,7 +50,7 @@ func Parse(s string) (map[string]interface{}, error) { return vals, err } -// Parse parses a set line and forces a string value. +// ParseString parses a set line and forces a string value. // // A set line is of the form name1=value1,name2=value2 func ParseString(s string) (map[string]interface{}, error) { From 75387fe3183a724684b2bea29c2a0023e7a9a354 Mon Sep 17 00:00:00 2001 From: muhongwei <32874157+muhongwei@users.noreply.github.com> Date: Wed, 28 Mar 2018 04:06:48 +0800 Subject: [PATCH 22/71] fix 'mulitenant'->'multitenant' (#3755) --- docs/securing_installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/securing_installation.md b/docs/securing_installation.md index ffce5df75..5c420242e 100644 --- a/docs/securing_installation.md +++ b/docs/securing_installation.md @@ -71,7 +71,7 @@ Enabling this feature currently requires setting the `--storage=secret` flag in Because of the relative longevity of Helm, the Helm chart ecosystem evolved without the immediate concern for cluster-wide control, and especially in the developer space this makes complete sense. However, charts are a kind of package that not only installs containers you may or may not have validated yourself, but it may also install into more than one namespace. -As with all shared software, in a controlled or shared environment you must validate all software you install yourself _before_ you install it. If you have secured Tiller with TLS and have installed it with permissions to only one or a subset of namespaces, some charts may fail to install -- but in these environments, that is exactly what you want. If you need to use the chart, you may have to work with the creator or modify it yourself in order to use it securely in a mulitenant cluster with proper RBAC rules applied. The `helm template` command renders the chart locally and displays the output. +As with all shared software, in a controlled or shared environment you must validate all software you install yourself _before_ you install it. If you have secured Tiller with TLS and have installed it with permissions to only one or a subset of namespaces, some charts may fail to install -- but in these environments, that is exactly what you want. If you need to use the chart, you may have to work with the creator or modify it yourself in order to use it securely in a multitenant cluster with proper RBAC rules applied. The `helm template` command renders the chart locally and displays the output. Once vetted, you can use Helm's provenance tools to [ensure the provenance and integrity of charts](provenance.md) that you use. From 0ba75b6652aec058c51fc1152988e01403496318 Mon Sep 17 00:00:00 2001 From: muhongwei <32874157+muhongwei@users.noreply.github.com> Date: Wed, 28 Mar 2018 04:07:13 +0800 Subject: [PATCH 23/71] fix 'eveything' -> 'everything' (#3754) --- docs/release_checklist.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/release_checklist.md b/docs/release_checklist.md index 9bb3ae00c..26506985c 100644 --- a/docs/release_checklist.md +++ b/docs/release_checklist.md @@ -197,7 +197,7 @@ From here on just repeat this process, continuously testing until you're happy w ## 6. Finalize the Release -When you're finally happy with the quality of a release candidate, you can move on and create the real thing. Double-check one last time to make sure eveything is in order, then finally push the release tag. +When you're finally happy with the quality of a release candidate, you can move on and create the real thing. Double-check one last time to make sure everything is in order, then finally push the release tag. ```shell git checkout $RELEASE_BRANCH_NAME From 3334309a9193b61da6b24ceceb5435ef4d2f71ed Mon Sep 17 00:00:00 2001 From: Shredgar Date: Wed, 28 Mar 2018 10:12:56 +0200 Subject: [PATCH 24/71] Removed typos (additional plus signs) Removed typos (additional plus signs) --- docs/kubernetes_distros.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/kubernetes_distros.md b/docs/kubernetes_distros.md index c30bc87f3..8b80519ec 100644 --- a/docs/kubernetes_distros.md +++ b/docs/kubernetes_distros.md @@ -45,7 +45,7 @@ Helm works straightforward on OpenShift Online, OpenShift Dedicated, OpenShift C Helm Client and Helm Server (Tiller) are pre-installed with [Platform9 Managed Kubernetes](https://platform9.com/managed-kubernetes/?utm_source=helm_distro_notes). Platform9 provides access to all official Helm charts through the App Catalog UI and native Kubernetes CLI. Additional repositories can be manually added. Further details are available in this [Platform9 App Catalog article](https://platform9.com/support/deploying-kubernetes-apps-platform9-managed-kubernetes/?utm_source=helm_distro_notes). ## DC/OS -+ -+Helm (both client and server) has been tested and is working on Mesospheres DC/OS Kubernetes platform, and requires -+no additional configuration. + +Helm (both client and server) has been tested and is working on Mesospheres DC/OS 1.11 Kubernetes platform, and requires +no additional configuration. From 2dd9b34d28338e24a646d393ed4e522be55b7851 Mon Sep 17 00:00:00 2001 From: fibonacci1729 Date: Wed, 28 Mar 2018 15:43:23 -0600 Subject: [PATCH 25/71] stream releases when listing --- cmd/helm/list.go | 18 +++++++++++------- pkg/helm/client.go | 18 ++++++++++++++++-- pkg/tiller/release_list.go | 17 +++++++++++------ 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/cmd/helm/list.go b/cmd/helm/list.go index 0219d60f2..4332ceb21 100644 --- a/cmd/helm/list.go +++ b/cmd/helm/list.go @@ -148,7 +148,7 @@ func (l *listCmd) run() error { return prettyError(err) } - if len(res.Releases) == 0 { + if len(res.GetReleases()) == 0 { return nil } @@ -239,12 +239,16 @@ func formatList(rels []*release.Release, colWidth uint) string { table.MaxColWidth = colWidth table.AddRow("NAME", "REVISION", "UPDATED", "STATUS", "CHART", "NAMESPACE") for _, r := range rels { - c := fmt.Sprintf("%s-%s", r.Chart.Metadata.Name, r.Chart.Metadata.Version) - t := timeconv.String(r.Info.LastDeployed) - s := r.Info.Status.Code.String() - v := r.Version - n := r.Namespace - table.AddRow(r.Name, v, t, s, c, n) + md := r.GetChart().GetMetadata() + c := fmt.Sprintf("%s-%s", md.GetName(), md.GetVersion()) + t := "-" + if tspb := r.GetInfo().GetLastDeployed(); tspb != nil { + t = timeconv.String(tspb) + } + s := r.GetInfo().GetStatus().GetCode().String() + v := r.GetVersion() + n := r.GetNamespace() + table.AddRow(r.GetName(), v, t, s, c, n) } return table.String() } diff --git a/pkg/helm/client.go b/pkg/helm/client.go index 2fb1e54e4..43e9f4daf 100644 --- a/pkg/helm/client.go +++ b/pkg/helm/client.go @@ -346,8 +346,22 @@ func (h *Client) list(ctx context.Context, req *rls.ListReleasesRequest) (*rls.L if err != nil { return nil, err } - - return s.Recv() + var resp *rls.ListReleasesResponse + for { + r, err := s.Recv() + if err == io.EOF { + break + } + if err != nil { + return nil, err + } + if resp == nil { + resp = r + continue + } + resp.Releases = append(resp.Releases, r.GetReleases()[0]) + } + return resp, nil } // Executes tiller.InstallRelease RPC. diff --git a/pkg/tiller/release_list.go b/pkg/tiller/release_list.go index 9ccc8a686..ec4dbfb39 100644 --- a/pkg/tiller/release_list.go +++ b/pkg/tiller/release_list.go @@ -108,13 +108,18 @@ func (s *ReleaseServer) ListReleases(req *services.ListReleasesRequest, stream s l = int64(len(rels)) } - res := &services.ListReleasesResponse{ - Next: next, - Count: l, - Total: total, - Releases: rels, + for i := 0; i < min(len(rels), int(req.Limit)); i++ { + res := &services.ListReleasesResponse{ + Next: next, + Count: l, + Total: total, + Releases: []*release.Release{rels[i]}, + } + if err := stream.Send(res); err != nil { + return err + } } - return stream.Send(res) + return nil } func filterByNamespace(namespace string, rels []*release.Release) ([]*release.Release, error) { From 343acc5a60c8d63272f6ad9511473218e6b7a6ea Mon Sep 17 00:00:00 2001 From: Ryan Hartje Date: Wed, 28 Mar 2018 18:51:26 -0400 Subject: [PATCH 26/71] return a non 0 exit code when lint fails due to missing Chart.yaml --- cmd/helm/lint.go | 8 ++++++++ cmd/helm/lint_test.go | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/cmd/helm/lint.go b/cmd/helm/lint.go index 6e08f2747..e83026117 100644 --- a/cmd/helm/lint.go +++ b/cmd/helm/lint.go @@ -97,10 +97,14 @@ func (l *lintCmd) run() error { var total int var failures int + rc := 0 for _, path := range l.paths { if linter, err := lintChart(path, rvals, l.namespace, l.strict); err != nil { fmt.Println("==> Skipping", path) fmt.Println(err) + if err == errLintNoChart { + rc = 1 + } } else { fmt.Println("==> Linting", path) @@ -127,6 +131,10 @@ func (l *lintCmd) run() error { fmt.Fprintf(l.out, "%s, no failures\n", msg) + if rc != 0 { + os.Exit(rc) + } + return nil } diff --git a/cmd/helm/lint_test.go b/cmd/helm/lint_test.go index 7f045153c..973af9b63 100644 --- a/cmd/helm/lint_test.go +++ b/cmd/helm/lint_test.go @@ -28,6 +28,7 @@ var ( archivedChartPathWithHyphens = "testdata/testcharts/compressedchart-with-hyphens-0.1.0.tgz" invalidArchivedChartPath = "testdata/testcharts/invalidcompressedchart0.1.0.tgz" chartDirPath = "testdata/testcharts/decompressedchart/" + chartMissingManifest = "testdata/testcharts/chart-missing-manifest" ) func TestLintChart(t *testing.T) { @@ -46,4 +47,8 @@ func TestLintChart(t *testing.T) { if _, err := lintChart(invalidArchivedChartPath, values, namespace, strict); err == nil { t.Errorf("Expected a chart parsing error") } + + if _, err := lintChart(chartMissingManifest, values, namespace, strict); err == nil { + t.Errorf("Expected a chart parsing error") + } } From 1e03f1bce5eaab384c8ddabbf38b8f566d1c1d14 Mon Sep 17 00:00:00 2001 From: John Koleszar Date: Wed, 28 Mar 2018 18:05:54 -0700 Subject: [PATCH 27/71] Fix tiller deployment on RBAC clusters Adds automountServiceAccountToken when a serviceAccount is specified. Prior to this, tiller falls back to contacting the KUBERNETES_SERVICE on localhost:8080 rather than respecting the cluster IP in the KUBERNETES_SERVICE_{HOST,PORT} environment variables. Fixes #3460, fixes #3467. --- cmd/helm/installer/install.go | 4 +++- cmd/helm/installer/install_test.go | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/cmd/helm/installer/install.go b/cmd/helm/installer/install.go index fc81fa26b..a45179a48 100644 --- a/cmd/helm/installer/install.go +++ b/cmd/helm/installer/install.go @@ -176,6 +176,7 @@ func generateDeployment(opts *Options) (*v1beta1.Deployment, error) { return nil, err } } + automountServiceAccountToken := opts.ServiceAccount != "" d := &v1beta1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Namespace: opts.Namespace, @@ -189,7 +190,8 @@ func generateDeployment(opts *Options) (*v1beta1.Deployment, error) { Labels: labels, }, Spec: v1.PodSpec{ - ServiceAccountName: opts.ServiceAccount, + ServiceAccountName: opts.ServiceAccount, + AutomountServiceAccountToken: &automountServiceAccountToken, Containers: []v1.Container{ { Name: "tiller", diff --git a/cmd/helm/installer/install_test.go b/cmd/helm/installer/install_test.go index dbb7143e3..80219505a 100644 --- a/cmd/helm/installer/install_test.go +++ b/cmd/helm/installer/install_test.go @@ -96,6 +96,9 @@ func TestDeploymentManifestForServiceAccount(t *testing.T) { if got := d.Spec.Template.Spec.ServiceAccountName; got != tt.serviceAccount { t.Errorf("%s: expected service account value %q, got %q", tt.name, tt.serviceAccount, got) } + if got := *d.Spec.Template.Spec.AutomountServiceAccountToken; got != (tt.serviceAccount != "") { + t.Errorf("%s: unexpected automountServiceAccountToken = %t for serviceAccount %q", tt.name, got, tt.serviceAccount) + } } } From e0056a9e41d5432192ee4d103a72e36ba9aeec36 Mon Sep 17 00:00:00 2001 From: Arash Deshmeh Date: Thu, 29 Mar 2018 05:08:56 -0400 Subject: [PATCH 28/71] fix(helm): fix output leak from unit tests of helm create command Signed-off-by: Arash Deshmeh --- cmd/helm/create_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/helm/create_test.go b/cmd/helm/create_test.go index cb7b1d387..84ccebece 100644 --- a/cmd/helm/create_test.go +++ b/cmd/helm/create_test.go @@ -46,7 +46,7 @@ func TestCreateCmd(t *testing.T) { defer os.Chdir(pwd) // Run a create - cmd := newCreateCmd(os.Stdout) + cmd := newCreateCmd(ioutil.Discard) if err := cmd.RunE(cmd, []string{cname}); err != nil { t.Errorf("Failed to run create: %s", err) return @@ -117,7 +117,7 @@ func TestCreateStarterCmd(t *testing.T) { defer os.Chdir(pwd) // Run a create - cmd := newCreateCmd(os.Stdout) + cmd := newCreateCmd(ioutil.Discard) cmd.ParseFlags([]string{"--starter", "starterchart"}) if err := cmd.RunE(cmd, []string{cname}); err != nil { t.Errorf("Failed to run create: %s", err) From 026e6b55e5510e0e521760829e041aeac26b22be Mon Sep 17 00:00:00 2001 From: AdamDang Date: Thu, 29 Mar 2018 23:43:04 +0800 Subject: [PATCH 29/71] Update rbac.md In the main text and paragraph title, "Tiller" and "tiller" are both used, It's better use same format. --- docs/rbac.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/rbac.md b/docs/rbac.md index bc138ceee..b48a85ad2 100644 --- a/docs/rbac.md +++ b/docs/rbac.md @@ -4,7 +4,7 @@ In Kubernetes, granting a role to an application-specific service account is a b Bitnami also has a fantastic guide for [configuring RBAC in your cluster](https://docs.bitnami.com/kubernetes/how-to/configure-rbac-in-your-kubernetes-cluster/) that takes you through RBAC basics. -This guide is for users who want to restrict tiller's capabilities to install resources to certain namespaces, or to grant a helm client running access to a tiller instance. +This guide is for users who want to restrict Tiller's capabilities to install resources to certain namespaces, or to grant a helm client running access to a Tiller instance. ## Tiller and Role-based Access Control @@ -51,7 +51,7 @@ clusterrolebinding "tiller" created $ helm init --service-account tiller ``` -### Example: Deploy tiller in a namespace, restricted to deploying resources only in that namespace +### Example: Deploy Tiller in a namespace, restricted to deploying resources only in that namespace In the example above, we gave Tiller admin access to the entire cluster. You are not at all required to give Tiller cluster-admin access for it to work. Instead of specifying a ClusterRole or a ClusterRoleBinding, you can specify a Role and RoleBinding to limit Tiller's scope to a particular namespace. @@ -62,7 +62,7 @@ $ kubectl create serviceaccount tiller --namespace tiller-world serviceaccount "tiller" created ``` -Define a Role that allows tiller to manage all resources in `tiller-world` like in `role-tiller.yaml`: +Define a Role that allows Tiller to manage all resources in `tiller-world` like in `role-tiller.yaml`: ```yaml kind: Role @@ -104,7 +104,7 @@ $ kubectl create -f rolebinding-tiller.yaml rolebinding "tiller-binding" created ``` -Afterwards you can run `helm init` to install tiller in the `tiller-world` namespace. +Afterwards you can run `helm init` to install Tiller in the `tiller-world` namespace. ```console $ helm init --service-account tiller --tiller-namespace tiller-world @@ -125,11 +125,11 @@ NAME READY STATUS RESTARTS AGE wayfaring-yak-alpine 0/1 ContainerCreating 0 0s ``` -### Example: Deploy tiller in a namespace, restricted to deploying resources in another namespace +### Example: Deploy Tiller in a namespace, restricted to deploying resources in another namespace In the example above, we gave Tiller admin access to the namespace it was deployed inside. Now, let's limit Tiller's scope to deploy resources in a different namespace! -For example, let's install tiller in the namespace `myorg-system` and allow tiller to deploy resources in the namespace `myorg-users`. +For example, let's install Tiller in the namespace `myorg-system` and allow Tiller to deploy resources in the namespace `myorg-users`. ```console $ kubectl create namespace myorg-system @@ -138,7 +138,7 @@ $ kubectl create serviceaccount tiller --namespace myorg-system serviceaccount "tiller" created ``` -Define a Role that allows tiller to manage all resources in `myorg-users` like in `role-tiller.yaml`: +Define a Role that allows Tiller to manage all resources in `myorg-users` like in `role-tiller.yaml`: ```yaml kind: Role @@ -180,7 +180,7 @@ $ kubectl create -f rolebinding-tiller.yaml rolebinding "tiller-binding" created ``` -We'll also need to grant tiller access to read configmaps in myorg-system so it can store release information. In `role-tiller-myorg-system.yaml`: +We'll also need to grant Tiller access to read configmaps in myorg-system so it can store release information. In `role-tiller-myorg-system.yaml`: ```yaml kind: Role @@ -224,11 +224,11 @@ rolebinding "tiller-binding" created ## Helm and Role-based Access Control -When running a helm client in a pod, in order for the helm client to talk to a tiller instance, it will need certain privileges to be granted. Specifically, the helm client will need to be able to create pods, forward ports and be able to list pods in the namespace where tiller is running (so it can find tiller). +When running a helm client in a pod, in order for the helm client to talk to a Tiller instance, it will need certain privileges to be granted. Specifically, the helm client will need to be able to create pods, forward ports and be able to list pods in the namespace where Tiller is running (so it can find Tiller). ### Example: Deploy Helm in a namespace, talking to Tiller in another namespace -In this example, we will assume tiller is running in a namespace called `tiller-world` and that the helm client is running in a namespace called `helm-world`. By default, tiller is running in the `kube-system` namespace. +In this example, we will assume Tiller is running in a namespace called `tiller-world` and that the helm client is running in a namespace called `helm-world`. By default, Tiller is running in the `kube-system` namespace. In `helm-user.yaml`: From 09e0ab18090fed1c0b2c91fa401154fcd8e93a0f Mon Sep 17 00:00:00 2001 From: ReSearchITEng Date: Fri, 30 Mar 2018 09:59:06 +0300 Subject: [PATCH 30/71] closes #3795 closes #3795 --- scripts/get | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/get b/scripts/get index 3c4fb9f7d..943ca4158 100755 --- a/scripts/get +++ b/scripts/get @@ -184,7 +184,9 @@ help () { # cleanup temporary files to avoid https://github.com/kubernetes/helm/issues/2977 cleanup() { - rm -rf "$HELM_TMP_ROOT" + if [[ -d "${HELM_TMP_ROOT:-}" ]]; then + rm -rf "$HELM_TMP_ROOT" + fi } # Execution From ea7c3fefc8cd79ac497965a7fca2aef390f82287 Mon Sep 17 00:00:00 2001 From: Arash Deshmeh Date: Sun, 1 Apr 2018 12:39:43 -0400 Subject: [PATCH 31/71] fix(helm) refactor release_testing unit tests to utilize runReleaseCases Signed-off-by: Arash Deshmeh --- cmd/helm/helm_test.go | 8 ++++-- cmd/helm/release_testing_test.go | 47 ++++++++------------------------ 2 files changed, 18 insertions(+), 37 deletions(-) diff --git a/cmd/helm/helm_test.go b/cmd/helm/helm_test.go index c95caa75f..79b8c16f2 100644 --- a/cmd/helm/helm_test.go +++ b/cmd/helm/helm_test.go @@ -43,7 +43,10 @@ func runReleaseCases(t *testing.T, tests []releaseCase, rcmd releaseCmd) { var buf bytes.Buffer for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - c := &helm.FakeClient{Rels: tt.rels} + c := &helm.FakeClient{ + Rels: tt.rels, + Responses: tt.responses, + } cmd := rcmd(c, &buf) cmd.ParseFlags(tt.flags) err := cmd.RunE(cmd, tt.args) @@ -69,7 +72,8 @@ type releaseCase struct { err bool resp *release.Release // Rels are the available releases at the start of the test. - rels []*release.Release + rels []*release.Release + responses map[string]release.TestRun_Status } // tempHelmHome sets up a Helm Home in a temp dir. diff --git a/cmd/helm/release_testing_test.go b/cmd/helm/release_testing_test.go index 02ab1883c..b946746d0 100644 --- a/cmd/helm/release_testing_test.go +++ b/cmd/helm/release_testing_test.go @@ -17,55 +17,50 @@ limitations under the License. package main import ( - "bytes" + "io" "testing" + "github.com/spf13/cobra" "k8s.io/helm/pkg/helm" "k8s.io/helm/pkg/proto/hapi/release" ) func TestReleaseTesting(t *testing.T) { - tests := []struct { - name string - args []string - flags []string - responses map[string]release.TestRun_Status - fail bool - }{ + tests := []releaseCase{ { name: "basic test", args: []string{"example-release"}, flags: []string{}, responses: map[string]release.TestRun_Status{"PASSED: green lights everywhere": release.TestRun_SUCCESS}, - fail: false, + err: false, }, { name: "test failure", args: []string{"example-fail"}, flags: []string{}, responses: map[string]release.TestRun_Status{"FAILURE: red lights everywhere": release.TestRun_FAILURE}, - fail: true, + err: true, }, { name: "test unknown", args: []string{"example-unknown"}, flags: []string{}, responses: map[string]release.TestRun_Status{"UNKNOWN: yellow lights everywhere": release.TestRun_UNKNOWN}, - fail: false, + err: false, }, { name: "test error", args: []string{"example-error"}, flags: []string{}, responses: map[string]release.TestRun_Status{"ERROR: yellow lights everywhere": release.TestRun_FAILURE}, - fail: true, + err: true, }, { name: "test running", args: []string{"example-running"}, flags: []string{}, responses: map[string]release.TestRun_Status{"RUNNING: things are happpeningggg": release.TestRun_RUNNING}, - fail: false, + err: false, }, { name: "multiple tests example", @@ -78,29 +73,11 @@ func TestReleaseTesting(t *testing.T) { "FAILURE: good thing u checked :)": release.TestRun_FAILURE, "RUNNING: things are happpeningggg yet again": release.TestRun_RUNNING, "PASSED: feel free to party again": release.TestRun_SUCCESS}, - fail: true, + err: true, }, } - for _, tt := range tests { - c := &helm.FakeClient{Responses: tt.responses} - - buf := bytes.NewBuffer(nil) - cmd := newReleaseTestCmd(c, buf) - cmd.ParseFlags(tt.flags) - - err := cmd.RunE(cmd, tt.args) - if err == nil && tt.fail { - t.Errorf("%q did not fail but should have failed", tt.name) - } - - if err != nil { - if tt.fail { - continue - } else { - t.Errorf("%q reported error: %s", tt.name, err) - } - } - - } + runReleaseCases(t, tests, func(c *helm.FakeClient, out io.Writer) *cobra.Command { + return newReleaseTestCmd(c, out) + }) } From ad99c1b749df43c6dcdbc1df278a88e8093290fb Mon Sep 17 00:00:00 2001 From: fibonacci1729 Date: Fri, 30 Mar 2018 09:28:42 -0600 Subject: [PATCH 32/71] release list paritioning --- pkg/tiller/release_list.go | 51 ++++++++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/pkg/tiller/release_list.go b/pkg/tiller/release_list.go index ec4dbfb39..72c21d97c 100644 --- a/pkg/tiller/release_list.go +++ b/pkg/tiller/release_list.go @@ -18,11 +18,11 @@ package tiller import ( "fmt" - "regexp" - + "github.com/golang/protobuf/proto" "k8s.io/helm/pkg/proto/hapi/release" "k8s.io/helm/pkg/proto/hapi/services" relutil "k8s.io/helm/pkg/releaseutil" + "regexp" ) // ListReleases lists the releases found by the server. @@ -107,21 +107,52 @@ func (s *ReleaseServer) ListReleases(req *services.ListReleasesRequest, stream s rels = rels[0:req.Limit] l = int64(len(rels)) } - - for i := 0; i < min(len(rels), int(req.Limit)); i++ { - res := &services.ListReleasesResponse{ - Next: next, - Count: l, - Total: total, - Releases: []*release.Release{rels[i]}, - } + res := &services.ListReleasesResponse{ + Next: next, + Count: l, + Total: total, + } + chunks := s.partition(rels[:min(len(rels), int(req.Limit))], maxMsgSize-proto.Size(res)) + for res.Releases = range chunks { if err := stream.Send(res); err != nil { + for range chunks { // drain + } return err } } return nil } +// partition packs releases into slices upto the capacity cap in bytes. +func (s *ReleaseServer) partition(rels []*release.Release, cap int) <-chan []*release.Release { + chunks := make(chan []*release.Release, 1) + go func() { + var ( + fill = 0 // fill is space available to fill + size int // size is size of a release + ) + var chunk []*release.Release + for _, rls := range rels { + if size = proto.Size(rls); size+fill > cap { + // Over-cap, push chunk onto channel to send over gRPC stream + s.Log("partitioned at %d with %d releases (cap=%d)", fill, len(chunk), cap) + chunks <- chunk + // reset paritioning state + chunk = chunk[:0] + fill = 0 + } + chunk = append(chunk, rls) + fill += size + } + if len(chunk) > 0 { + // send remaining if any + chunks <- chunk + } + close(chunks) + }() + return chunks +} + func filterByNamespace(namespace string, rels []*release.Release) ([]*release.Release, error) { matches := []*release.Release{} for _, r := range rels { From 458ba8ce37d5068ec03384257d4cbeed79e4906d Mon Sep 17 00:00:00 2001 From: Gijs Kunze Date: Tue, 3 Apr 2018 11:12:12 +0200 Subject: [PATCH 33/71] Removes unnecessary if block --- cmd/helm/install.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/cmd/helm/install.go b/cmd/helm/install.go index 55e34f405..d52dbc667 100644 --- a/cmd/helm/install.go +++ b/cmd/helm/install.go @@ -313,11 +313,6 @@ func mergeValues(dest map[string]interface{}, src map[string]interface{}) map[st dest[k] = v continue } - // If the key doesn't exist already, then just set the key to that value - if _, exists := dest[k]; !exists { - dest[k] = nextMap - continue - } // Edge case: If the key exists in the destination, but isn't a map destMap, isMap := dest[k].(map[string]interface{}) // If the source map has a map for this key, prefer it From b7fe0d0bd7d059ad45b71fe84be5275f66e1bf4a Mon Sep 17 00:00:00 2001 From: Timofey Kirillov Date: Wed, 21 Mar 2018 23:13:29 +0300 Subject: [PATCH 34/71] Add hook-delete-policy ReleaseServer unit tests Cover hook-succeeded, hook-failed and before-hook-creation hook delete policies. --- pkg/tiller/release_server_test.go | 464 ++++++++++++++++++++++++++++++ 1 file changed, 464 insertions(+) diff --git a/pkg/tiller/release_server_test.go b/pkg/tiller/release_server_test.go index d71e34bee..6c4d42e04 100644 --- a/pkg/tiller/release_server_test.go +++ b/pkg/tiller/release_server_test.go @@ -18,18 +18,25 @@ package tiller import ( "errors" + "fmt" "io" "io/ioutil" "os" "regexp" "testing" + "time" + "github.com/ghodss/yaml" "github.com/golang/protobuf/ptypes/timestamp" "golang.org/x/net/context" "google.golang.org/grpc/metadata" + "k8s.io/kubernetes/pkg/apis/core" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake" + "k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/helm/pkg/helm" + "k8s.io/helm/pkg/hooks" + "k8s.io/helm/pkg/kube" "k8s.io/helm/pkg/proto/hapi/chart" "k8s.io/helm/pkg/proto/hapi/release" "k8s.io/helm/pkg/proto/hapi/services" @@ -345,3 +352,460 @@ func (rs mockRunReleaseTestServer) SetTrailer(m metadata.MD) {} func (rs mockRunReleaseTestServer) SendMsg(v interface{}) error { return nil } func (rs mockRunReleaseTestServer) RecvMsg(v interface{}) error { return nil } func (rs mockRunReleaseTestServer) Context() context.Context { return helm.NewContext() } + +type mockHooksManifest struct { + Metadata struct { + Name string + Annotations map[string]string + } +} +type mockHooksKubeClient struct { + Resources map[string]*mockHooksManifest +} + +var errResourceExists = errors.New("resource already exists") + +func (kc *mockHooksKubeClient) makeManifest(r io.Reader) (*mockHooksManifest, error) { + b, err := ioutil.ReadAll(r) + if err != nil { + return nil, err + } + + manifest := &mockHooksManifest{} + err = yaml.Unmarshal(b, manifest) + if err != nil { + return nil, err + } + + return manifest, nil +} +func (kc *mockHooksKubeClient) Create(ns string, r io.Reader, timeout int64, shouldWait bool) error { + manifest, err := kc.makeManifest(r) + if err != nil { + return err + } + + if _, hasKey := kc.Resources[manifest.Metadata.Name]; hasKey { + return errResourceExists + } + + kc.Resources[manifest.Metadata.Name] = manifest + + return nil +} +func (kc *mockHooksKubeClient) Get(ns string, r io.Reader) (string, error) { + return "", nil +} +func (kc *mockHooksKubeClient) Delete(ns string, r io.Reader) error { + manifest, err := kc.makeManifest(r) + if err != nil { + return err + } + + delete(kc.Resources, manifest.Metadata.Name) + + return nil +} +func (kc *mockHooksKubeClient) WatchUntilReady(ns string, r io.Reader, timeout int64, shouldWait bool) error { + paramManifest, err := kc.makeManifest(r) + if err != nil { + return err + } + + manifest, hasManifest := kc.Resources[paramManifest.Metadata.Name] + if !hasManifest { + return fmt.Errorf("mockHooksKubeClient.WatchUntilReady: no such resource %s found", paramManifest.Metadata.Name) + } + + if manifest.Metadata.Annotations["mockHooksKubeClient/Emulate"] == "hook-failed" { + return fmt.Errorf("mockHooksKubeClient.WatchUntilReady: hook-failed") + } + + return nil +} +func (kc *mockHooksKubeClient) Update(ns string, currentReader, modifiedReader io.Reader, force bool, recreate bool, timeout int64, shouldWait bool) error { + return nil +} +func (kc *mockHooksKubeClient) Build(ns string, reader io.Reader) (kube.Result, error) { + return []*resource.Info{}, nil +} +func (kc *mockHooksKubeClient) BuildUnstructured(ns string, reader io.Reader) (kube.Result, error) { + return []*resource.Info{}, nil +} +func (kc *mockHooksKubeClient) WaitAndGetCompletedPodPhase(namespace string, reader io.Reader, timeout time.Duration) (core.PodPhase, error) { + return core.PodUnknown, nil +} + +func deletePolicyStub(kubeClient *mockHooksKubeClient) *ReleaseServer { + e := environment.New() + e.Releases = storage.Init(driver.NewMemory()) + e.KubeClient = kubeClient + + clientset := fake.NewSimpleClientset() + return &ReleaseServer{ + ReleaseModule: &LocalReleaseModule{ + clientset: clientset, + }, + env: e, + clientset: clientset, + Log: func(_ string, _ ...interface{}) {}, + } +} + +func deletePolicyHookStub(hookName string, extraAnnotations map[string]string, DeletePolicies []release.Hook_DeletePolicy) *release.Hook { + extraAnnotationsStr := "" + for k, v := range extraAnnotations { + extraAnnotationsStr += fmt.Sprintf(" \"%s\": \"%s\"\n", k, v) + } + + return &release.Hook{ + Name: hookName, + Kind: "Job", + Path: hookName, + Manifest: fmt.Sprintf(`kind: Job +metadata: + name: %s + annotations: + "helm.sh/hook": pre-install,pre-upgrade +%sdata: +name: value`, hookName, extraAnnotationsStr), + Events: []release.Hook_Event{ + release.Hook_PRE_INSTALL, + release.Hook_PRE_UPGRADE, + }, + DeletePolicies: DeletePolicies, + } +} + +func execHookShouldSucceed(rs *ReleaseServer, hook *release.Hook, releaseName string, namespace string, hookType string) error { + err := rs.execHook([]*release.Hook{hook}, releaseName, namespace, hookType, 600) + if err != nil { + return fmt.Errorf("expected hook %s to be successful: %s", hook.Name, err) + } + return nil +} + +func execHookShouldFail(rs *ReleaseServer, hook *release.Hook, releaseName string, namespace string, hookType string) error { + err := rs.execHook([]*release.Hook{hook}, releaseName, namespace, hookType, 600) + if err == nil { + return fmt.Errorf("expected hook %s to be failed", hook.Name) + } + return nil +} + +func execHookShouldFailWithError(rs *ReleaseServer, hook *release.Hook, releaseName string, namespace string, hookType string, expectedError error) error { + err := rs.execHook([]*release.Hook{hook}, releaseName, namespace, hookType, 600) + if err != expectedError { + return fmt.Errorf("expected hook %s to fail with error %v, got %v", hook.Name, expectedError, err) + } + return nil +} + +type deletePolicyContext struct { + ReleaseServer *ReleaseServer + ReleaseName string + Namespace string + HookName string + KubeClient *mockHooksKubeClient +} + +func newDeletePolicyContext() *deletePolicyContext { + kubeClient := &mockHooksKubeClient{ + Resources: make(map[string]*mockHooksManifest), + } + + return &deletePolicyContext{ + KubeClient: kubeClient, + ReleaseServer: deletePolicyStub(kubeClient), + ReleaseName: "flying-carp", + Namespace: "river", + HookName: "migration-job", + } +} + +func TestSuccessfulHookWithoutDeletePolicy(t *testing.T) { + ctx := newDeletePolicyContext() + hook := deletePolicyHookStub(ctx.HookName, nil, nil) + + err := execHookShouldSucceed(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall) + if err != nil { + t.Error(err) + } + if _, hasResource := ctx.KubeClient.Resources[hook.Name]; !hasResource { + t.Errorf("expected resource %s to be created by kube client", hook.Name) + } +} + +func TestFailedHookWithoutDeletePolicy(t *testing.T) { + ctx := newDeletePolicyContext() + hook := deletePolicyHookStub(ctx.HookName, + map[string]string{"mockHooksKubeClient/Emulate": "hook-failed"}, + nil, + ) + + err := execHookShouldFail(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall) + if err != nil { + t.Error(err) + } + if _, hasResource := ctx.KubeClient.Resources[hook.Name]; !hasResource { + t.Errorf("expected resource %s to be created by kube client", hook.Name) + } +} + +func TestSuccessfulHookWithSucceededDeletePolicy(t *testing.T) { + ctx := newDeletePolicyContext() + hook := deletePolicyHookStub(ctx.HookName, + map[string]string{"helm.sh/hook-delete-policy": "hook-succeeded"}, + []release.Hook_DeletePolicy{release.Hook_SUCCEEDED}, + ) + + err := execHookShouldSucceed(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall) + if err != nil { + t.Error(err) + } + if _, hasResource := ctx.KubeClient.Resources[hook.Name]; hasResource { + t.Errorf("expected resource %s to be unexisting after hook succeeded", hook.Name) + } +} + +func TestSuccessfulHookWithFailedDeletePolicy(t *testing.T) { + ctx := newDeletePolicyContext() + hook := deletePolicyHookStub(ctx.HookName, + map[string]string{"helm.sh/hook-delete-policy": "hook-failed"}, + []release.Hook_DeletePolicy{release.Hook_FAILED}, + ) + + err := execHookShouldSucceed(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall) + if err != nil { + t.Error(err) + } + if _, hasResource := ctx.KubeClient.Resources[hook.Name]; !hasResource { + t.Errorf("expected resource %s to be existing after hook succeeded", hook.Name) + } +} + +func TestFailedHookWithSucceededDeletePolicy(t *testing.T) { + ctx := newDeletePolicyContext() + + hook := deletePolicyHookStub(ctx.HookName, + map[string]string{ + "mockHooksKubeClient/Emulate": "hook-failed", + "helm.sh/hook-delete-policy": "hook-succeeded", + }, + []release.Hook_DeletePolicy{release.Hook_SUCCEEDED}, + ) + + err := execHookShouldFail(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall) + if err != nil { + t.Error(err) + } + if _, hasResource := ctx.KubeClient.Resources[hook.Name]; !hasResource { + t.Errorf("expected resource %s to be existing after hook failed", hook.Name) + } +} + +func TestFailedHookWithFailedDeletePolicy(t *testing.T) { + ctx := newDeletePolicyContext() + + hook := deletePolicyHookStub(ctx.HookName, + map[string]string{ + "mockHooksKubeClient/Emulate": "hook-failed", + "helm.sh/hook-delete-policy": "hook-failed", + }, + []release.Hook_DeletePolicy{release.Hook_FAILED}, + ) + + err := execHookShouldFail(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall) + if err != nil { + t.Error(err) + } + if _, hasResource := ctx.KubeClient.Resources[hook.Name]; hasResource { + t.Errorf("expected resource %s to be unexisting after hook failed", hook.Name) + } +} + +func TestSuccessfulHookWithSuccededOrFailedDeletePolicy(t *testing.T) { + ctx := newDeletePolicyContext() + + hook := deletePolicyHookStub(ctx.HookName, + map[string]string{ + "helm.sh/hook-delete-policy": "hook-succeeded,hook-failed", + }, + []release.Hook_DeletePolicy{release.Hook_SUCCEEDED, release.Hook_FAILED}, + ) + + err := execHookShouldSucceed(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall) + if err != nil { + t.Error(err) + } + if _, hasResource := ctx.KubeClient.Resources[hook.Name]; hasResource { + t.Errorf("expected resource %s to be unexisting after hook succeeded", hook.Name) + } +} + +func TestFailedHookWithSuccededOrFailedDeletePolicy(t *testing.T) { + ctx := newDeletePolicyContext() + + hook := deletePolicyHookStub(ctx.HookName, + map[string]string{ + "mockHooksKubeClient/Emulate": "hook-failed", + "helm.sh/hook-delete-policy": "hook-succeeded,hook-failed", + }, + []release.Hook_DeletePolicy{release.Hook_SUCCEEDED, release.Hook_FAILED}, + ) + + err := execHookShouldFail(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall) + if err != nil { + t.Error(err) + } + if _, hasResource := ctx.KubeClient.Resources[hook.Name]; hasResource { + t.Errorf("expected resource %s to be unexisting after hook failed", hook.Name) + } +} + +func TestHookAlreadyExists(t *testing.T) { + ctx := newDeletePolicyContext() + + hook := deletePolicyHookStub(ctx.HookName, nil, nil) + + err := execHookShouldSucceed(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall) + if err != nil { + t.Error(err) + } + + if _, hasResource := ctx.KubeClient.Resources[hook.Name]; !hasResource { + t.Errorf("expected resource %s to be existing after hook succeeded", hook.Name) + } + + err = execHookShouldFailWithError(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreUpgrade, errResourceExists) + if err != nil { + t.Error(err) + } + + if _, hasResource := ctx.KubeClient.Resources[hook.Name]; !hasResource { + t.Errorf("expected resource %s to be existing after already exists error", hook.Name) + } +} + +func TestHookDeletingWithBeforeHookCreationDeletePolicy(t *testing.T) { + ctx := newDeletePolicyContext() + + hook := deletePolicyHookStub(ctx.HookName, + map[string]string{"helm.sh/hook-delete-policy": "before-hook-creation"}, + []release.Hook_DeletePolicy{release.Hook_BEFORE_HOOK_CREATION}, + ) + + err := execHookShouldSucceed(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall) + if err != nil { + t.Error(err) + } + + if _, hasResource := ctx.KubeClient.Resources[hook.Name]; !hasResource { + t.Errorf("expected resource %s to be existing after hook succeeded", hook.Name) + } + + err = execHookShouldSucceed(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreUpgrade) + if err != nil { + t.Error(err) + } + + if _, hasResource := ctx.KubeClient.Resources[hook.Name]; !hasResource { + t.Errorf("expected resource %s to be existing after hook succeeded", hook.Name) + } +} + +func TestSuccessfulHookWithMixedDeletePolicies(t *testing.T) { + ctx := newDeletePolicyContext() + + hook := deletePolicyHookStub(ctx.HookName, + map[string]string{ + "helm.sh/hook-delete-policy": "hook-succeeded,before-hook-creation", + }, + []release.Hook_DeletePolicy{release.Hook_SUCCEEDED, release.Hook_BEFORE_HOOK_CREATION}, + ) + + err := execHookShouldSucceed(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall) + if err != nil { + t.Error(err) + } + + if _, hasResource := ctx.KubeClient.Resources[hook.Name]; hasResource { + t.Errorf("expected resource %s to be unexisting after hook succeeded", hook.Name) + } + + err = execHookShouldSucceed(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreUpgrade) + if err != nil { + t.Error(err) + } + + if _, hasResource := ctx.KubeClient.Resources[hook.Name]; hasResource { + t.Errorf("expected resource %s to be unexisting after hook succeeded", hook.Name) + } +} + +func TestFailedHookWithMixedDeletePolicies(t *testing.T) { + ctx := newDeletePolicyContext() + + hook := deletePolicyHookStub(ctx.HookName, + map[string]string{ + "mockHooksKubeClient/Emulate": "hook-failed", + "helm.sh/hook-delete-policy": "hook-succeeded,before-hook-creation", + }, + []release.Hook_DeletePolicy{release.Hook_SUCCEEDED, release.Hook_BEFORE_HOOK_CREATION}, + ) + + err := execHookShouldFail(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall) + if err != nil { + t.Error(err) + } + + if _, hasResource := ctx.KubeClient.Resources[hook.Name]; !hasResource { + t.Errorf("expected resource %s to be existing after hook failed", hook.Name) + } + + err = execHookShouldFail(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreUpgrade) + if err != nil { + t.Error(err) + } + + if _, hasResource := ctx.KubeClient.Resources[hook.Name]; !hasResource { + t.Errorf("expected resource %s to be existing after hook failed", hook.Name) + } +} + +func TestFailedThenSuccessfulHookWithMixedDeletePolicies(t *testing.T) { + ctx := newDeletePolicyContext() + + hook := deletePolicyHookStub(ctx.HookName, + map[string]string{ + "mockHooksKubeClient/Emulate": "hook-failed", + "helm.sh/hook-delete-policy": "hook-succeeded,before-hook-creation", + }, + []release.Hook_DeletePolicy{release.Hook_SUCCEEDED, release.Hook_BEFORE_HOOK_CREATION}, + ) + + err := execHookShouldFail(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall) + if err != nil { + t.Error(err) + } + + if _, hasResource := ctx.KubeClient.Resources[hook.Name]; !hasResource { + t.Errorf("expected resource %s to be existing after hook failed", hook.Name) + } + + hook = deletePolicyHookStub(ctx.HookName, + map[string]string{ + "helm.sh/hook-delete-policy": "hook-succeeded,before-hook-creation", + }, + []release.Hook_DeletePolicy{release.Hook_SUCCEEDED, release.Hook_BEFORE_HOOK_CREATION}, + ) + + err = execHookShouldSucceed(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreUpgrade) + if err != nil { + t.Error(err) + } + + if _, hasResource := ctx.KubeClient.Resources[hook.Name]; hasResource { + t.Errorf("expected resource %s to be unexisting after hook succeeded", hook.Name) + } +} From c52707fb26e67609606f6ec35971068e79e32641 Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Tue, 3 Apr 2018 08:13:18 -0700 Subject: [PATCH 35/71] import client auth plugins --- cmd/helm/helm.go | 2 ++ glide.lock | 58 +++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index 497565209..f19cb3c50 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -30,6 +30,8 @@ import ( "k8s.io/client-go/rest" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" + // Import to initialize client auth plugins. + _ "k8s.io/client-go/plugin/pkg/client/auth" "k8s.io/helm/pkg/helm" helm_env "k8s.io/helm/pkg/helm/environment" "k8s.io/helm/pkg/helm/portforwarder" diff --git a/glide.lock b/glide.lock index 068f86372..6c54c927c 100644 --- a/glide.lock +++ b/glide.lock @@ -1,6 +1,11 @@ hash: 6837936360d447b64aa7a09d3c89c18ac5540b009a57fc4d3227af299bf40268 -updated: 2018-03-27T05:13:24.967040785Z +updated: 2018-04-03T08:17:14.801847688-07:00 imports: +- name: cloud.google.com/go + version: 3b1ae45394a234c385be014e9a488f2bb6eef821 + subpackages: + - compute/metadata + - internal - name: github.com/aokoli/goutils version: 9c37978a95bd5c709a15883b6242714ea6709e64 - name: github.com/asaskevich/govalidator @@ -9,6 +14,13 @@ imports: version: 19f72df4d05d31cbe1c56bfc8045c96babff6c7e subpackages: - winterm +- name: github.com/Azure/go-autorest + version: d4e6b95c12a08b4de2d48b45d5b4d594e5d32fab + subpackages: + - autorest + - autorest/adal + - autorest/azure + - autorest/date - name: github.com/beorn7/perks version: 3ac7bf7a47d159a033b107610db8a1b6575507a4 subpackages: @@ -23,6 +35,8 @@ imports: version: 782f4967f2dc4564575ca782fe2d04090b5faca8 subpackages: - spew +- name: github.com/dgrijalva/jwt-go + version: 01aeca54ebda6e0fbfafd0a524d234159c05ec20 - name: github.com/docker/distribution version: edc3ab29cdff8694dd6feb85cfeb4b5f1b38ed9c subpackages: @@ -121,12 +135,23 @@ imports: version: 7d79101e329e5a3adf994758c578dab82b90c017 - name: github.com/google/gofuzz version: 44d81051d367757e1c7c6a5a86423ece9afcf63c +- name: github.com/google/uuid + version: 064e2069ce9c359c118179501254f67d7d37ba24 - name: github.com/googleapis/gnostic version: 0c5108395e2debce0d731cf0287ddf7242066aba subpackages: - OpenAPIv2 - compiler - extensions +- name: github.com/gophercloud/gophercloud + version: 6da026c32e2d622cc242d32984259c77237aefe1 + subpackages: + - openstack + - openstack/identity/v2/tenants + - openstack/identity/v2/tokens + - openstack/identity/v3/tokens + - openstack/utils + - pagination - name: github.com/gosuri/uitable version: 36ee7e946282a3fb1cfecd476ddc9b35d8847e42 subpackages: @@ -163,7 +188,7 @@ imports: - name: github.com/Masterminds/semver version: 517734cc7d6470c0d07130e40fd40bdeb9bcd3fd - name: github.com/Masterminds/sprig - version: b217b9c388de2cacde4354c536e520c52c055563 + version: 6b2a58267f6a8b1dc8e2eb5519b984008fa85e8c - name: github.com/Masterminds/vcs version: 3084677c2c188840777bff30054f2b553729d329 - name: github.com/mattn/go-runewidth @@ -210,8 +235,6 @@ imports: version: 5bd2802263f21d8788851d5305584c82a5c75d7e - name: github.com/russross/blackfriday version: 300106c228d52c8941d4b3de6054a6062a86dda3 -- name: github.com/satori/go.uuid - version: f58768cc1a7a7e77a3bd49e98cdd21419399b6a3 - name: github.com/shurcooL/sanitized_anchor_name version: 10ef21a441db47d8b13ebcc5fd2310f636973c77 - name: github.com/sirupsen/logrus @@ -244,12 +267,20 @@ imports: version: 1c05540f6879653db88113bc4a2b70aec4bd491f subpackages: - context + - context/ctxhttp - http2 - http2/hpack - idna - internal/timeseries - lex/httplex - trace +- name: golang.org/x/oauth2 + version: a6bd8cefa1811bd24b86f8902872e4e8225f74c4 + subpackages: + - google + - internal + - jws + - jwt - name: golang.org/x/sys version: 43eea11bc92608addb41b8a406b0407495c106f6 subpackages: @@ -278,6 +309,18 @@ imports: version: f51c12702a4d776e4c1fa9b0fabab841babae631 subpackages: - rate +- name: google.golang.org/appengine + version: 12d5545dc1cfa6047a286d5e853841b6471f4c19 + subpackages: + - internal + - internal/app_identity + - internal/base + - internal/datastore + - internal/log + - internal/modules + - internal/remote_api + - internal/urlfetch + - urlfetch - name: google.golang.org/genproto version: 09f6ed296fc66555a25fe4ce95173148778dfa85 subpackages: @@ -550,7 +593,12 @@ imports: - pkg/apis/clientauthentication - pkg/apis/clientauthentication/v1alpha1 - pkg/version + - plugin/pkg/client/auth + - plugin/pkg/client/auth/azure - plugin/pkg/client/auth/exec + - plugin/pkg/client/auth/gcp + - plugin/pkg/client/auth/oidc + - plugin/pkg/client/auth/openstack - rest - rest/fake - rest/watch @@ -593,7 +641,7 @@ imports: - pkg/util/proto - pkg/util/proto/validation - name: k8s.io/kubernetes - version: 923d8441db527e908cd7d266c4a9d18daa596d4c + version: a22f9fd34871d9dc9e5db2c02c713821d18ab2cd subpackages: - pkg/api/events - pkg/api/legacyscheme From 1d3ae54185e369477f42fb15d3a97f80c014c09c Mon Sep 17 00:00:00 2001 From: Zack Williams Date: Tue, 3 Apr 2018 13:10:56 -0700 Subject: [PATCH 36/71] Fix #3822 --- pkg/chartutil/create.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/chartutil/create.go b/pkg/chartutil/create.go index 319a75e2f..07bd11449 100644 --- a/pkg/chartutil/create.go +++ b/pkg/chartutil/create.go @@ -59,6 +59,9 @@ image: tag: stable pullPolicy: IfNotPresent +nameOverride: "" +fullnameOverride: "" + service: type: ClusterIP port: 80 From 58ac6023653632afe1bb13d01ed5516fff3737f5 Mon Sep 17 00:00:00 2001 From: AdamDang Date: Wed, 4 Apr 2018 07:07:55 +0800 Subject: [PATCH 37/71] Typo fix helm->Helm --- docs/rbac.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/rbac.md b/docs/rbac.md index b48a85ad2..2a3dfe7a0 100644 --- a/docs/rbac.md +++ b/docs/rbac.md @@ -4,11 +4,11 @@ In Kubernetes, granting a role to an application-specific service account is a b Bitnami also has a fantastic guide for [configuring RBAC in your cluster](https://docs.bitnami.com/kubernetes/how-to/configure-rbac-in-your-kubernetes-cluster/) that takes you through RBAC basics. -This guide is for users who want to restrict Tiller's capabilities to install resources to certain namespaces, or to grant a helm client running access to a Tiller instance. +This guide is for users who want to restrict Tiller's capabilities to install resources to certain namespaces, or to grant a Helm client running access to a Tiller instance. ## Tiller and Role-based Access Control -You can add a service account to Tiller using the `--service-account ` flag while you're configuring helm. As a prerequisite, you'll have to create a role binding which specifies a [role](https://kubernetes.io/docs/admin/authorization/rbac/#role-and-clusterrole) and a [service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) name that have been set up in advance. +You can add a service account to Tiller using the `--service-account ` flag while you're configuring Helm. As a prerequisite, you'll have to create a role binding which specifies a [role](https://kubernetes.io/docs/admin/authorization/rbac/#role-and-clusterrole) and a [service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) name that have been set up in advance. Once you have satisfied the pre-requisite and have a service account with the correct permissions, you'll run a command like this: `helm init --service-account ` @@ -110,7 +110,7 @@ Afterwards you can run `helm init` to install Tiller in the `tiller-world` names $ helm init --service-account tiller --tiller-namespace tiller-world $HELM_HOME has been configured at /Users/awesome-user/.helm. -Tiller (the helm server side component) has been installed into your Kubernetes Cluster. +Tiller (the Helm server side component) has been installed into your Kubernetes Cluster. Happy Helming! $ helm install nginx --tiller-namespace tiller-world --namespace tiller-world @@ -224,11 +224,11 @@ rolebinding "tiller-binding" created ## Helm and Role-based Access Control -When running a helm client in a pod, in order for the helm client to talk to a Tiller instance, it will need certain privileges to be granted. Specifically, the helm client will need to be able to create pods, forward ports and be able to list pods in the namespace where Tiller is running (so it can find Tiller). +When running a Helm client in a pod, in order for the Helm client to talk to a Tiller instance, it will need certain privileges to be granted. Specifically, the Helm client will need to be able to create pods, forward ports and be able to list pods in the namespace where Tiller is running (so it can find Tiller). ### Example: Deploy Helm in a namespace, talking to Tiller in another namespace -In this example, we will assume Tiller is running in a namespace called `tiller-world` and that the helm client is running in a namespace called `helm-world`. By default, Tiller is running in the `kube-system` namespace. +In this example, we will assume Tiller is running in a namespace called `tiller-world` and that the Helm client is running in a namespace called `helm-world`. By default, Tiller is running in the `kube-system` namespace. In `helm-user.yaml`: From 9b20d91e427da1d8bc5c961a25174ccee2f4ff6c Mon Sep 17 00:00:00 2001 From: Ryan Hartje Date: Tue, 3 Apr 2018 20:17:54 -0400 Subject: [PATCH 38/71] using existing mechanism to flag for failures --- cmd/helm/lint.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/cmd/helm/lint.go b/cmd/helm/lint.go index e83026117..63f11c062 100644 --- a/cmd/helm/lint.go +++ b/cmd/helm/lint.go @@ -97,13 +97,12 @@ func (l *lintCmd) run() error { var total int var failures int - rc := 0 for _, path := range l.paths { if linter, err := lintChart(path, rvals, l.namespace, l.strict); err != nil { fmt.Println("==> Skipping", path) fmt.Println(err) if err == errLintNoChart { - rc = 1 + failures = failures + 1 } } else { fmt.Println("==> Linting", path) @@ -131,10 +130,6 @@ func (l *lintCmd) run() error { fmt.Fprintf(l.out, "%s, no failures\n", msg) - if rc != 0 { - os.Exit(rc) - } - return nil } From bac4f45f94bf2856215f9e5657ee6a67d16d71fd Mon Sep 17 00:00:00 2001 From: Stuart Leeks Date: Wed, 4 Apr 2018 09:17:57 +0100 Subject: [PATCH 39/71] Switch flag to update existing record --- pkg/tiller/release_rollback.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/tiller/release_rollback.go b/pkg/tiller/release_rollback.go index fe9a6a032..fa3d943f4 100644 --- a/pkg/tiller/release_rollback.go +++ b/pkg/tiller/release_rollback.go @@ -135,7 +135,7 @@ func (s *ReleaseServer) performRollback(currentRelease, targetRelease *release.R targetRelease.Info.Status.Code = release.Status_FAILED targetRelease.Info.Description = msg s.recordRelease(currentRelease, true) - s.recordRelease(targetRelease, false) + s.recordRelease(targetRelease, true) return res, err } From 499636d70c85d7615eb27d9c58d69ef55f26fc4a Mon Sep 17 00:00:00 2001 From: Michelle Noorali Date: Wed, 4 Apr 2018 12:04:27 -0400 Subject: [PATCH 40/71] ref(cmd/helm): show grpc error msg from prettyError --- cmd/helm/helm.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index 497565209..86d7696fb 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -214,7 +214,7 @@ func prettyError(err error) error { } // If it's grpc's error, make it more user-friendly. if s, ok := status.FromError(err); ok { - return s.Err() + return fmt.Errorf(s.Message()) } // Else return the original error. return err From 9266731dc45aac2aa6a726e5a887ee1078aaf35f Mon Sep 17 00:00:00 2001 From: Michelle Noorali Date: Wed, 4 Apr 2018 15:44:28 -0400 Subject: [PATCH 41/71] fix(pkg/tiller): reuseValues combines all prev val Resolves #3655 We were seeing that when running helm upgrade with the reuse-values flag enabled that you could end up in the position where overrides a.k.a computed values from previous revisions were not being saved on the updated revision. This left us in a weird position where some computed values would disappear mysteriously in the abyss. That happened because computed values from previous revisions weren't merged with the new computed values every time the reuse-values flag was used. This PR merges computed values from the previous revisions so you don't end up in that kind of conundrum. --- pkg/tiller/release_server.go | 16 +++++ pkg/tiller/release_update_test.go | 106 +++++++++++++++++++++++++++++- 2 files changed, 120 insertions(+), 2 deletions(-) diff --git a/pkg/tiller/release_server.go b/pkg/tiller/release_server.go index a96c64938..c72107026 100644 --- a/pkg/tiller/release_server.go +++ b/pkg/tiller/release_server.go @@ -25,6 +25,7 @@ import ( "strings" "github.com/technosophos/moniker" + "gopkg.in/yaml.v2" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/discovery" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" @@ -135,7 +136,22 @@ func (s *ReleaseServer) reuseValues(req *services.UpdateReleaseRequest, current if err != nil { return err } + + // merge new values with current + req.Values.Raw = current.Config.Raw + "\n" + req.Values.Raw req.Chart.Values = &chart.Config{Raw: nv} + + // yaml unmarshal and marshal to remove duplicate keys + y := map[string]interface{}{} + if err := yaml.Unmarshal([]byte(req.Values.Raw), &y); err != nil { + return err + } + data, err := yaml.Marshal(y) + if err != nil { + return err + } + + req.Values.Raw = string(data) return nil } diff --git a/pkg/tiller/release_update_test.go b/pkg/tiller/release_update_test.go index 642952f19..0189201d1 100644 --- a/pkg/tiller/release_update_test.go +++ b/pkg/tiller/release_update_test.go @@ -17,6 +17,7 @@ limitations under the License. package tiller import ( + "fmt" "strings" "testing" @@ -128,6 +129,107 @@ func TestUpdateRelease_ResetValues(t *testing.T) { } } +// This is a regression test for bug found in issue #3655 +func TestUpdateRelease_ComplexReuseValues(t *testing.T) { + c := helm.NewContext() + rs := rsFixture() + + installReq := &services.InstallReleaseRequest{ + Namespace: "spaced", + Chart: &chart.Chart{ + Metadata: &chart.Metadata{Name: "hello"}, + Templates: []*chart.Template{ + {Name: "templates/hello", Data: []byte("hello: world")}, + {Name: "templates/hooks", Data: []byte(manifestWithHook)}, + }, + Values: &chart.Config{Raw: "defaultFoo: defaultBar"}, + }, + Values: &chart.Config{Raw: "foo: bar"}, + } + + fmt.Println("Running Install release with foo: bar override") + installResp, err := rs.InstallRelease(c, installReq) + if err != nil { + t.Fatal(err) + } + + rel := installResp.Release + req := &services.UpdateReleaseRequest{ + Name: rel.Name, + Chart: &chart.Chart{ + Metadata: &chart.Metadata{Name: "hello"}, + Templates: []*chart.Template{ + {Name: "templates/hello", Data: []byte("hello: world")}, + {Name: "templates/hooks", Data: []byte(manifestWithUpgradeHooks)}, + }, + Values: &chart.Config{Raw: "defaultFoo: defaultBar"}, + }, + } + + fmt.Println("Running Update release with no overrides and no reuse-values flag") + res, err := rs.UpdateRelease(c, req) + if err != nil { + t.Fatalf("Failed updated: %s", err) + } + + expect := "foo: bar" + if res.Release.Config != nil && res.Release.Config.Raw != expect { + t.Errorf("Expected chart values to be %q, got %q", expect, res.Release.Config.Raw) + } + + rel = res.Release + req = &services.UpdateReleaseRequest{ + Name: rel.Name, + Chart: &chart.Chart{ + Metadata: &chart.Metadata{Name: "hello"}, + Templates: []*chart.Template{ + {Name: "templates/hello", Data: []byte("hello: world")}, + {Name: "templates/hooks", Data: []byte(manifestWithUpgradeHooks)}, + }, + Values: &chart.Config{Raw: "defaultFoo: defaultBar"}, + }, + Values: &chart.Config{Raw: "foo2: bar2"}, + ReuseValues: true, + } + + fmt.Println("Running Update release with foo2: bar2 override and reuse-values") + res, err = rs.UpdateRelease(c, req) + if err != nil { + t.Fatalf("Failed updated: %s", err) + } + + // This should have the newly-passed overrides. + expect = "foo: bar\nfoo2: bar2\n" + if res.Release.Config != nil && res.Release.Config.Raw != expect { + t.Errorf("Expected request config to be %q, got %q", expect, res.Release.Config.Raw) + } + + rel = res.Release + req = &services.UpdateReleaseRequest{ + Name: rel.Name, + Chart: &chart.Chart{ + Metadata: &chart.Metadata{Name: "hello"}, + Templates: []*chart.Template{ + {Name: "templates/hello", Data: []byte("hello: world")}, + {Name: "templates/hooks", Data: []byte(manifestWithUpgradeHooks)}, + }, + Values: &chart.Config{Raw: "defaultFoo: defaultBar"}, + }, + Values: &chart.Config{Raw: "foo: baz"}, + ReuseValues: true, + } + + fmt.Println("Running Update release with foo=baz override with reuse-values flag") + res, err = rs.UpdateRelease(c, req) + if err != nil { + t.Fatalf("Failed updated: %s", err) + } + expect = "foo: baz\nfoo2: bar2\n" + if res.Release.Config != nil && res.Release.Config.Raw != expect { + t.Errorf("Expected chart values to be %q, got %q", expect, res.Release.Config.Raw) + } +} + func TestUpdateRelease_ReuseValues(t *testing.T) { c := helm.NewContext() rs := rsFixture() @@ -157,8 +259,8 @@ func TestUpdateRelease_ReuseValues(t *testing.T) { if res.Release.Chart.Values != nil && res.Release.Chart.Values.Raw != expect { t.Errorf("Expected chart values to be %q, got %q", expect, res.Release.Chart.Values.Raw) } - // This should have the newly-passed overrides. - expect = "name2: val2" + // This should have the newly-passed overrides and any other computed values. `name: value` comes from release Config via releaseStub() + expect = "name: value\nname2: val2" if res.Release.Config != nil && res.Release.Config.Raw != expect { t.Errorf("Expected request config to be %q, got %q", expect, res.Release.Config.Raw) } From e02802ba95e17c2a21dceeb56df893c01001e5d8 Mon Sep 17 00:00:00 2001 From: Michelle Noorali Date: Wed, 4 Apr 2018 15:54:12 -0400 Subject: [PATCH 42/71] ref(pkg/tiller): clarify reuseValues comment --- pkg/tiller/release_update.go | 2 +- pkg/tiller/release_update_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/tiller/release_update.go b/pkg/tiller/release_update.go index cb8b57792..6f5d37331 100644 --- a/pkg/tiller/release_update.go +++ b/pkg/tiller/release_update.go @@ -80,7 +80,7 @@ func (s *ReleaseServer) prepareUpdate(req *services.UpdateReleaseRequest) (*rele return nil, nil, err } - // If new values were not supplied in the upgrade, re-use the existing values. + // determine if values will be reused if err := s.reuseValues(req, currentRelease); err != nil { return nil, nil, err } diff --git a/pkg/tiller/release_update_test.go b/pkg/tiller/release_update_test.go index 0189201d1..a1b9a4bff 100644 --- a/pkg/tiller/release_update_test.go +++ b/pkg/tiller/release_update_test.go @@ -260,7 +260,7 @@ func TestUpdateRelease_ReuseValues(t *testing.T) { t.Errorf("Expected chart values to be %q, got %q", expect, res.Release.Chart.Values.Raw) } // This should have the newly-passed overrides and any other computed values. `name: value` comes from release Config via releaseStub() - expect = "name: value\nname2: val2" + expect = "name: value\nname2: val2\n" if res.Release.Config != nil && res.Release.Config.Raw != expect { t.Errorf("Expected request config to be %q, got %q", expect, res.Release.Config.Raw) } From e922a873bc30acfb1ed65031014eea6e25f604c1 Mon Sep 17 00:00:00 2001 From: Michelle Noorali Date: Mon, 19 Mar 2018 12:35:42 -0400 Subject: [PATCH 43/71] ref(cmd/upgrade): update reuse-values flag descrip helps clarify behavior. See #3655 --- cmd/helm/upgrade.go | 2 +- docs/helm/helm_upgrade.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/helm/upgrade.go b/cmd/helm/upgrade.go index 125796762..66c4a3657 100644 --- a/cmd/helm/upgrade.go +++ b/cmd/helm/upgrade.go @@ -130,7 +130,7 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command { f.StringVar(&upgrade.version, "version", "", "specify the exact chart version to use. If this is not specified, the latest version is used") f.Int64Var(&upgrade.timeout, "timeout", 300, "time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)") f.BoolVar(&upgrade.resetValues, "reset-values", false, "when upgrading, reset the values to the ones built into the chart") - f.BoolVar(&upgrade.reuseValues, "reuse-values", false, "when upgrading, reuse the last release's values, and merge in any new values. If '--reset-values' is specified, this is ignored.") + f.BoolVar(&upgrade.reuseValues, "reuse-values", false, "when upgrading, reuse the last release's values and merge in any overrides from the command line via --set and -f. If '--reset-values' is specified, this is ignored.") f.BoolVar(&upgrade.wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment are in a ready state before marking the release as successful. It will wait for as long as --timeout") f.StringVar(&upgrade.repoURL, "repo", "", "chart repository url where to locate the requested chart") f.StringVar(&upgrade.username, "username", "", "chart repository username where to locate the requested chart") diff --git a/docs/helm/helm_upgrade.md b/docs/helm/helm_upgrade.md index 7ada6025f..c2882265e 100644 --- a/docs/helm/helm_upgrade.md +++ b/docs/helm/helm_upgrade.md @@ -51,7 +51,7 @@ helm upgrade [RELEASE] [CHART] --recreate-pods performs pods restart for the resource if applicable --repo string chart repository url where to locate the requested chart --reset-values when upgrading, reset the values to the ones built into the chart - --reuse-values when upgrading, reuse the last release's values, and merge in any new values. If '--reset-values' is specified, this is ignored. + --reuse-values when upgrading, reuse the last release's values and merge in any overrides from the command line via --set and -f. If '--reset-values' is specified, this is ignored. --set stringArray set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) --set-string stringArray set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) --timeout int time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks) (default 300) @@ -81,4 +81,4 @@ helm upgrade [RELEASE] [CHART] ### SEE ALSO * [helm](helm.md) - The Helm package manager for Kubernetes. -###### Auto generated by spf13/cobra on 20-Mar-2018 +###### Auto generated by spf13/cobra on 4-Apr-2018 From 1e7915587f383a5e37f8506c9c1784bd37f43753 Mon Sep 17 00:00:00 2001 From: Daryl Walleck Date: Wed, 4 Apr 2018 18:43:03 -0500 Subject: [PATCH 44/71] Fixes typos introduced in #3540. Closes #3823 --- docs/charts_hooks.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/charts_hooks.md b/docs/charts_hooks.md index af6d0f4f9..4142f2ce0 100644 --- a/docs/charts_hooks.md +++ b/docs/charts_hooks.md @@ -191,8 +191,8 @@ When helm release being updated it is possible, that hook resource already exist One might choose `"helm.sh/hook-delete-policy": "before-hook-creation"` over `"helm.sh/hook-delete-policy": "hook-succeeded,hook-failed"` because: -* It is convinient to keep failed hook job resource in kubernetes for example for manual debug. +* It is convenient to keep failed hook job resource in kubernetes for example for manual debug. * It may be necessary to keep succeeded hook resource in kubernetes for some reason. -* At the same time it is not desireable to do manual resource deletion before helm release upgrade. +* At the same time it is not desirable to do manual resource deletion before helm release upgrade. -`"helm.sh/hook-delete-policy": "before-hook-creation"` annotation on hook causes tiller to remove the hook from previous release if there is one before the new hook is launched and can be used with another policies. +`"helm.sh/hook-delete-policy": "before-hook-creation"` annotation on hook causes tiller to remove the hook from previous release if there is one before the new hook is launched and can be used with another policy. From f52d0f4f18003ab6392a1b768abc2bd68cf82649 Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Thu, 5 Apr 2018 08:06:47 -0700 Subject: [PATCH 45/71] rename TPR to CRD --- .../custom_resource_definitions.md | 37 ++++++++++++++++++ .../third_party_resources.md | 38 ------------------- 2 files changed, 37 insertions(+), 38 deletions(-) create mode 100644 docs/chart_best_practices/custom_resource_definitions.md delete mode 100644 docs/chart_best_practices/third_party_resources.md diff --git a/docs/chart_best_practices/custom_resource_definitions.md b/docs/chart_best_practices/custom_resource_definitions.md new file mode 100644 index 000000000..96690dc9b --- /dev/null +++ b/docs/chart_best_practices/custom_resource_definitions.md @@ -0,0 +1,37 @@ +# Custom Resource Definitions + +This section of the Best Practices Guide deals with creating and using Custom Resource Definition +objects. + +When working with Custom Resource Definitions (CRDs), it is important to distinguish +two different pieces: + +- There is a declaration of a CRD. This is the YAML file that has the kind `CustomResourceDefinition` +- Then there are resources that _use_ the CRD. Say a CRD defines `foo.example.com/v1`. Any resource + that has `apiVersion: example.com/v1` and kind `Foo` is a resource that uses the CRD. + +## Install a CRD Declaration Before Using the Resource + +Helm is optimized to load as many resources into Kubernetes as fast as possible. +By design, Kubernetes can take an entire set of manifests and bring them all +online (this is called the reconciliation loop). + +But there's a difference with CRDs. + +For a CRD, the declaration must be registered before any resources of that CRDs +kind(s) can be used. And the registration process sometimes takes a few seconds. + +### Method 1: Separate Charts + +One way to do this is to put the CRD definition in one chart, and then put any +resources that use that CRD in _another_ chart. + +In this method, each chart must be installed separately. + +### Method 2: Pre-install Hooks + +To package the two together, add a `pre-install` hook to the CRD definition so +that it is fully installed before the rest of the chart is executed. + +Note that if you create the CRD with a `pre-install` hook, that CRD definition +will not be deleted when `helm delete` is run. diff --git a/docs/chart_best_practices/third_party_resources.md b/docs/chart_best_practices/third_party_resources.md deleted file mode 100644 index cb0fcc754..000000000 --- a/docs/chart_best_practices/third_party_resources.md +++ /dev/null @@ -1,38 +0,0 @@ -# Third Party Resources - -This section of the Best Practices Guide deals with creating and using Third Party Resource -objects. - -When working with Third Party Resources (TPRs), it is important to distinguish -two different pieces: - -- There is a declaration of a TPR. This is the YAML file that has the kind `ThirdPartyResource` -- Then there are resources that _use_ the TPR. Say a TPR defines `foo.example.com/v1`. Any resource - that has `apiVersion: example.com/v1` and kind `Foo` is a resource that uses the - TPR. - -## Install a TPR Declaration Before Using the Resource - -Helm is optimized to load as many resources into Kubernetes as fast as possible. -By design, Kubernetes can take an entire set of manifests and bring them all -online (this is called the reconciliation loop). - -But there's a difference with TPRs. - -For a TPR, the declaration must be registered before any resources of that TPRs -kind(s) can be used. And the registration process sometimes takes a few seconds. - -### Method 1: Separate Charts - -One way to do this is to put the TPR definition in one chart, and then put any -resources that use that TPR in _another_ chart. - -In this method, each chart must be installed separately. - -### Method 2: Pre-install Hooks - -To package the two together, add a `pre-install` hook to the TPR definition so -that it is fully installed before the rest of the chart is executed. - -Note that if you create the TPR with a `pre-install` hook, that TPR definition -will not be deleted when `helm delete` is run. From c6faed101b4ad9c60c99f7f0fdb8edf093b73490 Mon Sep 17 00:00:00 2001 From: Stefan Henseler Date: Thu, 5 Apr 2018 23:51:21 +0200 Subject: [PATCH 46/71] Updates readme with choco install command --- README.md | 6 +++++- docs/install.md | 9 +++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7622fafc8..fb2e16bce 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,11 @@ Binary downloads of the Helm client can be found at the following links: - [Windows](https://kubernetes-helm.storage.googleapis.com/helm-v2.8.2-windows-amd64.tar.gz) Unpack the `helm` binary and add it to your PATH and you are good to go! -macOS/[homebrew](https://brew.sh/) users can also use `brew install kubernetes-helm`. + +If you want to use a package manager: + +- macOS/[homebrew](https://brew.sh/) users can use `brew install kubernetes-helm`. +- Windows/[chocolatey](https://chocolatey.org/) users can use `choco install kubernetes-helm`. To rapidly get Helm up and running, start with the [Quick Start Guide](https://docs.helm.sh/using_helm/#quickstart-guide). diff --git a/docs/install.md b/docs/install.md index 78b1a53b6..6ca13c90a 100755 --- a/docs/install.md +++ b/docs/install.md @@ -36,6 +36,15 @@ brew install kubernetes-helm (Note: There is also a formula for emacs-helm, which is a different project.) +### From Chocolatey (Windows) + +Members of the Kubernetes community have contributed a [Helm package](https://chocolatey.org/packages/kubernetes-helm) build to +[Chocolatey](https://chocolatey.org/). This package is generally up to date. + +``` +choco install kubernetes-helm +``` + ## From Script Helm now has an installer script that will automatically grab the latest version From 35132d141c54598e7dab23207acce7e6fb1dfa4a Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 6 Apr 2018 00:40:43 +0200 Subject: [PATCH 47/71] toYaml - Fixes #3410 - trailing \n issue `toYaml` utilized by `.Files` was introducing a new line. It is an issue since the new line is part of a functions output, it can't be whitespace chomped away so it would require a `trimSuffix "\n"` pipe. This commit trims one trailing `\n` from the toYaml output. --- pkg/chartutil/files.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/chartutil/files.go b/pkg/chartutil/files.go index a711a3366..ced8ce15a 100644 --- a/pkg/chartutil/files.go +++ b/pkg/chartutil/files.go @@ -175,7 +175,7 @@ func ToYaml(v interface{}) string { // Swallow errors inside of a template. return "" } - return string(data) + return strings.TrimSuffix(string(data), "\n") } // FromYaml converts a YAML document into a map[string]interface{}. From 6cdf6cee56dcd90e3dca82950b15d46e6bb4587b Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 6 Apr 2018 00:41:14 +0200 Subject: [PATCH 48/71] toYaml - Fixes #3470 - trailing \n issue `toYaml` was introducing a new line. It is an issue since the new line is part of a functions output, it can't be whitespace chomped away so it would require a `trimSuffix "\n"` pipe. This commit trims one trailing `\n` from the toYaml output. --- pkg/strvals/parser.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/strvals/parser.go b/pkg/strvals/parser.go index 8a16adf7e..aa3d15904 100644 --- a/pkg/strvals/parser.go +++ b/pkg/strvals/parser.go @@ -36,7 +36,7 @@ func ToYAML(s string) (string, error) { return "", err } d, err := yaml.Marshal(m) - return string(d), err + return strings.TrimSuffix(string(d), "\n"), err } // Parse parses a set line. From 05a1f7f46c2ce2beacbb099d06a99422376f848d Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 6 Apr 2018 00:59:22 +0200 Subject: [PATCH 49/71] Updated tests for PR #3837 --- pkg/chartutil/files_test.go | 8 ++++---- pkg/strvals/parser_test.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/chartutil/files_test.go b/pkg/chartutil/files_test.go index 731c82e6f..5cec35883 100644 --- a/pkg/chartutil/files_test.go +++ b/pkg/chartutil/files_test.go @@ -72,10 +72,10 @@ func TestToConfig(t *testing.T) { f := NewFiles(getTestFiles()) out := f.Glob("**/captain.txt").AsConfig() - as.Equal("captain.txt: The Captain\n", out) + as.Equal("captain.txt: The Captain", out) out = f.Glob("ship/**").AsConfig() - as.Equal("captain.txt: The Captain\nstowaway.txt: Legatt\n", out) + as.Equal("captain.txt: The Captain\nstowaway.txt: Legatt", out) } func TestToSecret(t *testing.T) { @@ -84,7 +84,7 @@ func TestToSecret(t *testing.T) { f := NewFiles(getTestFiles()) out := f.Glob("ship/**").AsSecrets() - as.Equal("captain.txt: VGhlIENhcHRhaW4=\nstowaway.txt: TGVnYXR0\n", out) + as.Equal("captain.txt: VGhlIENhcHRhaW4=\nstowaway.txt: TGVnYXR0", out) } func TestLines(t *testing.T) { @@ -99,7 +99,7 @@ func TestLines(t *testing.T) { } func TestToYaml(t *testing.T) { - expect := "foo: bar\n" + expect := "foo: bar" v := struct { Foo string `json:"foo"` }{ diff --git a/pkg/strvals/parser_test.go b/pkg/strvals/parser_test.go index 3f9828498..fd287bf8a 100644 --- a/pkg/strvals/parser_test.go +++ b/pkg/strvals/parser_test.go @@ -365,7 +365,7 @@ func TestToYAML(t *testing.T) { if err != nil { t.Fatal(err) } - expect := "name: value\n" + expect := "name: value" if o != expect { t.Errorf("Expected %q, got %q", expect, o) } From 138de17c64e6c01468f4e17ddff0231b34d3f683 Mon Sep 17 00:00:00 2001 From: AdamDang Date: Fri, 6 Apr 2018 16:58:40 +0800 Subject: [PATCH 50/71] Fix some typos outout->output Get's->get the the->the --- pkg/kube/client.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/kube/client.go b/pkg/kube/client.go index e4e2dc1bb..89e919e3d 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -56,7 +56,7 @@ import ( ) const ( - // MissingGetHeader is added to Get's outout when a resource is not found. + // MissingGetHeader is added to get output when a resource is not found. MissingGetHeader = "==> MISSING\nKIND\t\tNAME\n" ) @@ -406,7 +406,7 @@ func createPatch(mapping *meta.RESTMapping, target, current runtime.Object) ([]b // While different objects need different merge types, the parent function // that calls this does not try to create a patch when the data (first - // returned object) is nil. We can skip calculating the the merge type as + // returned object) is nil. We can skip calculating the merge type as // the returned merge type is ignored. if apiequality.Semantic.DeepEqual(oldData, newData) { return nil, types.StrategicMergePatchType, nil @@ -692,7 +692,7 @@ func (c *Client) watchPodUntilComplete(timeout time.Duration, info *resource.Inf return err } -//get an kubernetes resources's relation pods +//get a kubernetes resources' relation pods // kubernetes resource used select labels to relate pods func (c *Client) getSelectRelationPod(info *resource.Info, objPods map[string][]core.Pod) (map[string][]core.Pod, error) { if info == nil { From 8bd42d264515d65372c6728c2762df1b6e9da7ce Mon Sep 17 00:00:00 2001 From: Steffen Windoffer Date: Fri, 6 Apr 2018 11:21:54 +0200 Subject: [PATCH 51/71] remove unused servicePort from default ingress template --- pkg/chartutil/create.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/chartutil/create.go b/pkg/chartutil/create.go index 07bd11449..9f2a8cc1f 100644 --- a/pkg/chartutil/create.go +++ b/pkg/chartutil/create.go @@ -123,7 +123,6 @@ const defaultIgnore = `# Patterns to ignore when building packages. const defaultIngress = `{{- if .Values.ingress.enabled -}} {{- $fullName := include ".fullname" . -}} -{{- $servicePort := .Values.service.port -}} {{- $ingressPath := .Values.ingress.path -}} apiVersion: extensions/v1beta1 kind: Ingress From b718b1c87038d44f2519f50d2df25454aad5f926 Mon Sep 17 00:00:00 2001 From: Taylor Thomas Date: Fri, 6 Apr 2018 15:43:37 -0700 Subject: [PATCH 52/71] fix(package): Adds missing `set-string` flag and parameter A recent PR (#3471) wasn't up to date with the latest master changes. This fixes the issue and adds the right number of parameters to the `vals` function call. --- cmd/helm/package.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/helm/package.go b/cmd/helm/package.go index 4b2e6b398..bf171e534 100644 --- a/cmd/helm/package.go +++ b/cmd/helm/package.go @@ -55,6 +55,7 @@ type packageCmd struct { path string valueFiles valueFiles values []string + stringValues []string key string keyring string version string @@ -99,6 +100,7 @@ func newPackageCmd(out io.Writer) *cobra.Command { f := cmd.Flags() f.VarP(&pkg.valueFiles, "values", "f", "specify values in a YAML file or a URL(can specify multiple)") f.StringArrayVar(&pkg.values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)") + f.StringArrayVar(&pkg.stringValues, "set-string", []string{}, "set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)") f.BoolVar(&pkg.save, "save", true, "save packaged chart to local chart repository") f.BoolVar(&pkg.sign, "sign", false, "use a PGP private key to sign this package") f.StringVar(&pkg.key, "key", "", "name of the key to use when signing. Used if --sign is true") @@ -137,7 +139,7 @@ func (p *packageCmd) run() error { return err } - overrideVals, err := vals(p.valueFiles, p.values) + overrideVals, err := vals(p.valueFiles, p.values, p.stringValues) if err != nil { return err } From 85282ab864973cbbb85d97849c37be0432df7149 Mon Sep 17 00:00:00 2001 From: Taylor Thomas Date: Fri, 6 Apr 2018 16:33:38 -0700 Subject: [PATCH 53/71] fix(docs): Add the missing docs Because I was too stupid to regen the docs and flake made it so we didn't get a reminder --- docs/helm/helm_package.md | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/docs/helm/helm_package.md b/docs/helm/helm_package.md index daceef32c..957f9c3b2 100644 --- a/docs/helm/helm_package.md +++ b/docs/helm/helm_package.md @@ -23,16 +23,17 @@ helm package [flags] [CHART_PATH] [...] ### Options ``` - --app-version string set the appVersion on the chart to this version - -u, --dependency-update update dependencies from "requirements.yaml" to dir "charts/" before packaging - -d, --destination string location to write the chart. (default ".") - --key string name of the key to use when signing. Used if --sign is true - --keyring string location of a public keyring (default "~/.gnupg/pubring.gpg") - --save save packaged chart to local chart repository (default true) - --set stringArray set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) - --sign use a PGP private key to sign this package - -f, --values valueFiles specify values in a YAML file or a URL(can specify multiple) (default []) - --version string set the version on the chart to this semver version + --app-version string set the appVersion on the chart to this version + -u, --dependency-update update dependencies from "requirements.yaml" to dir "charts/" before packaging + -d, --destination string location to write the chart. (default ".") + --key string name of the key to use when signing. Used if --sign is true + --keyring string location of a public keyring (default "~/.gnupg/pubring.gpg") + --save save packaged chart to local chart repository (default true) + --set stringArray set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) + --set-string stringArray set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) + --sign use a PGP private key to sign this package + -f, --values valueFiles specify values in a YAML file or a URL(can specify multiple) (default []) + --version string set the version on the chart to this semver version ``` ### Options inherited from parent commands @@ -49,4 +50,4 @@ helm package [flags] [CHART_PATH] [...] ### SEE ALSO * [helm](helm.md) - The Helm package manager for Kubernetes. -###### Auto generated by spf13/cobra on 14-Mar-2018 +###### Auto generated by spf13/cobra on 6-Apr-2018 From ee9ef91df0ea72646ae0ea23497622bfd019a944 Mon Sep 17 00:00:00 2001 From: AdamDang Date: Sat, 7 Apr 2018 08:56:44 +0800 Subject: [PATCH 54/71] typo fix get->Get's typo fix get->Get's --- pkg/kube/client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/kube/client.go b/pkg/kube/client.go index 89e919e3d..8a7402938 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -56,7 +56,7 @@ import ( ) const ( - // MissingGetHeader is added to get output when a resource is not found. + // MissingGetHeader is added to Get's output when a resource is not found. MissingGetHeader = "==> MISSING\nKIND\t\tNAME\n" ) From 826781a1a3ceddaf0abeced14bee867d86c96b4e Mon Sep 17 00:00:00 2001 From: Arash Deshmeh Date: Sat, 7 Apr 2018 12:04:29 -0400 Subject: [PATCH 55/71] fix(helm) refactor reset command unit tests to remove duplication in test code Signed-off-by: Arash Deshmeh --- cmd/helm/reset_test.go | 169 ++++++++++++++++------------------------- 1 file changed, 65 insertions(+), 104 deletions(-) diff --git a/cmd/helm/reset_test.go b/cmd/helm/reset_test.go index 458736a63..ae6a00036 100644 --- a/cmd/helm/reset_test.go +++ b/cmd/helm/reset_test.go @@ -31,40 +31,58 @@ import ( "k8s.io/helm/pkg/proto/hapi/release" ) +type resetCase struct { + name string + err bool + resp []*release.Release + removeHelmHome bool + force bool + expectedActions int + expectedOutput string +} + func TestResetCmd(t *testing.T) { - home, err := ioutil.TempDir("", "helm_home") - if err != nil { - t.Fatal(err) - } - defer os.Remove(home) - var buf bytes.Buffer - c := &helm.FakeClient{} - fc := fake.NewSimpleClientset() - cmd := &resetCmd{ - out: &buf, - home: helmpath.Home(home), - client: c, - kubeClient: fc, - namespace: core.NamespaceDefault, - } - if err := cmd.run(); err != nil { - t.Errorf("unexpected error: %v", err) - } - actions := fc.Actions() - if len(actions) != 3 { - t.Errorf("Expected 3 actions, got %d", len(actions)) - } - expected := "Tiller (the Helm server-side component) has been uninstalled from your Kubernetes Cluster." - if !strings.Contains(buf.String(), expected) { - t.Errorf("expected %q, got %q", expected, buf.String()) - } - if _, err := os.Stat(home); err != nil { - t.Errorf("Helm home directory %s does not exists", home) - } + verifyResetCmd(t, resetCase{ + name: "test reset command", + expectedActions: 3, + expectedOutput: "Tiller (the Helm server-side component) has been uninstalled from your Kubernetes Cluster.", + }) } func TestResetCmd_removeHelmHome(t *testing.T) { + verifyResetCmd(t, resetCase{ + name: "test reset command - remove helm home", + removeHelmHome: true, + expectedActions: 3, + expectedOutput: "Tiller (the Helm server-side component) has been uninstalled from your Kubernetes Cluster.", + }) +} + +func TestReset_deployedReleases(t *testing.T) { + verifyResetCmd(t, resetCase{ + name: "test reset command - deployed releases", + resp: []*release.Release{ + helm.ReleaseMock(&helm.MockReleaseOptions{Name: "atlas-guide", StatusCode: release.Status_DEPLOYED}), + }, + err: true, + expectedOutput: "there are still 1 deployed releases (Tip: use --force to remove Tiller. Releases will not be deleted.)", + }) +} + +func TestReset_forceFlag(t *testing.T) { + verifyResetCmd(t, resetCase{ + name: "test reset command - force flag", + force: true, + resp: []*release.Release{ + helm.ReleaseMock(&helm.MockReleaseOptions{Name: "atlas-guide", StatusCode: release.Status_DEPLOYED}), + }, + expectedActions: 3, + expectedOutput: "Tiller (the Helm server-side component) has been uninstalled from your Kubernetes Cluster.", + }) +} + +func verifyResetCmd(t *testing.T, tc resetCase) { home, err := ioutil.TempDir("", "helm_home") if err != nil { t.Fatal(err) @@ -72,99 +90,42 @@ func TestResetCmd_removeHelmHome(t *testing.T) { defer os.Remove(home) var buf bytes.Buffer - c := &helm.FakeClient{} + c := &helm.FakeClient{ + Rels: tc.resp, + } fc := fake.NewSimpleClientset() cmd := &resetCmd{ - removeHelmHome: true, + removeHelmHome: tc.removeHelmHome, + force: tc.force, out: &buf, home: helmpath.Home(home), client: c, kubeClient: fc, namespace: core.NamespaceDefault, } - if err := cmd.run(); err != nil { - t.Errorf("unexpected error: %v", err) - } - actions := fc.Actions() - if len(actions) != 3 { - t.Errorf("Expected 3 actions, got %d", len(actions)) - } - expected := "Tiller (the Helm server-side component) has been uninstalled from your Kubernetes Cluster." - if !strings.Contains(buf.String(), expected) { - t.Errorf("expected %q, got %q", expected, buf.String()) - } - if _, err := os.Stat(home); err == nil { - t.Errorf("Helm home directory %s already exists", home) - } -} -func TestReset_deployedReleases(t *testing.T) { - home, err := ioutil.TempDir("", "helm_home") - if err != nil { - t.Fatal(err) - } - defer os.Remove(home) - - var buf bytes.Buffer - resp := []*release.Release{ - helm.ReleaseMock(&helm.MockReleaseOptions{Name: "atlas-guide", StatusCode: release.Status_DEPLOYED}), - } - c := &helm.FakeClient{ - Rels: resp, - } - fc := fake.NewSimpleClientset() - cmd := &resetCmd{ - out: &buf, - home: helmpath.Home(home), - client: c, - kubeClient: fc, - namespace: core.NamespaceDefault, - } err = cmd.run() - expected := "there are still 1 deployed releases (Tip: use --force to remove Tiller. Releases will not be deleted.)" - if !strings.Contains(err.Error(), expected) { + if !tc.err && err != nil { t.Errorf("unexpected error: %v", err) } - if _, err := os.Stat(home); err != nil { - t.Errorf("Helm home directory %s does not exists", home) - } -} -func TestReset_forceFlag(t *testing.T) { - home, err := ioutil.TempDir("", "helm_home") - if err != nil { - t.Fatal(err) + got := buf.String() + if tc.err { + got = err.Error() } - defer os.Remove(home) - var buf bytes.Buffer - resp := []*release.Release{ - helm.ReleaseMock(&helm.MockReleaseOptions{Name: "atlas-guide", StatusCode: release.Status_DEPLOYED}), - } - c := &helm.FakeClient{ - Rels: resp, - } - fc := fake.NewSimpleClientset() - cmd := &resetCmd{ - force: true, - out: &buf, - home: helmpath.Home(home), - client: c, - kubeClient: fc, - namespace: core.NamespaceDefault, - } - if err := cmd.run(); err != nil { - t.Errorf("unexpected error: %v", err) - } actions := fc.Actions() - if len(actions) != 3 { - t.Errorf("Expected 3 actions, got %d", len(actions)) + if tc.expectedActions > 0 && len(actions) != tc.expectedActions { + t.Errorf("Expected %d actions, got %d", tc.expectedActions, len(actions)) } - expected := "Tiller (the Helm server-side component) has been uninstalled from your Kubernetes Cluster." - if !strings.Contains(buf.String(), expected) { - t.Errorf("expected %q, got %q", expected, buf.String()) + if !strings.Contains(got, tc.expectedOutput) { + t.Errorf("expected %q, got %q", tc.expectedOutput, got) } - if _, err := os.Stat(home); err != nil { + _, err = os.Stat(home) + if !tc.removeHelmHome && err != nil { t.Errorf("Helm home directory %s does not exists", home) } + if tc.removeHelmHome && err == nil { + t.Errorf("Helm home directory %s exists", home) + } } From 3f5e82c83207c7444f98cc65a73bf71a0babeb1d Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 8 Apr 2018 23:18:47 +0200 Subject: [PATCH 56/71] Typo fix `*/ }}` would cause an error actually, one must use `*/}}` or `*/ -}}`. Not obvious at all. --- docs/chart_best_practices/templates.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/chart_best_practices/templates.md b/docs/chart_best_practices/templates.md index c9995ea0a..7b21c5fea 100644 --- a/docs/chart_best_practices/templates.md +++ b/docs/chart_best_practices/templates.md @@ -155,7 +155,7 @@ Template comments should be used when documenting features of a template, such a ```yaml {{- /* mychart.shortname provides a 6 char truncated version of the release name. -*/ }} +*/ -}} {{ define "mychart.shortname" -}} {{ .Release.Name | trunc 6 }} {{- end -}} From c22492ff016ea85d7b05be013ecd4458b00b02ae Mon Sep 17 00:00:00 2001 From: Arash Deshmeh Date: Mon, 9 Apr 2018 12:18:23 -0400 Subject: [PATCH 57/71] fix(helm): refactor tiller release install unit tests using chart and install request stubs Signed-off-by: Arash Deshmeh --- pkg/tiller/release_install_test.go | 150 +++++++---------------------- pkg/tiller/release_server_test.go | 125 ++++++++++++++++++++++-- 2 files changed, 148 insertions(+), 127 deletions(-) diff --git a/pkg/tiller/release_install_test.go b/pkg/tiller/release_install_test.go index 8e2238e74..2f21dc46b 100644 --- a/pkg/tiller/release_install_test.go +++ b/pkg/tiller/release_install_test.go @@ -22,7 +22,6 @@ import ( "testing" "k8s.io/helm/pkg/helm" - "k8s.io/helm/pkg/proto/hapi/chart" "k8s.io/helm/pkg/proto/hapi/release" "k8s.io/helm/pkg/proto/hapi/services" "k8s.io/helm/pkg/version" @@ -32,17 +31,7 @@ func TestInstallRelease(t *testing.T) { c := helm.NewContext() rs := rsFixture() - // TODO: Refactor this into a mock. - req := &services.InstallReleaseRequest{ - Namespace: "spaced", - Chart: &chart.Chart{ - Metadata: &chart.Metadata{Name: "hello"}, - Templates: []*chart.Template{ - {Name: "templates/hello", Data: []byte("hello: world")}, - {Name: "templates/hooks", Data: []byte(manifestWithHook)}, - }, - }, - } + req := installRequest() res, err := rs.InstallRelease(c, req) if err != nil { t.Fatalf("Failed install: %s", err) @@ -96,18 +85,9 @@ func TestInstallRelease_WithNotes(t *testing.T) { c := helm.NewContext() rs := rsFixture() - // TODO: Refactor this into a mock. - req := &services.InstallReleaseRequest{ - Namespace: "spaced", - Chart: &chart.Chart{ - Metadata: &chart.Metadata{Name: "hello"}, - Templates: []*chart.Template{ - {Name: "templates/hello", Data: []byte("hello: world")}, - {Name: "templates/hooks", Data: []byte(manifestWithHook)}, - {Name: "templates/NOTES.txt", Data: []byte(notesText)}, - }, - }, - } + req := installRequest( + withChart(withNotes(notesText)), + ) res, err := rs.InstallRelease(c, req) if err != nil { t.Fatalf("Failed install: %s", err) @@ -165,18 +145,9 @@ func TestInstallRelease_WithNotesRendered(t *testing.T) { c := helm.NewContext() rs := rsFixture() - // TODO: Refactor this into a mock. - req := &services.InstallReleaseRequest{ - Namespace: "spaced", - Chart: &chart.Chart{ - Metadata: &chart.Metadata{Name: "hello"}, - Templates: []*chart.Template{ - {Name: "templates/hello", Data: []byte("hello: world")}, - {Name: "templates/hooks", Data: []byte(manifestWithHook)}, - {Name: "templates/NOTES.txt", Data: []byte(notesText + " {{.Release.Name}}")}, - }, - }, - } + req := installRequest( + withChart(withNotes(notesText + " {{.Release.Name}}")), + ) res, err := rs.InstallRelease(c, req) if err != nil { t.Fatalf("Failed install: %s", err) @@ -236,17 +207,9 @@ func TestInstallRelease_TillerVersion(t *testing.T) { c := helm.NewContext() rs := rsFixture() - // TODO: Refactor this into a mock. - req := &services.InstallReleaseRequest{ - Namespace: "spaced", - Chart: &chart.Chart{ - Metadata: &chart.Metadata{Name: "hello", TillerVersion: ">=2.2.0"}, - Templates: []*chart.Template{ - {Name: "templates/hello", Data: []byte("hello: world")}, - {Name: "templates/hooks", Data: []byte(manifestWithHook)}, - }, - }, - } + req := installRequest( + withChart(withTiller(">=2.2.0")), + ) _, err := rs.InstallRelease(c, req) if err != nil { t.Fatalf("Expected valid range. Got %q", err) @@ -258,17 +221,9 @@ func TestInstallRelease_WrongTillerVersion(t *testing.T) { c := helm.NewContext() rs := rsFixture() - // TODO: Refactor this into a mock. - req := &services.InstallReleaseRequest{ - Namespace: "spaced", - Chart: &chart.Chart{ - Metadata: &chart.Metadata{Name: "hello", TillerVersion: "<2.0.0"}, - Templates: []*chart.Template{ - {Name: "templates/hello", Data: []byte("hello: world")}, - {Name: "templates/hooks", Data: []byte(manifestWithHook)}, - }, - }, - } + req := installRequest( + withChart(withTiller("<2.0.0")), + ) _, err := rs.InstallRelease(c, req) if err == nil { t.Fatalf("Expected to fail because of wrong version") @@ -284,29 +239,10 @@ func TestInstallRelease_WithChartAndDependencyNotes(t *testing.T) { c := helm.NewContext() rs := rsFixture() - // TODO: Refactor this into a mock. - req := &services.InstallReleaseRequest{ - Namespace: "spaced", - Chart: &chart.Chart{ - Metadata: &chart.Metadata{Name: "hello"}, - Templates: []*chart.Template{ - {Name: "templates/hello", Data: []byte("hello: world")}, - {Name: "templates/hooks", Data: []byte(manifestWithHook)}, - {Name: "templates/NOTES.txt", Data: []byte(notesText)}, - }, - Dependencies: []*chart.Chart{ - { - Metadata: &chart.Metadata{Name: "hello"}, - Templates: []*chart.Template{ - {Name: "templates/hello", Data: []byte("hello: world")}, - {Name: "templates/hooks", Data: []byte(manifestWithHook)}, - {Name: "templates/NOTES.txt", Data: []byte(notesText + " child")}, - }, - }, - }, - }, - } - + req := installRequest(withChart( + withNotes(notesText), + withDependency(withNotes(notesText+" child")), + )) res, err := rs.InstallRelease(c, req) if err != nil { t.Fatalf("Failed install: %s", err) @@ -335,10 +271,9 @@ func TestInstallRelease_DryRun(t *testing.T) { c := helm.NewContext() rs := rsFixture() - req := &services.InstallReleaseRequest{ - Chart: chartStub(), - DryRun: true, - } + req := installRequest(withDryRun(), + withChart(withSampleTemplates()), + ) res, err := rs.InstallRelease(c, req) if err != nil { t.Errorf("Failed install: %s", err) @@ -389,10 +324,7 @@ func TestInstallRelease_NoHooks(t *testing.T) { rs := rsFixture() rs.env.Releases.Create(releaseStub()) - req := &services.InstallReleaseRequest{ - Chart: chartStub(), - DisableHooks: true, - } + req := installRequest(withDisabledHooks()) res, err := rs.InstallRelease(c, req) if err != nil { t.Errorf("Failed install: %s", err) @@ -409,9 +341,7 @@ func TestInstallRelease_FailedHooks(t *testing.T) { rs.env.Releases.Create(releaseStub()) rs.env.KubeClient = newHookFailingKubeClient() - req := &services.InstallReleaseRequest{ - Chart: chartStub(), - } + req := installRequest() res, err := rs.InstallRelease(c, req) if err == nil { t.Error("Expected failed install") @@ -429,11 +359,10 @@ func TestInstallRelease_ReuseName(t *testing.T) { rel.Info.Status.Code = release.Status_DELETED rs.env.Releases.Create(rel) - req := &services.InstallReleaseRequest{ - Chart: chartStub(), - ReuseName: true, - Name: rel.Name, - } + req := installRequest( + withReuseName(), + withName(rel.Name), + ) res, err := rs.InstallRelease(c, req) if err != nil { t.Fatalf("Failed install: %s", err) @@ -457,16 +386,9 @@ func TestInstallRelease_KubeVersion(t *testing.T) { c := helm.NewContext() rs := rsFixture() - // TODO: Refactor this into a mock. - req := &services.InstallReleaseRequest{ - Chart: &chart.Chart{ - Metadata: &chart.Metadata{Name: "hello", KubeVersion: ">=0.0.0"}, - Templates: []*chart.Template{ - {Name: "templates/hello", Data: []byte("hello: world")}, - {Name: "templates/hooks", Data: []byte(manifestWithHook)}, - }, - }, - } + req := installRequest( + withChart(withKube(">=0.0.0")), + ) _, err := rs.InstallRelease(c, req) if err != nil { t.Fatalf("Expected valid range. Got %q", err) @@ -477,16 +399,10 @@ func TestInstallRelease_WrongKubeVersion(t *testing.T) { c := helm.NewContext() rs := rsFixture() - // TODO: Refactor this into a mock. - req := &services.InstallReleaseRequest{ - Chart: &chart.Chart{ - Metadata: &chart.Metadata{Name: "hello", KubeVersion: ">=5.0.0"}, - Templates: []*chart.Template{ - {Name: "templates/hello", Data: []byte("hello: world")}, - {Name: "templates/hooks", Data: []byte(manifestWithHook)}, - }, - }, - } + req := installRequest( + withChart(withKube(">=5.0.0")), + ) + _, err := rs.InstallRelease(c, req) if err == nil { t.Fatalf("Expected to fail because of wrong version") diff --git a/pkg/tiller/release_server_test.go b/pkg/tiller/release_server_test.go index 6c4d42e04..73ba08f67 100644 --- a/pkg/tiller/release_server_test.go +++ b/pkg/tiller/release_server_test.go @@ -105,23 +105,128 @@ func rsFixture() *ReleaseServer { } } -// chartStub creates a fully stubbed out chart. -func chartStub() *chart.Chart { - return &chart.Chart{ - // TODO: This should be more complete. - Metadata: &chart.Metadata{ - Name: "hello", +type chartOptions struct { + *chart.Chart +} + +type chartOption func(*chartOptions) + +func buildChart(opts ...chartOption) *chart.Chart { + c := &chartOptions{ + Chart: &chart.Chart{ + // TODO: This should be more complete. + Metadata: &chart.Metadata{ + Name: "hello", + }, + // This adds a basic template and hooks. + Templates: []*chart.Template{ + {Name: "templates/hello", Data: []byte("hello: world")}, + {Name: "templates/hooks", Data: []byte(manifestWithHook)}, + }, }, - // This adds basic templates, partials, and hooks. - Templates: []*chart.Template{ - {Name: "templates/hello", Data: []byte("hello: world")}, + } + + for _, opt := range opts { + opt(c) + } + + return c.Chart +} + +func withKube(version string) chartOption { + return func(opts *chartOptions) { + opts.Metadata.KubeVersion = version + } +} + +func withTiller(version string) chartOption { + return func(opts *chartOptions) { + opts.Metadata.TillerVersion = version + } +} + +func withDependency(dependencyOpts ...chartOption) chartOption { + return func(opts *chartOptions) { + opts.Dependencies = append(opts.Dependencies, buildChart(dependencyOpts...)) + } +} + +func withNotes(notes string) chartOption { + return func(opts *chartOptions) { + opts.Templates = append(opts.Templates, &chart.Template{ + Name: "templates/NOTES.txt", + Data: []byte(notes), + }) + } +} + +func withSampleTemplates() chartOption { + return func(opts *chartOptions) { + sampleTemplates := []*chart.Template{ + // This adds basic templates and partials. {Name: "templates/goodbye", Data: []byte("goodbye: world")}, {Name: "templates/empty", Data: []byte("")}, {Name: "templates/with-partials", Data: []byte(`hello: {{ template "_planet" . }}`)}, {Name: "templates/partials/_planet", Data: []byte(`{{define "_planet"}}Earth{{end}}`)}, - {Name: "templates/hooks", Data: []byte(manifestWithHook)}, + } + opts.Templates = append(opts.Templates, sampleTemplates...) + } +} + +type installOptions struct { + *services.InstallReleaseRequest +} + +type installOption func(*installOptions) + +func withName(name string) installOption { + return func(opts *installOptions) { + opts.Name = name + } +} + +func withDryRun() installOption { + return func(opts *installOptions) { + opts.DryRun = true + } +} + +func withDisabledHooks() installOption { + return func(opts *installOptions) { + opts.DisableHooks = true + } +} + +func withReuseName() installOption { + return func(opts *installOptions) { + opts.ReuseName = true + } +} + +func withChart(chartOpts ...chartOption) installOption { + return func(opts *installOptions) { + opts.Chart = buildChart(chartOpts...) + } +} + +func installRequest(opts ...installOption) *services.InstallReleaseRequest { + reqOpts := &installOptions{ + &services.InstallReleaseRequest{ + Namespace: "spaced", + Chart: buildChart(), }, } + + for _, opt := range opts { + opt(reqOpts) + } + + return reqOpts.InstallReleaseRequest +} + +// chartStub creates a fully stubbed out chart. +func chartStub() *chart.Chart { + return buildChart(withSampleTemplates()) } // releaseStub creates a release stub, complete with the chartStub as its chart. From 79ffa98ec0c05415961e1e87f1415fd47136d277 Mon Sep 17 00:00:00 2001 From: AdamDang Date: Tue, 10 Apr 2018 23:54:19 +0800 Subject: [PATCH 58/71] Proper none not in capitals Helm is a proper none, should be written in capitals. --- docs/chart_repository_sync_example.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/chart_repository_sync_example.md b/docs/chart_repository_sync_example.md index 2fff70de9..de140c636 100644 --- a/docs/chart_repository_sync_example.md +++ b/docs/chart_repository_sync_example.md @@ -3,7 +3,7 @@ ## Prerequisites * Install the [gsutil](https://cloud.google.com/storage/docs/gsutil) tool. *We rely heavily on the gsutil rsync functionality* -* Be sure to have access to the helm binary +* Be sure to have access to the Helm binary * _Optional: We recommend you set [object versioning](https://cloud.google.com/storage/docs/gsutil/addlhelp/ObjectVersioningandConcurrencyControl#top_of_page) on your GCS bucket in case you accidentally delete something._ ## Set up a local chart repository directory @@ -16,7 +16,7 @@ $ mv alpine-0.1.0.tgz fantastic-charts/ ``` ## Generate an updated index.yaml -Use helm to generate an updated index.yaml file by passing in the directory path and the url of the remote repository to the `helm repo index` command like this: +Use Helm to generate an updated index.yaml file by passing in the directory path and the url of the remote repository to the `helm repo index` command like this: ```console $ helm repo index fantastic-charts/ --url https://fantastic-charts.storage.googleapis.com From 4b09b0489b6ba1fbd413aaf599fe44e6697a1b1c Mon Sep 17 00:00:00 2001 From: xianlubird Date: Wed, 11 Apr 2018 20:51:58 +0800 Subject: [PATCH 59/71] Fix some typo Signed-off-by: xianlubird --- docs/chart_template_guide/named_templates.md | 2 +- docs/charts.md | 2 +- docs/plugins.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/chart_template_guide/named_templates.md b/docs/chart_template_guide/named_templates.md index 042842427..33425d102 100644 --- a/docs/chart_template_guide/named_templates.md +++ b/docs/chart_template_guide/named_templates.md @@ -105,7 +105,7 @@ data: {{- end }} ``` -As mentioned above, **template names are global**. As a result of this, if two templates are declared with the same name the last occurance will be the one that is used. Since templates in subcharts are compiled together with top-level templates, it is best to name your templates with _chart specific names_. A popular naming convention is to prefix each defined template with the name of the chart: `{{ define "mychart.labels" }}`. +As mentioned above, **template names are global**. As a result of this, if two templates are declared with the same name the last occurrence will be the one that is used. Since templates in subcharts are compiled together with top-level templates, it is best to name your templates with _chart specific names_. A popular naming convention is to prefix each defined template with the name of the chart: `{{ define "mychart.labels" }}`. ## Setting the scope of a template diff --git a/docs/charts.md b/docs/charts.md index 8722e6862..1bbab5f19 100644 --- a/docs/charts.md +++ b/docs/charts.md @@ -850,7 +850,7 @@ considerations in mind: - The `Chart.yaml` will be overwritten by the generator. - Users will expect to modify such a chart's contents, so documentation should indicate how users can do so. -- All occurences of `` will be replaced with the specified chart +- All occurrences of `` will be replaced with the specified chart name so that starter charts can be used as templates. Currently the only way to add a chart to `$HELM_HOME/starters` is to manually diff --git a/docs/plugins.md b/docs/plugins.md index b2cc3c812..82bcfe33b 100644 --- a/docs/plugins.md +++ b/docs/plugins.md @@ -134,7 +134,7 @@ downloaders: If such plugin is installed, Helm can interact with the repository using the specified protocol scheme by invoking the `command`. The special repository shall be added -similarily to the regular ones: `helm repo add favorite myprotocol://example.com/` +similarly to the regular ones: `helm repo add favorite myprotocol://example.com/` The rules for the special repos are the same to the regular ones: Helm must be able to download the `index.yaml` file in order to discover and cache the list of available Charts. From fa62c476fc2156495dce4cce76780d7c166f3011 Mon Sep 17 00:00:00 2001 From: eyalbe4 Date: Wed, 11 Apr 2018 19:07:15 +0300 Subject: [PATCH 60/71] Basic auth credentials from repo not used in install/upgrade/fetch commands #3858 --- pkg/downloader/chart_downloader.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/downloader/chart_downloader.go b/pkg/downloader/chart_downloader.go index 9262ea547..fe2f3ce92 100644 --- a/pkg/downloader/chart_downloader.go +++ b/pkg/downloader/chart_downloader.go @@ -167,7 +167,6 @@ func (c *ChartDownloader) ResolveChartVersionAndGetRepo(ref, version string) (*u if err != nil { return u, nil, nil, err } - g.SetCredentials(c.getRepoCredentials(nil)) if u.IsAbs() && len(u.Host) > 0 && len(u.Path) > 0 { // In this case, we have to find the parent repo that contains this chart @@ -203,6 +202,7 @@ func (c *ChartDownloader) ResolveChartVersionAndGetRepo(ref, version string) (*u repoName := p[0] chartName := p[1] rc, err := pickChartRepositoryConfigByName(repoName, rf.Repositories) + if err != nil { return u, nil, nil, err } @@ -211,6 +211,7 @@ func (c *ChartDownloader) ResolveChartVersionAndGetRepo(ref, version string) (*u if err != nil { return u, nil, nil, err } + g.SetCredentials(c.getRepoCredentials(r)) // Next, we need to load the index, and actually look up the chart. i, err := repo.LoadIndexFile(c.HelmHome.CacheIndex(r.Config.Name)) From 87cd8ce79a16e3a17d97c7668b8d021a42932006 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20R=C3=BCger?= Date: Wed, 11 Apr 2018 18:33:50 +0200 Subject: [PATCH 61/71] Remove Mercurial build-time dependency Since the only dep (bitbucket.org/ww/goautoneg) that required it is gone in https://github.com/kubernetes/helm/commit/845e99014362983d01179a411ae664b104e6c1f7#diff-f16a80eae23d5b298c2652448ec420cfL4 --- Makefile | 4 ---- docs/developers.md | 1 - docs/install.md | 2 +- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 709cc301a..2dfbd2c3c 100644 --- a/Makefile +++ b/Makefile @@ -120,7 +120,6 @@ coverage: HAS_GLIDE := $(shell command -v glide;) HAS_GOX := $(shell command -v gox;) HAS_GIT := $(shell command -v git;) -HAS_HG := $(shell command -v hg;) .PHONY: bootstrap bootstrap: @@ -133,9 +132,6 @@ endif ifndef HAS_GIT $(error You must install Git) -endif -ifndef HAS_HG - $(error You must install Mercurial) endif glide install --strip-vendor go build -o bin/protoc-gen-go ./vendor/github.com/golang/protobuf/protoc-gen-go diff --git a/docs/developers.md b/docs/developers.md index 5095bf1a0..e18c28d5d 100644 --- a/docs/developers.md +++ b/docs/developers.md @@ -10,7 +10,6 @@ Helm and Tiller. - A Kubernetes cluster w/ kubectl (optional) - The gRPC toolchain - Git -- Mercurial ## Building Helm/Tiller diff --git a/docs/install.md b/docs/install.md index 6ca13c90a..17905a805 100755 --- a/docs/install.md +++ b/docs/install.md @@ -81,7 +81,7 @@ Building Helm from source is slightly more work, but is the best way to go if you want to test the latest (pre-release) Helm version. You must have a working Go environment with -[glide](https://github.com/Masterminds/glide) and Mercurial installed. +[glide](https://github.com/Masterminds/glide) installed. ```console $ cd $GOPATH From 00afbd7e74bf8d5ef7f7f30fc5e914b4a50ac7ef Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Wed, 11 Apr 2018 14:43:44 -0700 Subject: [PATCH 62/71] remove optional field from charts and templates --- docs/charts.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/charts.md b/docs/charts.md index 1bbab5f19..a19c1a477 100644 --- a/docs/charts.md +++ b/docs/charts.md @@ -27,8 +27,8 @@ wordpress/ README.md # OPTIONAL: A human-readable README file requirements.yaml # OPTIONAL: A YAML file listing dependencies for the chart values.yaml # The default configuration values for this chart - charts/ # OPTIONAL: A directory containing any charts upon which this chart depends. - templates/ # OPTIONAL: A directory of templates that, when combined with values, + charts/ # A directory containing any charts upon which this chart depends. + templates/ # A directory of templates that, when combined with values, # will generate valid Kubernetes manifest files. templates/NOTES.txt # OPTIONAL: A plain text file containing short usage notes ``` @@ -36,8 +36,6 @@ wordpress/ Helm reserves use of the `charts/` and `templates/` directories, and of the listed file names. Other files will be left as they are. -While the `charts` and `templates` directories are optional there must be at least one chart dependency or template file for the chart to be valid. - ## The Chart.yaml File The `Chart.yaml` file is required for a chart. It contains the following fields: From 62f85322ef114089724835e545d8d32d9bc30e12 Mon Sep 17 00:00:00 2001 From: Chance Zibolski Date: Wed, 11 Apr 2018 14:46:18 -0700 Subject: [PATCH 63/71] Update helm template -x to support children chart manifests in more cases The previous code validated the manifests exist within the file system, which only works when a chart is unpacked, meaning children charts stored as tgz's in the parent chart's `charts/` directory don't exist as individual files in the filesystem, causing validation to file. Instead, we validate against the paths within the chart itself rather than the filesystem, handling both chart.tgz's and charts that are unpacked as directories in the chart/ directory. --- cmd/helm/template.go | 78 ++++++++++++++++++++------------------- cmd/helm/template_test.go | 7 ++++ 2 files changed, 47 insertions(+), 38 deletions(-) diff --git a/cmd/helm/template.go b/cmd/helm/template.go index c04bc2dc8..fc1f44392 100644 --- a/cmd/helm/template.go +++ b/cmd/helm/template.go @@ -117,31 +117,10 @@ func (t *templateCmd) run(cmd *cobra.Command, args []string) error { } else { return err } - // verify specified templates exist relative to chart - rf := []string{} - var af string - var err error - if len(t.renderFiles) > 0 { - for _, f := range t.renderFiles { - if !filepath.IsAbs(f) { - af, err = filepath.Abs(filepath.Join(t.chartPath, f)) - if err != nil { - return fmt.Errorf("could not resolve template path: %s", err) - } - } else { - af = f - } - rf = append(rf, af) - - if _, err := os.Stat(af); err != nil { - return fmt.Errorf("could not resolve template path: %s", err) - } - } - } // verify that output-dir exists if provided if t.outputDir != "" { - _, err = os.Stat(t.outputDir) + _, err := os.Stat(t.outputDir) if os.IsNotExist(err) { return fmt.Errorf("output-dir '%s' does not exist", t.outputDir) } @@ -232,19 +211,7 @@ func (t *templateCmd) run(cmd *cobra.Command, args []string) error { m := tiller.Manifest{Name: k, Content: v, Head: &util.SimpleHead{Kind: h}} listManifests = append(listManifests, m) } - in := func(needle string, haystack []string) bool { - // make needle path absolute - d := strings.Split(needle, string(os.PathSeparator)) - dd := d[1:] - an := filepath.Join(t.chartPath, strings.Join(dd, string(os.PathSeparator))) - - for _, h := range haystack { - if h == an { - return true - } - } - return false - } + if settings.Debug { rel := &release.Release{ Name: t.releaseName, @@ -257,10 +224,45 @@ func (t *templateCmd) run(cmd *cobra.Command, args []string) error { printRelease(os.Stdout, rel) } - for _, m := range tiller.SortByKind(listManifests) { - if len(t.renderFiles) > 0 && !in(m.Name, rf) { - continue + var manifestsToRender []tiller.Manifest + + // if we have a list of files to render, then check that each of the + // provided files exists in the chart. + if len(t.renderFiles) > 0 { + for _, f := range t.renderFiles { + missing := true + if !filepath.IsAbs(f) { + newF, err := filepath.Abs(filepath.Join(t.chartPath, f)) + if err != nil { + return fmt.Errorf("could not turn template path %s into absolute path: %s", f, err) + } + f = newF + } + + for _, manifest := range listManifests { + manifestPathSplit := strings.Split(manifest.Name, string(filepath.Separator)) + // remove the chart name from the path + manifestPathSplit = manifestPathSplit[1:] + toJoin := append([]string{t.chartPath}, manifestPathSplit...) + manifestPath := filepath.Join(toJoin...) + + // if the filepath provided matches a manifest path in the + // chart, render that manifest + if f == manifestPath { + manifestsToRender = append(manifestsToRender, manifest) + missing = false + } + } + if missing { + return fmt.Errorf("could not find template %s in chart", f) + } } + } else { + // no renderFiles provided, render all manifests in the chart + manifestsToRender = listManifests + } + + for _, m := range tiller.SortByKind(manifestsToRender) { data := m.Content b := filepath.Base(m.Name) if !t.showNotes && b == "NOTES.txt" { diff --git a/cmd/helm/template_test.go b/cmd/helm/template_test.go index eefa46774..bbcc9f3af 100644 --- a/cmd/helm/template_test.go +++ b/cmd/helm/template_test.go @@ -69,6 +69,13 @@ func TestTemplateCmd(t *testing.T) { expectKey: "subchart1/templates/service.yaml", expectValue: "protocol: TCP\n name: apache", }, + { + name: "check_execute_subchart_template", + desc: "verify --execute single template on a subchart template", + args: []string{chartPath, "-x", "charts/subcharta/templates/service.yaml", "--set", "subcharta.service.name=foobar"}, + expectKey: "subchart1/charts/subcharta/templates/service.yaml", + expectValue: "protocol: TCP\n name: foobar", + }, { name: "check_namespace", desc: "verify --namespace", From d7e71709935b2f3b93c90c85ecdbf3d247cc7ebf Mon Sep 17 00:00:00 2001 From: Chance Zibolski Date: Wed, 11 Apr 2018 16:26:15 -0700 Subject: [PATCH 64/71] Add working template to mariner chart for use in testing subcharts --- pkg/chartutil/testdata/frobnitz-1.2.3.tgz | Bin 2070 -> 3654 bytes .../frobnitz/charts/mariner-4.3.2.tgz | Bin 1034 -> 1012 bytes .../mariner/charts/albatross-0.1.0.tgz | Bin 347 -> 292 bytes .../mariner/templates/placeholder.tpl | 2 ++ 4 files changed, 2 insertions(+) diff --git a/pkg/chartutil/testdata/frobnitz-1.2.3.tgz b/pkg/chartutil/testdata/frobnitz-1.2.3.tgz index fb21cd08fb08fd2f4e45e940634ce1cdb0421457..cd12d2c1ef7d96805820389c38a21d03add7f06d 100644 GIT binary patch literal 3654 zcmV-M4!Q9kiwFR5n9f=N1MOT3eA84H-+ti4f}qF;D$HFn{Rzs_cgxCnOg4}=Y`kAv z+T5*SX;YGPUBNK~kqPn;KINs;!KWZ^##B&n2r4=RR1i@ZQxPVQjhD(mpx;f>m90D~ z&=t`9e~hHL=RT5?bMCq4oSQV3G21DAj54hDpi-%fdOZ-22I;8M=%k|;K&{mnv^t$Z ztP;|UfG#bPDw7IQsL^W?|MfbpDh#NCJ&>#6U(SDt<4VQt9*2zE1E2hN zwh}Cly9pZ|^gaT~zd@_^)Bk#nRx8VYC_p%+6a zDt5-qknS3lIL66ZptnaW&pSAiQdx~anZ__mGr=mXjL@<`QIkMHS%iDOT2+HC%b>#@ z_H+edw=+B;JUX!_n8mXMdO9ZQER&#}!Z-;;WQD9);60a$Ld1Wc{96S1=W4|a{CoCa z_4!|?H)>`3FBI_cpJXhx!~L7$zea16`5y}S_%En5oXbqFg+2n!|9XSLpZ`XKQJ()p zfw+!YHX3-R@%CsIS79AHDB{qI08jy%y3kpB%sU0TTrS+D#ThnTsX;fXL}i#nc3l#2 zI+j|?qA7dE)ri%xv9U_AR3fi2BH*G(-r641=nWXM0jP8<@BKlM?J*v5Jk|l#iWAJ0 z4vEri41^atPAOgz%2;UOMe;O?Mf4yGwSWP)_`kP#o$7My26UcA(R6#vPGq_g|BCqkE)jWSU!?HfZN@(GIuJ6gmes!VO4@bI|C>qdNE<^bX6odHCeNXSdFY zy^|AbqmO{{uhIL@|5~+Lmj6(|%XWYS+XAf&O+r={?SBz`@}HC#pU^82w~@i`Bar-S zME_sS^}j(Q%YP^k4Z2kM=oHm#0k2C7h+ZLFp@@zK12`fbUX2I`-FRRnIACXhQy`M) z6JRE!8HZp=C=^{77Ld{f$RbN4jcEfo2$3m){uoCLEtCNkIv9@P8P+Z9pb)_)|12Es zq*y4hi^FNgk`cT<0?EHt6!7t1j5-Hf^GgV% z|J6Ew{jb*S<0|^U>I~-{!BGVP{l7Z@_>a+`mF>Tf;8{r13>Z5W z7e)6uK*BV_N%H`6F)U4D+1auH|D#be|EG4Gzyal7f5rT-SIhq2kl=DsXSE0rNE6(( z-Y~G&0Jz-)l~F73&;Lx3HUy;Dg18GLp#87%&;K$Q)Uy2_5{UB8Ss5oy0yDDRMed_h zw-}nC83L(;Ho)wZGC%|hyM=a=h(t8=z44*jeHSur7XI=t`Tc_H1G;hlk3prA^S?s^ zfBt*jv@xP%7U$E)1fh=r`X8sD<^PP9xiYAwksCdSRpI?idpxvzb@bTC!-=}&k67%|MeIzPU1CyE+6WpJyQ_7B<0{v7#i(x{;u zhR5ufF>&5U3*MMllAh=B45@G4(q$c9m|Jhc0|S~oP&jJke=v6C_Bsiz7j`dLKBb^R z-F}Vl(bxU&XZ!nymqtIZYlXG7HRHEE2Y*{lo{l>FWog$n8@sMPeJW-4gt;dv>%6y9 zCU$5v;58;Pr{BxpR@R^LRF7|Rv2QA$p78jj@qN#)*#DI$?vkOfZ-Za^gfEWnv*pDe z4Yp2yKH}@#p7pIi%nqN`Fk*Da=T}dwcY0k+y|JHwd2=f3&0!1HzOZF~$;XkOM<#vY z0mTP?*}JLyi0Al)#gQ)^$%$?CY1E>IrJKHZ#&D{;txo-;MUCe*%Ng(>e8+RBQ&+se zNoGWxHn#|S50usWx@V^ekvkU^g^z#bSXRVnhbbcIg>@sBuWV?W_*w=wl3V`a`llKX zPvUb|Z<@Mc)AFB-y0v8Mev|slsPd_);UA3f>{{L|F?NY(p<TGBA;nIvt4+6zUTP-&tE*;Yo}4SV#Idt)zswTlVKM- zo$TIu*XKh`ncnZob2ocT#pupAp}0dcm=>7Oh(hZ+dv$kdL;Rnnos=$EQZAr>^JhdvboX zwmLM|v^j2ckNk-GdvYdd26>`+u*_4u`S|g?NcZ$%C)$p|rgchxvUHYqGX2$Rdl!sZw`}U6l8xIoob{N0NNL``JdVmaQ@UzR-%Hzm-)X+I4Ew{g1e@B%v+a|m zy81 zuD^6}&C~PBx39{h+pTHo-aW5H#f5g-wHJ0L>|8(RV_Uy_f8t9!xsG1g(Wuez{l?hJ z)MNa`8R_GWJ~C$EFJ-Uf*_2V)KP8^!53l;}$SV~ck0}TA*?wWoqRrckY|3Tdi8rc7@_4of7W&eL@P$Sw~ zW4mVpbkK|&+4yFHqbwj^Bmq4syE6+o81nMgPcbT7ZLg#dxXozG;S{43_G`+EgF6}5 zkpH0W2EB3n->B8d`~O0MYw$m)j^IH0Uw=jZpHVCOe?kM%|5KIi>moRi4$<BuEAeQ4scPJtoWL_SI@K>uEsU=!XjK)+W?12} zB2^k<4C+lZWm17Jw5d=GVVuBHZPycD{Xs~#F5PamZBblRLQ<8Sq~7(euWQJEP_uz< zSpREODxF%^|DnJ&_#f14pg{Ux<)8nfQE4>t{;!ZATHNF*=wo3eP?hClbH_+8f}Fqe z4@W@#?>C3uOdo;d-=Ou||EbX!4YK@)1b*Lt%Ao9|3G_wFdt`tSB*7Dxv-`%5#giZn z2@Z!;7 zaMF?<-P#n-cPDDf-(UEp8-y*AIDc@!qf2=j3_bdig-#B@>Ph`Djl?qj4P@ zHve!@*OKO=<`lnWFZ^9+doQx^d%WGKW1An}k+|ar`|v%3SFJ4?_ekZMtV^AX@d3>% zD=tRu%vtk#&dSFtJUw#HjkPu0IDbawtj3jzmy!%$cp958b^Wf-k8ht3uUzWkhP-jn z^W{5fzk7PD%fGke-FMG6$pD4t`!J;{1_hFT zwfYMCPb2UD4GpR`=OI4V4FAMSU*Hs91kt0`!)b=B9ilbC)k8m+yjHvm@pt6^R-HkC zDc zVQyr3R8em|NM&qo0PLOLZ`(K$$NQ|mVxYXPWl8>ZHInWHO%_{RHdt(P2P_VU3oVT; zB9y2lX*<4b{`UhZS$33ol{Svjr2IZKvPhA##1Cgk4&ABXlZ>kWbw4IVC~rkl_HN(u zecvAq2IjBt`}M#6&>vOD=6n#2g26l3`;9!Lxl}~F^ZlomQ~z?WL?p|&B8u`%jvWA! zah0IB!qs?vydZ3j4gg*&K}>=VGe5Bfok|6b7V zTmIY70USf|>S0P5Lc|R^QXfp|Y%WaS;5lV8cXiW;bCRO#I1(d6dc}pS6M$wwpiFaL zu4P+2Miu`G)0`27vO4lqzMrn3iGRNz*7zUvf}Z8S4gHLaak#nh47z_pj8iga3fv_Z zEOu{iEx>PD75|?0kLWDp0{2}D`@ZGB4IRKw>zQk3%NHo8tRXV1LXLBA0RNO^h7V=(;FE%cNXR(Mb*3W! z2`-TFTze9Z^Ai^k7bj2v&8GYZkcglX1jX|X(`g_u=aMQeR<;jnNfrMwkK{9T0*&^6 zaDV=PWchDJn?<@U4*;eb*K!z8E&#mrjaog6D*kE2GfysN&rnAb|AXPMZvXpzyZ+yb z4u8B(8I-HhM{eNx?vEdwL%Ep2b;>e1mW$buTQ1lymrHNi^SGFGL*Mtiaxrs@Y=4=> z0f4f!YeuuHhcXB6-@osg)FS(xJAx&RRdVEpgP{uv(pjR)4>Ue<6-woPZ~zWtl+lON z(sj`d`mk$G=`?|nGTEn8Nae!yksG?OGXIvv_x(-jL*IodW$eh!$YSMo@0KAk;#q78 z2?nN=enTk&V_nFhejp#dq0Y48U*I2eDp2?M(qlZj+Fu<_{P+C2{qOe%cKyE<#h7Cj zV-`^~0YIH{DW9;MX1I;w7+l}eG@6qeSN-9r9W!u{TLq>>&X+ z3rp;`tR4LCH5#~yfB#wm52z%26TJT_)}9p#&@Iz>aU@w;zaJNVy30~aSc zIgh`(H)!I2*sHDohkn0j`ENzF4Lnygi^uRQkDU}1iHRbc=>D4A&fB${C1+uP2~ zjaN8|#b*bAkqI(0kN`+fP(@~Uul$T+L8Brw6w8a}>(XmwmC4oZRXNHt@F)B+=c>qD z+s6dpYQMyjNjDNm6vplt+^ze#psBu@h-b09y>%U#W27$>sM~}3l`|Txbe&Z|^f@k2 zxZssJ{+pXDrt)&rgxPPj8SyL$Rpo$(rsg@g1x(|f*DJ<65l>U1pZLGs?u2Gt$q;k* z2lu`2`|f|*=zDkYzt@GpCjJNa=l@0{>;JbRv;MbU?OzfJGt5wEU|=q3HZv>qWiy3K z%@hh_<30)%JrZzIyR> zx56UjdO~8&$>pnG7u~^s`6(q*1y7y~XdM6h{TlzH(IB+^x1l@Z|KnmGta|EEp{H1Qt>y}JJ&27%?j4IRMAedB3fyXPEiZQ=j@_?NTJ zl#7%o?HgzttTUWo#E3xS@fPg_azcd!#bKh4rNkI$rZ}wlTvP^F>WlS7a0ITo&dxNv zh?@BiU&j91^}km174|>0_W$kZG5kNb{r7wQ`}W`R-->qH|M1lhZLJ=mI{#&u#$E#e zn(co*{&z6!+4%of^u_kSh~lX{029Dtdx}@9UHtDe0HAUFKdAFR^zHnAJKACYO(STY z0jOeuW>h8fiHXxwJY^k5vY9#B#~i`fP7yqtcJaSi&e`4>{&l|y*6jZWb^m|l+xfp% zv`x7E4S?@YyZGPdc7R6ze^CGZpOO9jzm~K+{(nN0;8H#+HhXdFct$FCMzS*7)(8lmGQQS%|2-P@PLQ%XW@8+wgs zC{cqN=XU3ETlOupvu)3qh#>i3n-2{XEL0(gmeQ(NX#^|8)Y=AHVz8k>LqKV1T8daG zEj<%$=k{JmQfgYfTrJLzgPFUVotfQdp8x+@3u41YxGuDa2!aq*6cDs(t`#D3uFV5P zDWXcUtctP>1W^(NH3XE!+*ASIBM{TAXenrG=+@)tuw?Qd=E>hjvAsvI^6_7jf|&4W zXqly7gFX*cmTw(@QH;{~3yLaB(TF0^_$z`Wg@91{t=!E2JpMMcPz>Zx0jDER!-h)? z$Buyp(1=V6XxWnik`4xOKY_4~2=cfs2z$XGnV0womvXYRgu~q3z8%ILQpo=x%HHDG z!0qY(?EI%4iRphiD8wJ54&T7YLN@VAZ37GHzY>`je^n7>rvK$YM=r8;WNQZUVjKXq zAm<3sOhAGi&!fKqfRXEX261pY22zB$UaYPTZG)C;B3^T>y16P6TvBw1u)vGFz^viF zNg;9s(O^Pq?TcUmw_X3!tQSQ&sQ;o0)Bkc%i2oL7`l#f}pJO5YmqcaW`=1ht3QYgY zK_z$&C82K;kUIfEq5tff#)jri&o?x!UCncqU{lKQ0R2GlN<-7eS~|jlgdiOx4XXb| zLufZh%1;D-Kpe}gHKE-e^tkLInh7xq%q4f$fC~-m0pfr}w(og#KEtM8(~gfpQI4|f z=Nu^Y)DCL+s!aQ+Hp#}@qe}Xbs|08rDtN+Tv z?ChQ$d5&A}IC>(DKxZf4k~4>F9)h=ko)?VpRtKAUxT}lh-M!1a_y1zqUmH~IJZXRP z->${wV*aZl_5Y|Mi!A@k!K&@|hdSOLX$>v?eJsBDJK>>aZ+z~*H?-WXjvpL4 z+}G5<_l3u*R}8o8JX`OyO}(?U->9kT8ofSoa@}xcZ+FF4a?Q1eR~7Y2d{^PMLneEN5o@_g@A$io99Pa!(Gqe2EM9;aGzHLzl;B?va8(XMXA#dZOazk)CZYAAR6O_jKm3)#;J$KkF}5PtE+Xqr&>;_{v|u zobg_3J2%*OU*`IX%QJf~eIop_=ZoIU@iULE(~pg1Mn{i4Q!%i=ckS0p=02G2kD@hK icf1~Y>h8pa!C){L36ZtiaDKCZKKh4VpUVhPw(Vq+5%p#1{`AAuCqVmyct4N85WAVyGp#n=>Wj1n4S z;p*((BjusRcz0-+&)sGA_I7u6_dCDuclIoZ4IANLpo|EDpsOnITP@cLl9Frl08!F) zQI-`+mw+HDk|+d#TF#Ryka7vc^i(WJNH|3z353tP9o;Mz`UG2>HDDfLsOK$)?XBLPk%{~bzM;vs=p>GasUXWKb3R2#PzqKg+d@d3b-h8BiKk1 z!?8nP9+;0z3q-t;0b&jY&8aZLHX_L7+7WjBjTBzyB`)E3N2#gdF81Xx{vn0>_f>Yw z69X6O|EeIVvL?{_R~21m{$B|S`eW3VGBC1`P25t)z?A;4N@wN2u8Au1|4I-=Nn}Tn z9Wjs_;sB@zxkP|w7!vHbE?oxzMoGsth=bE1kRT-KhJrz~0$NEE@e#)gp6Md~F2#hX z5qOaoSTy`MDJVw}6%*2EFGB=ep#M*v|4Cl`Gyg9?1^wHhnL;IZ{v1>Jzocru{;DFY zvZ8zXD}u!QzY@#>_o5g~nFQoUfIrdC4+@@}1r{d^7tl8ZOXofKKt27{yHO|#Vg~j8 zVi?2?l1PR9EFg|$)|=3d`%9eHLBxa@`N5JKXCMg;>;mF|u(#~G^mv9%zowlO21P6K z`p>0NjlUbqkkWIm|I;Rd5{?vdH?_;X^S7q6M{?qA4k~LcYf~K+m|0+Ut*A;=jm8X{kE*t&)SnE4r zM%A}7hwC=o@X3?4*}Ff!Z$VXtJ9Kn&ORKnfAk%L&Pun>D;)phO*KK{PKizeFOIz=n zy*o!wAB(P@-@O0Xt)Vxb?^^Wuz7^Z9s$0DG<(79l=RDI;yJg+Mmmb}CrBT z-!}KT=4?9KaOjJkYcgQjk@g!0$KO1e>6trl|4%m!{=B?u+4!Emw}w`2=pOv&TJ81s z9#V$gq1JEK%Ii?$Zt#20ucxXP^=-QPiQlpZ?i{{l`pMzO-oRHAJB1&st=E><&K!N! zdCK`A=yslR;D-|}568cpeRH7ta7WJ{hsPI;tY0|c!1o)c|1u-g@WGDm6TNL{-wPw* z(Wd>^e|==&5o^vX4U=!@9%pP?@baZ~f!i;ZpbQ3s!C){L3#Uqb*v{OB-fRab*UwBA(c`Y z>HD>GHpXPa)Jof#p|#i7P0QBn5B52oW3GRMndiUgOi0D6>D{frG2KU{)b;s`Qe4bm z%SvX%Z{5gy{`vd|)7ysTSHM;_wqg5{wti^nk)EvUsolQ}TKhoH^`Tah5&VKqp@FnN`?~EI5yvGmVv;1>iU*^A(0{Q~{uR`@o*M z%7&edw#^PUw8u^~=X1(x-;Xn!Wk0%GbA5)&B41Dtb&pGEL74_#ol>prTw=m`( zZL@bK9qp@cfp6q5bA|kGVa)$vTxZ)U9snQJf0Fvu`%medQ2%dX$UhbD7&<%4vW@eV tab?Ds>0<3e$rj$(uw88|syhGx0000000000006*m_5v>lSt Date: Wed, 11 Apr 2018 16:28:14 -0700 Subject: [PATCH 65/71] test helm template -x against subcharts stored as tgz's --- cmd/helm/template_test.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/cmd/helm/template_test.go b/cmd/helm/template_test.go index bbcc9f3af..46adecca7 100644 --- a/cmd/helm/template_test.go +++ b/cmd/helm/template_test.go @@ -27,7 +27,10 @@ import ( "testing" ) -var chartPath = "./../../pkg/chartutil/testdata/subpop/charts/subchart1" +var ( + chartPath = "./../../pkg/chartutil/testdata/subpop/charts/subchart1" + frobnitzChartPath = "./../../pkg/chartutil/testdata/frobnitz" +) func TestTemplateCmd(t *testing.T) { absChartPath, err := filepath.Abs(chartPath) @@ -76,6 +79,13 @@ func TestTemplateCmd(t *testing.T) { expectKey: "subchart1/charts/subcharta/templates/service.yaml", expectValue: "protocol: TCP\n name: foobar", }, + { + name: "check_execute_subchart_template_for_tgz_subchart", + desc: "verify --execute single template on a subchart template where the subchart is a .tgz in the chart directory", + args: []string{frobnitzChartPath, "-x", "charts/mariner/templates/placeholder.tpl", "--set", "mariner.name=moon"}, + expectKey: "frobnitz/charts/mariner/templates/placeholder.tpl", + expectValue: "Goodbye moon", + }, { name: "check_namespace", desc: "verify --namespace", From ced20f6f4ebacbc120cd7e580668e3797aa7f2a9 Mon Sep 17 00:00:00 2001 From: Chance Zibolski Date: Wed, 11 Apr 2018 16:28:56 -0700 Subject: [PATCH 66/71] Disambigutate chartPaths variables in helm template tests --- cmd/helm/template_test.go | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/cmd/helm/template_test.go b/cmd/helm/template_test.go index 46adecca7..d2469525e 100644 --- a/cmd/helm/template_test.go +++ b/cmd/helm/template_test.go @@ -28,12 +28,12 @@ import ( ) var ( - chartPath = "./../../pkg/chartutil/testdata/subpop/charts/subchart1" - frobnitzChartPath = "./../../pkg/chartutil/testdata/frobnitz" + subchart1ChartPath = "./../../pkg/chartutil/testdata/subpop/charts/subchart1" + frobnitzChartPath = "./../../pkg/chartutil/testdata/frobnitz" ) func TestTemplateCmd(t *testing.T) { - absChartPath, err := filepath.Abs(chartPath) + subchart1AbsChartPath, err := filepath.Abs(subchart1ChartPath) if err != nil { t.Fatal(err) } @@ -47,35 +47,35 @@ func TestTemplateCmd(t *testing.T) { { name: "check_name", desc: "check for a known name in chart", - args: []string{chartPath}, + args: []string{subchart1ChartPath}, expectKey: "subchart1/templates/service.yaml", expectValue: "protocol: TCP\n name: nginx", }, { name: "check_set_name", desc: "verify --set values exist", - args: []string{chartPath, "-x", "templates/service.yaml", "--set", "service.name=apache"}, + args: []string{subchart1ChartPath, "-x", "templates/service.yaml", "--set", "service.name=apache"}, expectKey: "subchart1/templates/service.yaml", expectValue: "protocol: TCP\n name: apache", }, { name: "check_execute", desc: "verify --execute single template", - args: []string{chartPath, "-x", "templates/service.yaml", "--set", "service.name=apache"}, + args: []string{subchart1ChartPath, "-x", "templates/service.yaml", "--set", "service.name=apache"}, expectKey: "subchart1/templates/service.yaml", expectValue: "protocol: TCP\n name: apache", }, { name: "check_execute_absolute", desc: "verify --execute single template", - args: []string{chartPath, "-x", absChartPath + "/" + "templates/service.yaml", "--set", "service.name=apache"}, + args: []string{subchart1ChartPath, "-x", subchart1AbsChartPath + "/" + "templates/service.yaml", "--set", "service.name=apache"}, expectKey: "subchart1/templates/service.yaml", expectValue: "protocol: TCP\n name: apache", }, { name: "check_execute_subchart_template", desc: "verify --execute single template on a subchart template", - args: []string{chartPath, "-x", "charts/subcharta/templates/service.yaml", "--set", "subcharta.service.name=foobar"}, + args: []string{subchart1ChartPath, "-x", "charts/subcharta/templates/service.yaml", "--set", "subcharta.service.name=foobar"}, expectKey: "subchart1/charts/subcharta/templates/service.yaml", expectValue: "protocol: TCP\n name: foobar", }, @@ -89,42 +89,42 @@ func TestTemplateCmd(t *testing.T) { { name: "check_namespace", desc: "verify --namespace", - args: []string{chartPath, "--namespace", "test"}, + args: []string{subchart1ChartPath, "--namespace", "test"}, expectKey: "subchart1/templates/service.yaml", expectValue: "namespace: \"test\"", }, { name: "check_release_name", desc: "verify --release exists", - args: []string{chartPath, "--name", "test"}, + args: []string{subchart1ChartPath, "--name", "test"}, expectKey: "subchart1/templates/service.yaml", expectValue: "release-name: \"test\"", }, { name: "check_notes", desc: "verify --notes shows notes", - args: []string{chartPath, "--notes", "true"}, + args: []string{subchart1ChartPath, "--notes", "true"}, expectKey: "subchart1/templates/NOTES.txt", expectValue: "Sample notes for subchart1", }, { name: "check_values_files", desc: "verify --values files values exist", - args: []string{chartPath, "--values", chartPath + "/charts/subchartA/values.yaml"}, + args: []string{subchart1ChartPath, "--values", subchart1ChartPath + "/charts/subchartA/values.yaml"}, expectKey: "subchart1/templates/service.yaml", expectValue: "name: apache", }, { name: "check_name_template", desc: "verify --name-template result exists", - args: []string{chartPath, "--name-template", "foobar-{{ b64enc \"abc\" }}-baz"}, + args: []string{subchart1ChartPath, "--name-template", "foobar-{{ b64enc \"abc\" }}-baz"}, expectKey: "subchart1/templates/service.yaml", expectValue: "release-name: \"foobar-YWJj-baz\"", }, { name: "check_kube_version", desc: "verify --kube-version overrides the kubernetes version", - args: []string{chartPath, "--kube-version", "1.6"}, + args: []string{subchart1ChartPath, "--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\"", }, From b9adc356e8d1fe68b139a0b4c2495aa1e5fdebf8 Mon Sep 17 00:00:00 2001 From: Chance Zibolski Date: Wed, 11 Apr 2018 16:46:24 -0700 Subject: [PATCH 67/71] Correctly use subtests in helm template tests The testing.T in the subtest wasn't being used, so subtests were effectively not being used. Additionally, in case subtests run in parallel, we assign tt := tt to fix closure captures on range variables, since the variables produced by a range are re-used between iterations. --- cmd/helm/template_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/helm/template_test.go b/cmd/helm/template_test.go index d2469525e..7505b0474 100644 --- a/cmd/helm/template_test.go +++ b/cmd/helm/template_test.go @@ -132,7 +132,8 @@ func TestTemplateCmd(t *testing.T) { var buf bytes.Buffer for _, tt := range tests { - t.Run(tt.name, func(T *testing.T) { + tt := tt + t.Run(tt.name, func(t *testing.T) { // capture stdout old := os.Stdout r, w, _ := os.Pipe() From fb1dd48b5a1994e8a81a9b734cb6503d024799cd Mon Sep 17 00:00:00 2001 From: Chance Zibolski Date: Wed, 11 Apr 2018 16:48:53 -0700 Subject: [PATCH 68/71] test helm template -x with non-existent manifest --- cmd/helm/template_test.go | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/cmd/helm/template_test.go b/cmd/helm/template_test.go index 7505b0474..68ed87cd2 100644 --- a/cmd/helm/template_test.go +++ b/cmd/helm/template_test.go @@ -43,6 +43,7 @@ func TestTemplateCmd(t *testing.T) { args []string expectKey string expectValue string + expectError string }{ { name: "check_name", @@ -65,6 +66,12 @@ func TestTemplateCmd(t *testing.T) { expectKey: "subchart1/templates/service.yaml", expectValue: "protocol: TCP\n name: apache", }, + { + name: "check_execute_non_existent", + desc: "verify --execute fails on a template that doesnt exist", + args: []string{subchart1ChartPath, "-x", "templates/thisdoesntexist.yaml"}, + expectError: "could not find template", + }, { name: "check_execute_absolute", desc: "verify --execute single template", @@ -143,8 +150,21 @@ func TestTemplateCmd(t *testing.T) { cmd := newTemplateCmd(out) cmd.SetArgs(tt.args) err := cmd.Execute() - if err != nil { - t.Errorf("expected: %v, got %v", tt.expectValue, err) + + if tt.expectError != "" { + if err == nil { + t.Errorf("expected err: %s, but no error occurred", tt.expectError) + } + // non nil error, check if it contains the expected error + if strings.Contains(err.Error(), tt.expectError) { + // had the error we were looking for, this test case is + // done + return + } else { + t.Fatalf("expected err: %q, got: %q", tt.expectError, err) + } + } else if err != nil { + t.Errorf("expected no error, got %v", err) } // restore stdout w.Close() From 4ce35363d40fa6e8d8789121384d397748571e57 Mon Sep 17 00:00:00 2001 From: Jesse Weinstein Date: Fri, 13 Apr 2018 09:57:35 -0700 Subject: [PATCH 69/71] Document apiVersion field Added in https://github.com/kubernetes/helm/issues/1264 --- docs/charts.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/charts.md b/docs/charts.md index a19c1a477..a55038e3e 100644 --- a/docs/charts.md +++ b/docs/charts.md @@ -41,6 +41,7 @@ the listed file names. Other files will be left as they are. The `Chart.yaml` file is required for a chart. It contains the following fields: ```yaml +apiVersion: The chart API version, always "v1" (required) name: The name of the chart (required) version: A SemVer 2 version (required) kubeVersion: A SemVer range of compatible Kubernetes versions (optional) From 75682ed5844400e5dcd24ec7532c0fa454defd4d Mon Sep 17 00:00:00 2001 From: Arash Deshmeh Date: Sat, 14 Apr 2018 05:52:34 -0400 Subject: [PATCH 70/71] fix(helm): resolve linter's warning on template command unit tests Signed-off-by: Arash Deshmeh --- cmd/helm/template_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmd/helm/template_test.go b/cmd/helm/template_test.go index 68ed87cd2..cad42db16 100644 --- a/cmd/helm/template_test.go +++ b/cmd/helm/template_test.go @@ -160,9 +160,8 @@ func TestTemplateCmd(t *testing.T) { // had the error we were looking for, this test case is // done return - } else { - t.Fatalf("expected err: %q, got: %q", tt.expectError, err) } + t.Fatalf("expected err: %q, got: %q", tt.expectError, err) } else if err != nil { t.Errorf("expected no error, got %v", err) } From 7631b8a926d1cc533a94951b6a96c948081e78e7 Mon Sep 17 00:00:00 2001 From: Sergii Manannikov Date: Sat, 14 Apr 2018 15:51:01 +0200 Subject: [PATCH 71/71] Create PodSecurityPolicy before Pods and ServiceAccounts --- pkg/tiller/kind_sorter.go | 2 ++ pkg/tiller/kind_sorter_test.go | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/pkg/tiller/kind_sorter.go b/pkg/tiller/kind_sorter.go index f367e65c8..43726d53e 100644 --- a/pkg/tiller/kind_sorter.go +++ b/pkg/tiller/kind_sorter.go @@ -30,6 +30,7 @@ var InstallOrder SortOrder = []string{ "Namespace", "ResourceQuota", "LimitRange", + "PodSecurityPolicy", "Secret", "ConfigMap", "StorageClass", @@ -80,6 +81,7 @@ var UninstallOrder SortOrder = []string{ "StorageClass", "ConfigMap", "Secret", + "PodSecurityPolicy", "LimitRange", "ResourceQuota", "Namespace", diff --git a/pkg/tiller/kind_sorter_test.go b/pkg/tiller/kind_sorter_test.go index ef7296e89..8d01fac17 100644 --- a/pkg/tiller/kind_sorter_test.go +++ b/pkg/tiller/kind_sorter_test.go @@ -85,6 +85,10 @@ func TestKindSorter(t *testing.T) { Name: "o", Head: &util.SimpleHead{Kind: "Pod"}, }, + { + Name: "3", + Head: &util.SimpleHead{Kind: "PodSecurityPolicy"}, + }, { Name: "q", Head: &util.SimpleHead{Kind: "ReplicaSet"}, @@ -136,8 +140,8 @@ func TestKindSorter(t *testing.T) { order SortOrder expected string }{ - {"install", InstallOrder, "abcde1fgh2ijklmnopqrstuvw!"}, - {"uninstall", UninstallOrder, "wvmutsrqponlkji2hgf1edcba!"}, + {"install", InstallOrder, "abc3de1fgh2ijklmnopqrstuvw!"}, + {"uninstall", UninstallOrder, "wvmutsrqponlkji2hgf1ed3cba!"}, } { var buf bytes.Buffer t.Run(test.description, func(t *testing.T) {