From 865c46c014cdb7622f97ae287ee92fb8a280f3a9 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Thu, 7 Nov 2019 16:16:36 -0700 Subject: [PATCH 01/46] fix: stop discovery errors from halting chart rendering. (#6908) This blocks a particular error (caused by upstream discovery client), printing a warning instead of failing. It's not a great solution, but is a stop-gap until Client-Go gets fixed. Closes #6361 Signed-off-by: Matt Butcher --- pkg/action/action.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/pkg/action/action.go b/pkg/action/action.go index 48d6bf742..f74a25e41 100644 --- a/pkg/action/action.go +++ b/pkg/action/action.go @@ -109,9 +109,19 @@ func (c *Configuration) getCapabilities() (*chartutil.Capabilities, error) { if err != nil { return nil, errors.Wrap(err, "could not get server version from Kubernetes") } + // Issue #6361: + // Client-Go emits an error when an API service is registered but unimplemented. + // We trap that error here and print a warning. But since the discovery client continues + // building the API object, it is correctly populated with all valid APIs. + // See https://github.com/kubernetes/kubernetes/issues/72051#issuecomment-521157642 apiVersions, err := GetVersionSet(dc) if err != nil { - return nil, errors.Wrap(err, "could not get apiVersions from Kubernetes") + if discovery.IsGroupDiscoveryFailedError(err) { + c.Log("WARNING: The Kubernetes server has an orphaned API service. Server reports: %s", err) + c.Log("WARNING: To fix this, kubectl delete apiservice ") + } else { + return nil, errors.Wrap(err, "could not get apiVersions from Kubernetes") + } } c.Capabilities = &chartutil.Capabilities{ From aa6104e442ef8b574ae88efd8b5c41004437ac88 Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Fri, 8 Nov 2019 07:32:50 -0800 Subject: [PATCH 02/46] fix(tlsutil): accept only a CA certificate for validation Signed-off-by: Matthew Fisher --- internal/tlsutil/tls.go | 16 ++++++---- internal/tlsutil/tlsutil_test.go | 51 ++++++++++++++++++++++++++++++++ pkg/getter/httpgetter.go | 2 +- pkg/getter/httpgetter_test.go | 10 +++++++ 4 files changed, 72 insertions(+), 7 deletions(-) diff --git a/internal/tlsutil/tls.go b/internal/tlsutil/tls.go index dc123e1e5..ed7795dbe 100644 --- a/internal/tlsutil/tls.go +++ b/internal/tlsutil/tls.go @@ -26,13 +26,16 @@ import ( // 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}, + 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 { @@ -40,6 +43,7 @@ func NewClientTLS(certFile, keyFile, caFile string) (*tls.Config, error) { } config.RootCAs = cp } + return &config, nil } diff --git a/internal/tlsutil/tlsutil_test.go b/internal/tlsutil/tlsutil_test.go index c2a477272..a737226fd 100644 --- a/internal/tlsutil/tlsutil_test.go +++ b/internal/tlsutil/tlsutil_test.go @@ -81,3 +81,54 @@ func testfile(t *testing.T, file string) (path string) { } return path } + +func TestNewClientTLS(t *testing.T) { + certFile := testfile(t, testCertFile) + keyFile := testfile(t, testKeyFile) + caCertFile := testfile(t, testCaCertFile) + + cfg, err := NewClientTLS(certFile, keyFile, caCertFile) + if err != nil { + t.Error(err) + } + + if got := len(cfg.Certificates); got != 1 { + t.Fatalf("expecting 1 client certificates, got %d", got) + } + if cfg.InsecureSkipVerify { + t.Fatalf("insecure skip verify mistmatch, expecting false") + } + if cfg.RootCAs == nil { + t.Fatalf("mismatch tls RootCAs, expecting non-nil") + } + + cfg, err = NewClientTLS("", "", caCertFile) + if err != nil { + t.Error(err) + } + + if got := len(cfg.Certificates); got != 0 { + t.Fatalf("expecting 0 client certificates, got %d", got) + } + if cfg.InsecureSkipVerify { + t.Fatalf("insecure skip verify mistmatch, expecting false") + } + if cfg.RootCAs == nil { + t.Fatalf("mismatch tls RootCAs, expecting non-nil") + } + + cfg, err = NewClientTLS(certFile, keyFile, "") + if err != nil { + t.Error(err) + } + + if got := len(cfg.Certificates); got != 1 { + t.Fatalf("expecting 1 client certificates, got %d", got) + } + if cfg.InsecureSkipVerify { + t.Fatalf("insecure skip verify mistmatch, expecting false") + } + if cfg.RootCAs != nil { + t.Fatalf("mismatch tls RootCAs, expecting nil") + } +} diff --git a/pkg/getter/httpgetter.go b/pkg/getter/httpgetter.go index a36ab0321..89abfb1cf 100644 --- a/pkg/getter/httpgetter.go +++ b/pkg/getter/httpgetter.go @@ -89,7 +89,7 @@ func NewHTTPGetter(options ...Option) (Getter, error) { } func (g *HTTPGetter) httpClient() (*http.Client, error) { - if g.opts.certFile != "" && g.opts.keyFile != "" { + if (g.opts.certFile != "" && g.opts.keyFile != "") || g.opts.caFile != "" { tlsConf, err := tlsutil.NewClientTLS(g.opts.certFile, g.opts.keyFile, g.opts.caFile) if err != nil { return nil, errors.Wrap(err, "can't create TLS config for client") diff --git a/pkg/getter/httpgetter_test.go b/pkg/getter/httpgetter_test.go index 0c2c55ea3..b20085574 100644 --- a/pkg/getter/httpgetter_test.go +++ b/pkg/getter/httpgetter_test.go @@ -180,4 +180,14 @@ func TestDownloadTLS(t *testing.T) { if _, err := g.Get(u.String(), WithURL(u.String()), WithTLSClientConfig(pub, priv, ca)); err != nil { t.Error(err) } + + // test with only the CA file (see also #6635) + g, err = NewHTTPGetter() + if err != nil { + t.Fatal(err) + } + + if _, err := g.Get(u.String(), WithURL(u.String()), WithTLSClientConfig("", "", ca)); err != nil { + t.Error(err) + } } From 9ed2a28eded7678ccc26f2079fa815eefb489683 Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Fri, 8 Nov 2019 07:39:57 -0800 Subject: [PATCH 03/46] ref(tlsutil): remove ServerConfig dead code from Tiller days Signed-off-by: Matthew Fisher --- internal/tlsutil/cfg.go | 29 ++--------------------------- internal/tlsutil/tlsutil_test.go | 21 --------------------- 2 files changed, 2 insertions(+), 48 deletions(-) diff --git a/internal/tlsutil/cfg.go b/internal/tlsutil/cfg.go index f40258739..8b9d4329f 100644 --- a/internal/tlsutil/cfg.go +++ b/internal/tlsutil/cfg.go @@ -27,18 +27,14 @@ import ( // Options represents configurable options used to create client and server TLS configurations. type Options struct { CaCertFile string - // If either the KeyFile or CertFile is empty, ClientConfig() will not load them, - // preventing Helm from authenticating to Tiller. They are required to be non-empty - // when calling ServerConfig, otherwise an error is returned. + // If either the KeyFile or CertFile is empty, ClientConfig() will not load them. KeyFile string CertFile string // Client-only options InsecureSkipVerify bool - // Server-only options - ClientAuth tls.ClientAuthType } -// ClientConfig retusn a TLS configuration for use by a Helm client. +// ClientConfig returns a TLS configuration for use by a Helm client. func ClientConfig(opts Options) (cfg *tls.Config, err error) { var cert *tls.Certificate var pool *x509.CertPool @@ -60,24 +56,3 @@ func ClientConfig(opts Options) (cfg *tls.Config, err error) { cfg = &tls.Config{InsecureSkipVerify: opts.InsecureSkipVerify, Certificates: []tls.Certificate{*cert}, RootCAs: pool} return cfg, nil } - -// ServerConfig returns a TLS configuration for use by the Tiller server. -func ServerConfig(opts Options) (cfg *tls.Config, err error) { - var cert *tls.Certificate - var pool *x509.CertPool - - if cert, err = CertFromFilePair(opts.CertFile, opts.KeyFile); err != nil { - if os.IsNotExist(err) { - return nil, errors.Wrapf(err, "could not load x509 key pair (cert: %q, key: %q)", opts.CertFile, opts.KeyFile) - } - return nil, errors.Wrapf(err, "could not read x509 key pair (cert: %q, key: %q)", opts.CertFile, opts.KeyFile) - } - if opts.ClientAuth >= tls.VerifyClientCertIfGiven && opts.CaCertFile != "" { - if pool, err = CertPoolFromFile(opts.CaCertFile); err != nil { - return nil, err - } - } - - cfg = &tls.Config{MinVersion: tls.VersionTLS12, ClientAuth: opts.ClientAuth, Certificates: []tls.Certificate{*cert}, ClientCAs: pool} - return cfg, nil -} diff --git a/internal/tlsutil/tlsutil_test.go b/internal/tlsutil/tlsutil_test.go index c2a477272..811d64aa0 100644 --- a/internal/tlsutil/tlsutil_test.go +++ b/internal/tlsutil/tlsutil_test.go @@ -17,7 +17,6 @@ limitations under the License. package tlsutil import ( - "crypto/tls" "path/filepath" "testing" ) @@ -54,26 +53,6 @@ func TestClientConfig(t *testing.T) { } } -func TestServerConfig(t *testing.T) { - opts := Options{ - CaCertFile: testfile(t, testCaCertFile), - CertFile: testfile(t, testCertFile), - KeyFile: testfile(t, testKeyFile), - ClientAuth: tls.RequireAndVerifyClientCert, - } - - cfg, err := ServerConfig(opts) - if err != nil { - t.Fatalf("error building tls server config: %v", err) - } - if got := cfg.MinVersion; got != tls.VersionTLS12 { - t.Errorf("expecting TLS version 1.2, got %d", got) - } - if got := cfg.ClientCAs; got == nil { - t.Errorf("expecting non-nil CA pool") - } -} - func testfile(t *testing.T, file string) (path string) { var err error if path, err = filepath.Abs(filepath.Join(tlsTestDir, file)); err != nil { From 30e8ed2f3df9e776b5a5937d92e8ff45c54f984f Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Fri, 8 Nov 2019 17:22:47 -0500 Subject: [PATCH 04/46] fix(cli): helm list was ignoring some errors If the cluster is not reachable, helm list would not report that error. Signed-off-by: Marc Khouzam --- cmd/helm/list.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmd/helm/list.go b/cmd/helm/list.go index 32501530d..83edfe156 100644 --- a/cmd/helm/list.go +++ b/cmd/helm/list.go @@ -77,12 +77,15 @@ func newListCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { client.SetStateMask() results, err := client.Run() + if err != nil { + return err + } if client.Short { for _, res := range results { fmt.Fprintln(out, res.Name) } - return err + return nil } return outfmt.Write(out, newReleaseListWriter(results)) From 693bce93a073d1086f86cb119f96b306bbb2bc80 Mon Sep 17 00:00:00 2001 From: sayboras Date: Sat, 9 Nov 2019 19:56:45 +1100 Subject: [PATCH 05/46] Used timeout instead of deadline Signed-off-by: sayboras --- .golangci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.golangci.yml b/.golangci.yml index 601d98661..2c3b6234d 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,5 +1,5 @@ run: - deadline: 2m + timeout: 2m linters: disable-all: true From 4d1c11f05bf732bf914d5529493fc77d5134d1c9 Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Tue, 12 Nov 2019 09:36:08 -0800 Subject: [PATCH 06/46] fix(ci): pin golangci-lint to v1.21.0 Signed-off-by: Matthew Fisher --- .circleci/bootstrap.sh | 20 ++++++++++++++++++++ .circleci/config.yml | 4 ++++ Makefile | 8 ++------ 3 files changed, 26 insertions(+), 6 deletions(-) create mode 100755 .circleci/bootstrap.sh diff --git a/.circleci/bootstrap.sh b/.circleci/bootstrap.sh new file mode 100755 index 000000000..79d194077 --- /dev/null +++ b/.circleci/bootstrap.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +# Copyright The Helm Authors. +# +# 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. +set -euo pipefail + +curl -sSL https://github.com/golangci/golangci-lint/releases/download/v$GOLANGCI_LINT_VERSION/golangci-lint-$GOLANGCI_LINT_VERSION-linux-amd64.tar.gz | tar xz +sudo mv golangci-lint-$GOLANGCI_LINT_VERSION-linux-amd64/golangci-lint /usr/local/bin/golangci-lint +rm -rf golangci-lint-$GOLANGCI_LINT_VERSION-linux-amd64 diff --git a/.circleci/config.yml b/.circleci/config.yml index db7182adb..ef19b8ee7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,9 +9,13 @@ jobs: environment: GOCACHE: "/tmp/go/cache" + GOLANGCI_LINT_VERSION: "1.21.0" steps: - checkout + - run: + name: install test dependencies + command: .circleci/bootstrap.sh - run: name: test style command: make test-style diff --git a/Makefile b/Makefile index b52464ba9..e48a20fd5 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,6 @@ GOPATH = $(shell go env GOPATH) DEP = $(GOPATH)/bin/dep GOX = $(GOPATH)/bin/gox GOIMPORTS = $(GOPATH)/bin/goimports -GOLANGCI_LINT = $(GOPATH)/bin/golangci-lint ACCEPTANCE_DIR:=$(GOPATH)/src/helm.sh/acceptance-testing # To specify the subset of acceptance tests to run. '.' means all tests @@ -81,8 +80,8 @@ test-coverage: @ ./scripts/coverage.sh .PHONY: test-style -test-style: $(GOLANGCI_LINT) - GO111MODULE=on $(GOLANGCI_LINT) run +test-style: + GO111MODULE=on golangci-lint run @scripts/validate-license.sh .PHONY: test-acceptance @@ -118,9 +117,6 @@ format: $(GOIMPORTS) $(GOX): (cd /; GO111MODULE=on go get -u github.com/mitchellh/gox) -$(GOLANGCI_LINT): - (cd /; GO111MODULE=on go get -u github.com/golangci/golangci-lint/cmd/golangci-lint) - $(GOIMPORTS): (cd /; GO111MODULE=on go get -u golang.org/x/tools/cmd/goimports) From b30467c2e52ffebd0b4708ca318d34251ab5a692 Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Tue, 12 Nov 2019 08:34:58 -0800 Subject: [PATCH 07/46] fix(strvals): port #3912, #4142, #4682, and #5151 to Helm 3 Signed-off-by: Matthew Fisher --- pkg/strvals/parser.go | 30 ++++++++++-- pkg/strvals/parser_test.go | 95 +++++++++++++++++++++++++++++++++++++- 2 files changed, 119 insertions(+), 6 deletions(-) diff --git a/pkg/strvals/parser.go b/pkg/strvals/parser.go index eaadbfb07..03adbd3cb 100644 --- a/pkg/strvals/parser.go +++ b/pkg/strvals/parser.go @@ -84,7 +84,7 @@ func ParseFile(s string, reader RunesValueReader) (map[string]interface{}, error return vals, err } -// ParseIntoString parses a strvals line nad merges the result into dest. +// ParseIntoString parses a strvals line and merges the result into dest. // // This method always returns a string as the value. func ParseIntoString(s string, dest map[string]interface{}) error { @@ -108,6 +108,9 @@ type RunesValueReader func([]rune) (interface{}, error) // parser is a simple parser that takes a strvals line and parses it into a // map representation. +// +// where sc is the source of the original data being parsed +// where data is the final parsed data from the parses with correct types type parser struct { sc *bytes.Buffer data map[string]interface{} @@ -285,7 +288,13 @@ func (t *parser) listItem(list []interface{}, i int) ([]interface{}, error) { // We have a nested object. Send to t.key inner := map[string]interface{}{} if len(list) > i { - inner = list[i].(map[string]interface{}) + var ok bool + inner, ok = list[i].(map[string]interface{}) + if !ok { + // We have indices out of order. Initialize empty value. + list[i] = map[string]interface{}{} + inner = list[i].(map[string]interface{}) + } } // Recurse @@ -367,6 +376,11 @@ func inMap(k rune, m map[rune]bool) bool { func typedVal(v []rune, st bool) interface{} { val := string(v) + + if st { + return val + } + if strings.EqualFold(val, "true") { return true } @@ -375,8 +389,16 @@ func typedVal(v []rune, st bool) interface{} { return false } - // If this value does not start with zero, and not returnString, try parsing it to an int - if !st && len(val) != 0 && val[0] != '0' { + if strings.EqualFold(val, "null") { + return nil + } + + if strings.EqualFold(val, "0") { + return int64(0) + } + + // If this value does not start with zero, try parsing it to an int + if len(val) != 0 && val[0] != '0' { if iv, err := strconv.ParseInt(val, 10, 64); err == nil { return iv } diff --git a/pkg/strvals/parser_test.go b/pkg/strvals/parser_test.go index ff8d58587..7f38efa52 100644 --- a/pkg/strvals/parser_test.go +++ b/pkg/strvals/parser_test.go @@ -75,12 +75,32 @@ func TestParseSet(t *testing.T) { expect: map[string]interface{}{"long_int_string": "1234567890"}, err: false, }, + { + str: "boolean=true", + expect: map[string]interface{}{"boolean": "true"}, + err: false, + }, + { + str: "is_null=null", + expect: map[string]interface{}{"is_null": "null"}, + err: false, + }, + { + str: "zero=0", + expect: map[string]interface{}{"zero": "0"}, + err: false, + }, } tests := []struct { str string expect map[string]interface{} err bool }{ + { + "name1=null,f=false,t=true", + map[string]interface{}{"name1": nil, "f": false, "t": true}, + false, + }, { "name1=value1", map[string]interface{}{"name1": "value1"}, @@ -108,10 +128,23 @@ func TestParseSet(t *testing.T) { str: "leading_zeros=00009", expect: map[string]interface{}{"leading_zeros": "00009"}, }, + { + str: "zero_int=0", + expect: map[string]interface{}{"zero_int": 0}, + }, { str: "long_int=1234567890", expect: map[string]interface{}{"long_int": 1234567890}, }, + { + str: "boolean=true", + expect: map[string]interface{}{"boolean": true}, + }, + { + str: "is_null=null", + expect: map[string]interface{}{"is_null": nil}, + err: false, + }, { str: "name1,name2=", err: true, @@ -270,6 +303,30 @@ func TestParseSet(t *testing.T) { str: "nested[1][1]=1", expect: map[string]interface{}{"nested": []interface{}{nil, []interface{}{nil, 1}}}, }, + { + str: "name1.name2[0].foo=bar,name1.name2[1].foo=bar", + expect: map[string]interface{}{ + "name1": map[string]interface{}{ + "name2": []map[string]interface{}{{"foo": "bar"}, {"foo": "bar"}}, + }, + }, + }, + { + str: "name1.name2[1].foo=bar,name1.name2[0].foo=bar", + expect: map[string]interface{}{ + "name1": map[string]interface{}{ + "name2": []map[string]interface{}{{"foo": "bar"}, {"foo": "bar"}}, + }, + }, + }, + { + str: "name1.name2[1].foo=bar", + expect: map[string]interface{}{ + "name1": map[string]interface{}{ + "name2": []map[string]interface{}{nil, {"foo": "bar"}}, + }, + }, + }, } for _, tt := range tests { @@ -331,12 +388,13 @@ func TestParseInto(t *testing.T) { "inner2": "value2", }, } - input := "outer.inner1=value1,outer.inner3=value3" + input := "outer.inner1=value1,outer.inner3=value3,outer.inner4=4" expect := map[string]interface{}{ "outer": map[string]interface{}{ "inner1": "value1", "inner2": "value2", "inner3": "value3", + "inner4": 4, }, } @@ -357,6 +415,39 @@ func TestParseInto(t *testing.T) { t.Errorf("%s: Expected:\n%s\nGot:\n%s", input, y1, y2) } } +func TestParseIntoString(t *testing.T) { + got := map[string]interface{}{ + "outer": map[string]interface{}{ + "inner1": "overwrite", + "inner2": "value2", + }, + } + input := "outer.inner1=1,outer.inner3=3" + expect := map[string]interface{}{ + "outer": map[string]interface{}{ + "inner1": "1", + "inner2": "value2", + "inner3": "3", + }, + } + + if err := ParseIntoString(input, got); err != nil { + t.Fatal(err) + } + + y1, err := yaml.Marshal(expect) + if err != nil { + t.Fatal(err) + } + y2, err := yaml.Marshal(got) + if err != nil { + t.Fatalf("Error serializing parsed value: %s", err) + } + + if string(y1) != string(y2) { + t.Errorf("%s: Expected:\n%s\nGot:\n%s", input, y1, y2) + } +} func TestParseIntoFile(t *testing.T) { got := map[string]interface{}{} @@ -367,7 +458,7 @@ func TestParseIntoFile(t *testing.T) { rs2v := func(rs []rune) (interface{}, error) { v := string(rs) if v != "path1" { - t.Errorf("%s: RunesValueReader: Expected value path1, got %s", input, v) + t.Errorf("%s: runesToVal: Expected value path1, got %s", input, v) return "", nil } return "value1", nil From 17553db485f6165c15aa17c6dfcc7589edb025ee Mon Sep 17 00:00:00 2001 From: Hang Park Date: Wed, 13 Nov 2019 02:33:27 +0900 Subject: [PATCH 08/46] fix(pkg/downloader): add failing test for build with repo alias Signed-off-by: Hang Park --- pkg/downloader/manager_test.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/pkg/downloader/manager_test.go b/pkg/downloader/manager_test.go index 0c5c08615..d6cae4e51 100644 --- a/pkg/downloader/manager_test.go +++ b/pkg/downloader/manager_test.go @@ -181,7 +181,8 @@ func TestGetRepoNames(t *testing.T) { } } -// This function is the skeleton test code of failing tests for #6416 and bugs due to #5874. +// This function is the skeleton test code of failing tests for #6416 and #6871 and bugs due to #5874. +// // This function is used by below tests that ensures success of build operation // with optional fields, alias, condition, tags, and even with ranged version. // Parent chart includes local-subchart 0.1.0 subchart from a fake repository, by default. @@ -283,3 +284,11 @@ func TestBuild_WithTags(t *testing.T) { Tags: []string{"tag1", "tag2"}, }) } + +// Failing test for #6871 +func TestBuild_WithRepositoryAlias(t *testing.T) { + // Dependency repository is aliased in Chart.yaml + checkBuildWithOptionalFields(t, "with-repository-alias", chart.Dependency{ + Repository: "@test", + }) +} From 0987c6f7b91bffc360a8c604b9dfb87b845cc919 Mon Sep 17 00:00:00 2001 From: Hang Park Date: Wed, 13 Nov 2019 02:35:48 +0900 Subject: [PATCH 09/46] fix(pkg/downloader): resolve repo alias before checking digests on build `Update()` gets repo names before resolving a lock file by calling `resolveRepoNames(req)`. But that method changes aliased repo URLs into the actual URLs. That makes digests from `helm update` and `helm build` be different for each other. To make them in sync, setting actual (resolved) repo URLs into the loaded chart during `helm build` is necessary. Thus, this commit adds an extra step in the `Build()` implementation. For comments, this commit also changes the name of `getRepoNames()` into `resolveRepoNames()` to avoid misunderstanding since getters are expected to not mutate their input data in general. Signed-off-by: Hang Park --- pkg/downloader/manager.go | 12 +++++++++--- pkg/downloader/manager_test.go | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/pkg/downloader/manager.go b/pkg/downloader/manager.go index 407e7ffe4..4e9d691d8 100644 --- a/pkg/downloader/manager.go +++ b/pkg/downloader/manager.go @@ -79,7 +79,12 @@ func (m *Manager) Build() error { return m.Update() } + // Check that all of the repos we're dependent on actually exist. req := c.Metadata.Dependencies + if _, err := m.resolveRepoNames(req); err != nil { + return err + } + if sum, err := resolver.HashReq(req, lock.Dependencies); err != nil || sum != lock.Digest { return errors.New("Chart.lock is out of sync with Chart.yaml") } @@ -120,7 +125,7 @@ func (m *Manager) Update() error { // Check that all of the repos we're dependent on actually exist and // the repo index names. - repoNames, err := m.getRepoNames(req) + repoNames, err := m.resolveRepoNames(req) if err != nil { return err } @@ -372,8 +377,9 @@ Loop: return nil } -// getRepoNames returns the repo names of the referenced deps which can be used to fetch the cahced index file. -func (m *Manager) getRepoNames(deps []*chart.Dependency) (map[string]string, error) { +// resolveRepoNames returns the repo names of the referenced deps which can be used to fetch the cached index file +// and replaces aliased repository URLs into resolved URLs in dependencies. +func (m *Manager) resolveRepoNames(deps []*chart.Dependency) (map[string]string, error) { rf, err := loadRepoConfig(m.RepositoryConfig) if err != nil { if os.IsNotExist(err) { diff --git a/pkg/downloader/manager_test.go b/pkg/downloader/manager_test.go index d6cae4e51..6e32b3e85 100644 --- a/pkg/downloader/manager_test.go +++ b/pkg/downloader/manager_test.go @@ -161,7 +161,7 @@ func TestGetRepoNames(t *testing.T) { } for _, tt := range tests { - l, err := m.getRepoNames(tt.req) + l, err := m.resolveRepoNames(tt.req) if err != nil { if tt.err { continue From e91feed1a3c34ec15ff2bff4d07ee5e275a6d7b4 Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Tue, 12 Nov 2019 13:12:45 -0800 Subject: [PATCH 10/46] fix(install): log the error when recording the release Signed-off-by: Matthew Fisher --- pkg/action/install.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/action/install.go b/pkg/action/install.go index 8f1b5528b..65c10f636 100644 --- a/pkg/action/install.go +++ b/pkg/action/install.go @@ -301,7 +301,9 @@ func (i *Install) Run(chrt *chart.Chart, vals map[string]interface{}) (*release. // // One possible strategy would be to do a timed retry to see if we can get // this stored in the future. - i.recordRelease(rel) + if err := i.recordRelease(rel); err != nil { + i.cfg.Log("failed to record the release: %s", err) + } return rel, nil } From a9b178758c6c3c3f36f6bbd7a1b4359efae27366 Mon Sep 17 00:00:00 2001 From: flynnduism Date: Tue, 12 Nov 2019 14:27:30 -0800 Subject: [PATCH 11/46] fix(reame): update links to docs Signed-off-by: flynnduism --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9af358e10..fbabf7e44 100644 --- a/README.md +++ b/README.md @@ -44,12 +44,12 @@ If you want to use a package manager: To rapidly get Helm up and running, start with the [Quick Start Guide](https://docs.helm.sh/using_helm/#quickstart-guide). -See the [installation guide](https://docs.helm.sh/using_helm/#installing-helm) for more options, +See the [installation guide](https://helm.sh/docs/intro/install/) for more options, including installing pre-releases. ## Docs -Get started with the [Quick Start guide](https://docs.helm.sh/using_helm/#quickstart-guide) or plunge into the [complete documentation](https://docs.helm.sh) +Get started with the [Quick Start guide](https://helm.sh/docs/intro/quickstart/) or plunge into the [complete documentation](https://helm.sh/docs) ## Roadmap From 06a5eb2272f26fcdb2bd5abf5abe45d7078b5e50 Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Tue, 12 Nov 2019 18:11:56 -0800 Subject: [PATCH 12/46] fix(get): install Helm v2.16.1 This is a temporary fix. This prevents the get script from installing Helm 3 as soon as it's released, potentially causing disruption to users that were expecting a Helm 2 release. A `get-helm-3` script is also supplied, which is identical to the previous `get` script. That way, Helm 3 users also have an equivalent way to install Helm 3. Signed-off-by: Matthew Fisher --- scripts/get | 6 +- scripts/get-helm-3 | 245 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 249 insertions(+), 2 deletions(-) create mode 100755 scripts/get-helm-3 diff --git a/scripts/get b/scripts/get index 3f645f807..a264f8f76 100755 --- a/scripts/get +++ b/scripts/get @@ -187,7 +187,7 @@ testVersion() { help () { echo "Accepted cli arguments are:" echo -e "\t[--help|-h ] ->> prints this help" - echo -e "\t[--version|-v ] . When not defined it defaults to latest" + echo -e "\t[--version|-v ]" echo -e "\te.g. --version v2.4.0 or -v latest" echo -e "\t[--no-sudo] ->> install without sudo" } @@ -213,7 +213,9 @@ while [[ $# -gt 0 ]]; do '--version'|-v) shift if [[ $# -ne 0 ]]; then - export DESIRED_VERSION="${1}" + # FIXME(bacongobbler): hard code the desired version for the time being. + # A better fix would be to filter for Helm 2 release pages. + export DESIRED_VERSION="${1:="v2.16.1"}" else echo -e "Please provide the desired version. e.g. --version v2.4.0 or -v latest" exit 0 diff --git a/scripts/get-helm-3 b/scripts/get-helm-3 new file mode 100755 index 000000000..3f645f807 --- /dev/null +++ b/scripts/get-helm-3 @@ -0,0 +1,245 @@ +#!/usr/bin/env bash + +# Copyright The Helm Authors. +# +# 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. + +# The install script is based off of the MIT-licensed script from glide, +# the package manager for Go: https://github.com/Masterminds/glide.sh/blob/master/get + +PROJECT_NAME="helm" +TILLER_NAME="tiller" + +: ${USE_SUDO:="true"} +: ${HELM_INSTALL_DIR:="/usr/local/bin"} + +# initArch discovers the architecture for this system. +initArch() { + ARCH=$(uname -m) + case $ARCH in + armv5*) ARCH="armv5";; + armv6*) ARCH="armv6";; + armv7*) ARCH="arm";; + aarch64) ARCH="arm64";; + x86) ARCH="386";; + x86_64) ARCH="amd64";; + i686) ARCH="386";; + i386) ARCH="386";; + esac +} + +# initOS discovers the operating system for this system. +initOS() { + OS=$(echo `uname`|tr '[:upper:]' '[:lower:]') + + case "$OS" in + # Minimalist GNU for Windows + mingw*) OS='windows';; + esac +} + +# runs the given command as root (detects if we are root already) +runAsRoot() { + local CMD="$*" + + if [ $EUID -ne 0 -a $USE_SUDO = "true" ]; then + CMD="sudo $CMD" + fi + + $CMD +} + +# verifySupported checks that the os/arch combination is supported for +# binary builds. +verifySupported() { + local supported="darwin-386\ndarwin-amd64\nlinux-386\nlinux-amd64\nlinux-arm\nlinux-arm64\nlinux-ppc64le\nwindows-386\nwindows-amd64" + if ! echo "${supported}" | grep -q "${OS}-${ARCH}"; then + echo "No prebuilt binary for ${OS}-${ARCH}." + echo "To build from source, go to https://github.com/helm/helm" + exit 1 + fi + + if ! type "curl" > /dev/null && ! type "wget" > /dev/null; then + echo "Either curl or wget is required" + exit 1 + fi +} + +# checkDesiredVersion checks if the desired version is available. +checkDesiredVersion() { + if [ "x$DESIRED_VERSION" == "x" ]; then + # Get tag from release URL + local latest_release_url="https://github.com/helm/helm/releases/latest" + if type "curl" > /dev/null; then + TAG=$(curl -Ls -o /dev/null -w %{url_effective} $latest_release_url | grep -oE "[^/]+$" ) + elif type "wget" > /dev/null; then + TAG=$(wget $latest_release_url --server-response -O /dev/null 2>&1 | awk '/^ Location: /{DEST=$2} END{ print DEST}' | grep -oE "[^/]+$") + fi + else + TAG=$DESIRED_VERSION + fi +} + +# checkHelmInstalledVersion checks which version of helm is installed and +# if it needs to be changed. +checkHelmInstalledVersion() { + if [[ -f "${HELM_INSTALL_DIR}/${PROJECT_NAME}" ]]; then + local version=$("${HELM_INSTALL_DIR}/${PROJECT_NAME}" version -c | grep '^Client' | cut -d'"' -f2) + if [[ "$version" == "$TAG" ]]; then + echo "Helm ${version} is already ${DESIRED_VERSION:-latest}" + return 0 + else + echo "Helm ${TAG} is available. Changing from version ${version}." + return 1 + fi + else + return 1 + fi +} + +# downloadFile downloads the latest binary package and also the checksum +# for that binary. +downloadFile() { + HELM_DIST="helm-$TAG-$OS-$ARCH.tar.gz" + DOWNLOAD_URL="https://get.helm.sh/$HELM_DIST" + CHECKSUM_URL="$DOWNLOAD_URL.sha256" + HELM_TMP_ROOT="$(mktemp -dt helm-installer-XXXXXX)" + HELM_TMP_FILE="$HELM_TMP_ROOT/$HELM_DIST" + HELM_SUM_FILE="$HELM_TMP_ROOT/$HELM_DIST.sha256" + echo "Downloading $DOWNLOAD_URL" + if type "curl" > /dev/null; then + curl -SsL "$CHECKSUM_URL" -o "$HELM_SUM_FILE" + elif type "wget" > /dev/null; then + wget -q -O "$HELM_SUM_FILE" "$CHECKSUM_URL" + fi + if type "curl" > /dev/null; then + curl -SsL "$DOWNLOAD_URL" -o "$HELM_TMP_FILE" + elif type "wget" > /dev/null; then + wget -q -O "$HELM_TMP_FILE" "$DOWNLOAD_URL" + fi +} + +# installFile verifies the SHA256 for the file, then unpacks and +# installs it. +installFile() { + HELM_TMP="$HELM_TMP_ROOT/$PROJECT_NAME" + local sum=$(openssl sha1 -sha256 ${HELM_TMP_FILE} | awk '{print $2}') + local expected_sum=$(cat ${HELM_SUM_FILE}) + if [ "$sum" != "$expected_sum" ]; then + echo "SHA sum of ${HELM_TMP_FILE} does not match. Aborting." + exit 1 + fi + + mkdir -p "$HELM_TMP" + tar xf "$HELM_TMP_FILE" -C "$HELM_TMP" + HELM_TMP_BIN="$HELM_TMP/$OS-$ARCH/$PROJECT_NAME" + TILLER_TMP_BIN="$HELM_TMP/$OS-$ARCH/$TILLER_NAME" + echo "Preparing to install $PROJECT_NAME and $TILLER_NAME into ${HELM_INSTALL_DIR}" + runAsRoot cp "$HELM_TMP_BIN" "$HELM_INSTALL_DIR" + echo "$PROJECT_NAME installed into $HELM_INSTALL_DIR/$PROJECT_NAME" + if [ -x "$TILLER_TMP_BIN" ]; then + runAsRoot cp "$TILLER_TMP_BIN" "$HELM_INSTALL_DIR" + echo "$TILLER_NAME installed into $HELM_INSTALL_DIR/$TILLER_NAME" + else + echo "info: $TILLER_NAME binary was not found in this release; skipping $TILLER_NAME installation" + fi +} + +# fail_trap is executed if an error occurs. +fail_trap() { + result=$? + if [ "$result" != "0" ]; then + if [[ -n "$INPUT_ARGUMENTS" ]]; then + echo "Failed to install $PROJECT_NAME with the arguments provided: $INPUT_ARGUMENTS" + help + else + echo "Failed to install $PROJECT_NAME" + fi + echo -e "\tFor support, go to https://github.com/helm/helm." + fi + cleanup + exit $result +} + +# testVersion tests the installed client to make sure it is working. +testVersion() { + set +e + HELM="$(which $PROJECT_NAME)" + if [ "$?" = "1" ]; then + echo "$PROJECT_NAME not found. Is $HELM_INSTALL_DIR on your "'$PATH?' + exit 1 + fi + set -e + echo "Run '$PROJECT_NAME init' to configure $PROJECT_NAME." +} + +# help provides possible cli installation arguments +help () { + echo "Accepted cli arguments are:" + echo -e "\t[--help|-h ] ->> prints this help" + echo -e "\t[--version|-v ] . When not defined it defaults to latest" + echo -e "\te.g. --version v2.4.0 or -v latest" + echo -e "\t[--no-sudo] ->> install without sudo" +} + +# cleanup temporary files to avoid https://github.com/helm/helm/issues/2977 +cleanup() { + if [[ -d "${HELM_TMP_ROOT:-}" ]]; then + rm -rf "$HELM_TMP_ROOT" + fi +} + +# Execution + +#Stop execution on any error +trap "fail_trap" EXIT +set -e + +# Parsing input arguments (if any) +export INPUT_ARGUMENTS="${@}" +set -u +while [[ $# -gt 0 ]]; do + case $1 in + '--version'|-v) + shift + if [[ $# -ne 0 ]]; then + export DESIRED_VERSION="${1}" + else + echo -e "Please provide the desired version. e.g. --version v2.4.0 or -v latest" + exit 0 + fi + ;; + '--no-sudo') + USE_SUDO="false" + ;; + '--help'|-h) + help + exit 0 + ;; + *) exit 1 + ;; + esac + shift +done +set +u + +initArch +initOS +verifySupported +checkDesiredVersion +if ! checkHelmInstalledVersion; then + downloadFile + installFile +fi +testVersion +cleanup From ecb55c1de7bcc0063b2cf956a0e2172fcd75bc59 Mon Sep 17 00:00:00 2001 From: Taylor Thomas Date: Tue, 12 Nov 2019 19:41:01 -0700 Subject: [PATCH 13/46] fix(wait): Adds support for waiting on v1 apiextensions for CRDs This was a missed update when we updated the k8s libraries. I validated that this works for CRD installs with v1beta1 and v1 Signed-off-by: Taylor Thomas --- pkg/action/install.go | 2 +- pkg/kube/wait.go | 37 ++++++++++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/pkg/action/install.go b/pkg/action/install.go index 8f1b5528b..4019dbc54 100644 --- a/pkg/action/install.go +++ b/pkg/action/install.go @@ -126,7 +126,7 @@ func (i *Install) installCRDs(crds []*chart.File) error { i.cfg.Log("CRD %s is already present. Skipping.", crdName) continue } - return errors.Wrapf(err, "failed to instal CRD %s", obj.Name) + return errors.Wrapf(err, "failed to install CRD %s", obj.Name) } totalItems = append(totalItems, res...) } diff --git a/pkg/kube/wait.go b/pkg/kube/wait.go index 7198917c4..f0005a61e 100644 --- a/pkg/kube/wait.go +++ b/pkg/kube/wait.go @@ -27,6 +27,7 @@ import ( batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" extensionsv1beta1 "k8s.io/api/extensions/v1beta1" + apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" @@ -114,6 +115,17 @@ func (w *waiter) waitForResources(created ResourceList) error { if err := scheme.Scheme.Convert(v.Object, crd, nil); err != nil { return false, err } + if !w.crdBetaReady(*crd) { + return false, nil + } + case *apiextv1.CustomResourceDefinition: + if err := v.Get(); err != nil { + return false, err + } + crd := &apiextv1.CustomResourceDefinition{} + if err := scheme.Scheme.Convert(v.Object, crd, nil); err != nil { + return false, err + } if !w.crdReady(*crd) { return false, nil } @@ -229,7 +241,10 @@ func (w *waiter) daemonSetReady(ds *appsv1.DaemonSet) bool { return true } -func (w *waiter) crdReady(crd apiextv1beta1.CustomResourceDefinition) bool { +// Because the v1 extensions API is not available on all supported k8s versions +// yet and because Go doesn't support generics, we need to have a duplicate +// function to support the v1beta1 types +func (w *waiter) crdBetaReady(crd apiextv1beta1.CustomResourceDefinition) bool { for _, cond := range crd.Status.Conditions { switch cond.Type { case apiextv1beta1.Established: @@ -249,6 +264,26 @@ func (w *waiter) crdReady(crd apiextv1beta1.CustomResourceDefinition) bool { return false } +func (w *waiter) crdReady(crd apiextv1.CustomResourceDefinition) bool { + for _, cond := range crd.Status.Conditions { + switch cond.Type { + case apiextv1.Established: + if cond.Status == apiextv1.ConditionTrue { + return true + } + case apiextv1.NamesAccepted: + if cond.Status == apiextv1.ConditionFalse { + // This indicates a naming conflict, but it's probably not the + // job of this function to fail because of that. Instead, + // we treat it as a success, since the process should be able to + // continue. + return true + } + } + } + return false +} + func (w *waiter) statefulSetReady(sts *appsv1.StatefulSet) bool { // If the update strategy is not a rolling update, there will be nothing to wait for if sts.Spec.UpdateStrategy.Type != appsv1.RollingUpdateStatefulSetStrategyType { From 3e77ca22c71e182860b0ef9508d229e25d8f4140 Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Tue, 12 Nov 2019 19:56:34 -0800 Subject: [PATCH 14/46] fix(get): hard code DESIRED_VERSION when unset Signed-off-by: Matthew Fisher --- scripts/get | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/scripts/get b/scripts/get index a264f8f76..711635ee3 100755 --- a/scripts/get +++ b/scripts/get @@ -78,13 +78,16 @@ verifySupported() { # checkDesiredVersion checks if the desired version is available. checkDesiredVersion() { if [ "x$DESIRED_VERSION" == "x" ]; then + # FIXME(bacongobbler): hard code the desired version for the time being. + # A better fix would be to filter for Helm 2 release pages. + TAG="v2.16.1" # Get tag from release URL - local latest_release_url="https://github.com/helm/helm/releases/latest" - if type "curl" > /dev/null; then - TAG=$(curl -Ls -o /dev/null -w %{url_effective} $latest_release_url | grep -oE "[^/]+$" ) - elif type "wget" > /dev/null; then - TAG=$(wget $latest_release_url --server-response -O /dev/null 2>&1 | awk '/^ Location: /{DEST=$2} END{ print DEST}' | grep -oE "[^/]+$") - fi + # local latest_release_url="https://github.com/helm/helm/releases/latest" + # if type "curl" > /dev/null; then + # TAG=$(curl -Ls -o /dev/null -w %{url_effective} $latest_release_url | grep -oE "[^/]+$" ) + # elif type "wget" > /dev/null; then + # TAG=$(wget $latest_release_url --server-response -O /dev/null 2>&1 | awk '/^ Location: /{DEST=$2} END{ print DEST}' | grep -oE "[^/]+$") + # fi else TAG=$DESIRED_VERSION fi @@ -213,9 +216,7 @@ while [[ $# -gt 0 ]]; do '--version'|-v) shift if [[ $# -ne 0 ]]; then - # FIXME(bacongobbler): hard code the desired version for the time being. - # A better fix would be to filter for Helm 2 release pages. - export DESIRED_VERSION="${1:="v2.16.1"}" + export DESIRED_VERSION="${1}" else echo -e "Please provide the desired version. e.g. --version v2.4.0 or -v latest" exit 0 From c9da1eaae780488ba34c7bb66c5bff518c6c06ee Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Tue, 12 Nov 2019 20:06:09 -0800 Subject: [PATCH 15/46] fix(get-helm-3): remove tiller checks, fixup version check Signed-off-by: Matthew Fisher --- scripts/get-helm-3 | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/scripts/get-helm-3 b/scripts/get-helm-3 index 3f645f807..c1655a68e 100755 --- a/scripts/get-helm-3 +++ b/scripts/get-helm-3 @@ -18,7 +18,6 @@ # the package manager for Go: https://github.com/Masterminds/glide.sh/blob/master/get PROJECT_NAME="helm" -TILLER_NAME="tiller" : ${USE_SUDO:="true"} : ${HELM_INSTALL_DIR:="/usr/local/bin"} @@ -94,7 +93,7 @@ checkDesiredVersion() { # if it needs to be changed. checkHelmInstalledVersion() { if [[ -f "${HELM_INSTALL_DIR}/${PROJECT_NAME}" ]]; then - local version=$("${HELM_INSTALL_DIR}/${PROJECT_NAME}" version -c | grep '^Client' | cut -d'"' -f2) + local version=$("${HELM_INSTALL_DIR}/${PROJECT_NAME}" version --template="{{ .Version }}") if [[ "$version" == "$TAG" ]]; then echo "Helm ${version} is already ${DESIRED_VERSION:-latest}" return 0 @@ -143,16 +142,9 @@ installFile() { mkdir -p "$HELM_TMP" tar xf "$HELM_TMP_FILE" -C "$HELM_TMP" HELM_TMP_BIN="$HELM_TMP/$OS-$ARCH/$PROJECT_NAME" - TILLER_TMP_BIN="$HELM_TMP/$OS-$ARCH/$TILLER_NAME" - echo "Preparing to install $PROJECT_NAME and $TILLER_NAME into ${HELM_INSTALL_DIR}" + echo "Preparing to install $PROJECT_NAME into ${HELM_INSTALL_DIR}" runAsRoot cp "$HELM_TMP_BIN" "$HELM_INSTALL_DIR" echo "$PROJECT_NAME installed into $HELM_INSTALL_DIR/$PROJECT_NAME" - if [ -x "$TILLER_TMP_BIN" ]; then - runAsRoot cp "$TILLER_TMP_BIN" "$HELM_INSTALL_DIR" - echo "$TILLER_NAME installed into $HELM_INSTALL_DIR/$TILLER_NAME" - else - echo "info: $TILLER_NAME binary was not found in this release; skipping $TILLER_NAME installation" - fi } # fail_trap is executed if an error occurs. @@ -180,15 +172,14 @@ testVersion() { exit 1 fi set -e - echo "Run '$PROJECT_NAME init' to configure $PROJECT_NAME." } # help provides possible cli installation arguments help () { echo "Accepted cli arguments are:" echo -e "\t[--help|-h ] ->> prints this help" - echo -e "\t[--version|-v ] . When not defined it defaults to latest" - echo -e "\te.g. --version v2.4.0 or -v latest" + echo -e "\t[--version|-v ] . When not defined it fetches the latest release from GitHub" + echo -e "\te.g. --version v3.0.0 or -v canary" echo -e "\t[--no-sudo] ->> install without sudo" } @@ -215,7 +206,7 @@ while [[ $# -gt 0 ]]; do if [[ $# -ne 0 ]]; then export DESIRED_VERSION="${1}" else - echo -e "Please provide the desired version. e.g. --version v2.4.0 or -v latest" + echo -e "Please provide the desired version. e.g. --version v3.0.0 or -v canary" exit 0 fi ;; From 8889625af63a93d08909af0c2cfdbddbcd7204db Mon Sep 17 00:00:00 2001 From: Daniel Strobusch <1847260+dastrobu@users.noreply.github.com> Date: Wed, 13 Nov 2019 18:29:48 +0100 Subject: [PATCH 16/46] fix: change error message to contain correct field name When reporting an incompatible Kubernetes version, due to a version constraint from the kubeVersion field, the error message should report with the correct field name. Signed-off-by: Daniel Strobusch <1847260+dastrobu@users.noreply.github.com> --- pkg/action/install.go | 2 +- pkg/action/install_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/action/install.go b/pkg/action/install.go index 4019dbc54..11862c57a 100644 --- a/pkg/action/install.go +++ b/pkg/action/install.go @@ -420,7 +420,7 @@ func (c *Configuration) renderResources(ch *chart.Chart, values chartutil.Values if ch.Metadata.KubeVersion != "" { if !chartutil.IsCompatibleRange(ch.Metadata.KubeVersion, caps.KubeVersion.String()) { - return hs, b, "", errors.Errorf("chart requires kubernetesVersion: %s which is incompatible with Kubernetes %s", ch.Metadata.KubeVersion, caps.KubeVersion.String()) + return hs, b, "", errors.Errorf("chart requires kubeVersion: %s which is incompatible with Kubernetes %s", ch.Metadata.KubeVersion, caps.KubeVersion.String()) } } diff --git a/pkg/action/install_test.go b/pkg/action/install_test.go index bb36b843d..4637a4b10 100644 --- a/pkg/action/install_test.go +++ b/pkg/action/install_test.go @@ -306,7 +306,7 @@ func TestInstallRelease_KubeVersion(t *testing.T) { vals = map[string]interface{}{} _, err = instAction.Run(buildChart(withKube(">=99.0.0")), vals) is.Error(err) - is.Contains(err.Error(), "chart requires kubernetesVersion") + is.Contains(err.Error(), "chart requires kubeVersion") } func TestInstallRelease_Wait(t *testing.T) { From 44f72c4c0a55c8af25a88b363c3567408869aaa0 Mon Sep 17 00:00:00 2001 From: Jonathan Meyers Date: Mon, 18 Nov 2019 13:29:21 +0700 Subject: [PATCH 17/46] homebrew renamed formula to just helm from kubernetes-helm Signed-off-by: Jonathan Meyers --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9af358e10..06a53cf4d 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Unpack the `helm` binary and add it to your PATH and you are good to go! If you want to use a package manager: -- [Homebrew](https://brew.sh/) users can use `brew install kubernetes-helm`. +- [Homebrew](https://brew.sh/) users can use `brew install helm`. - [Chocolatey](https://chocolatey.org/) users can use `choco install kubernetes-helm`. - [Scoop](https://scoop.sh/) users can use `scoop install helm`. - [GoFish](https://gofi.sh/) users can use `gofish install helm`. From 27c1ea63c70368d92ca101eac2efc569e3717520 Mon Sep 17 00:00:00 2001 From: Jonathan Meyers Date: Mon, 18 Nov 2019 13:34:41 +0700 Subject: [PATCH 18/46] Signed-off-by: Jonathan Meyers --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 06a53cf4d..ff8842962 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Unpack the `helm` binary and add it to your PATH and you are good to go! If you want to use a package manager: -- [Homebrew](https://brew.sh/) users can use `brew install helm`. +- [Homebrew](https://brew.sh/) users can use `brew installhelm`. - [Chocolatey](https://chocolatey.org/) users can use `choco install kubernetes-helm`. - [Scoop](https://scoop.sh/) users can use `scoop install helm`. - [GoFish](https://gofi.sh/) users can use `gofish install helm`. From f65ce9167e08e9677940dce4bc5529db4dcf1665 Mon Sep 17 00:00:00 2001 From: Jonathan Meyers Date: Mon, 18 Nov 2019 13:35:01 +0700 Subject: [PATCH 19/46] Signed-off-by: Jonathan Meyers --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ff8842962..06a53cf4d 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Unpack the `helm` binary and add it to your PATH and you are good to go! If you want to use a package manager: -- [Homebrew](https://brew.sh/) users can use `brew installhelm`. +- [Homebrew](https://brew.sh/) users can use `brew install helm`. - [Chocolatey](https://chocolatey.org/) users can use `choco install kubernetes-helm`. - [Scoop](https://scoop.sh/) users can use `scoop install helm`. - [GoFish](https://gofi.sh/) users can use `gofish install helm`. From 8a8463e08d8a52b59fd20739ec3aa98be5bf1177 Mon Sep 17 00:00:00 2001 From: Scott Morgan Date: Tue, 19 Nov 2019 17:18:45 -0600 Subject: [PATCH 20/46] fix(lint): Remove requirement that directory name and chart name match Signed-off-by: Scott Morgan --- pkg/lint/rules/chartfile.go | 8 -------- pkg/lint/rules/chartfile_test.go | 18 ------------------ 2 files changed, 26 deletions(-) diff --git a/pkg/lint/rules/chartfile.go b/pkg/lint/rules/chartfile.go index 35bb81571..70cb83bc5 100644 --- a/pkg/lint/rules/chartfile.go +++ b/pkg/lint/rules/chartfile.go @@ -46,7 +46,6 @@ func Chartfile(linter *support.Linter) { } linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartName(chartFile)) - linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartNameDirMatch(linter.ChartDir, chartFile)) // Chart metadata linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartAPIVersion(chartFile)) @@ -82,13 +81,6 @@ func validateChartName(cf *chart.Metadata) error { return nil } -func validateChartNameDirMatch(chartDir string, cf *chart.Metadata) error { - if cf.Name != filepath.Base(chartDir) { - return errors.Errorf("directory name (%s) and chart name (%s) must be the same", filepath.Base(chartDir), cf.Name) - } - return nil -} - func validateChartAPIVersion(cf *chart.Metadata) error { if cf.APIVersion == "" { return errors.New("apiVersion is required. The value must be either \"v1\" or \"v2\"") diff --git a/pkg/lint/rules/chartfile_test.go b/pkg/lint/rules/chartfile_test.go index ff2a63583..f99299737 100644 --- a/pkg/lint/rules/chartfile_test.go +++ b/pkg/lint/rules/chartfile_test.go @@ -73,24 +73,6 @@ func TestValidateChartName(t *testing.T) { } } -func TestValidateChartNameDirMatch(t *testing.T) { - err := validateChartNameDirMatch(goodChartDir, goodChart) - if err != nil { - t.Errorf("validateChartNameDirMatch to return no error, gor a linter error") - } - // It has not name - err = validateChartNameDirMatch(badChartDir, badChart) - if err == nil { - t.Errorf("validatechartnamedirmatch to return a linter error, got no error") - } - - // Wrong path - err = validateChartNameDirMatch(badChartDir, goodChart) - if err == nil { - t.Errorf("validatechartnamedirmatch to return a linter error, got no error") - } -} - func TestValidateChartVersion(t *testing.T) { var failTest = []struct { Version string From eb833ccead99ecec15f3c192ad41bb041e284953 Mon Sep 17 00:00:00 2001 From: Scott Morgan Date: Wed, 20 Nov 2019 07:35:11 -0600 Subject: [PATCH 21/46] remove unused variable Signed-off-by: Scott Morgan --- pkg/lint/rules/chartfile_test.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pkg/lint/rules/chartfile_test.go b/pkg/lint/rules/chartfile_test.go index f99299737..fe306f653 100644 --- a/pkg/lint/rules/chartfile_test.go +++ b/pkg/lint/rules/chartfile_test.go @@ -30,18 +30,15 @@ import ( ) const ( - badChartDir = "testdata/badchartfile" - goodChartDir = "testdata/goodone" + badChartDir = "testdata/badchartfile" ) var ( badChartFilePath = filepath.Join(badChartDir, "Chart.yaml") - goodChartFilePath = filepath.Join(goodChartDir, "Chart.yaml") nonExistingChartFilePath = filepath.Join(os.TempDir(), "Chart.yaml") ) var badChart, _ = chartutil.LoadChartfile(badChartFilePath) -var goodChart, _ = chartutil.LoadChartfile(goodChartFilePath) // Validation functions Test func TestValidateChartYamlNotDirectory(t *testing.T) { From 32ce016054648f20168a2d7f4ff4b954686e0689 Mon Sep 17 00:00:00 2001 From: Scott Morgan Date: Wed, 20 Nov 2019 08:26:12 -0600 Subject: [PATCH 22/46] fix(lint): Remove requirement that directory name and chart name match Signed-off-by: Scott Morgan --- pkg/lint/lint_test.go | 15 ++++++--------- pkg/lint/rules/chartfile_test.go | 18 +++++++----------- 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/pkg/lint/lint_test.go b/pkg/lint/lint_test.go index b770704c6..2a982d088 100644 --- a/pkg/lint/lint_test.go +++ b/pkg/lint/lint_test.go @@ -35,12 +35,12 @@ const goodChartDir = "rules/testdata/goodone" func TestBadChart(t *testing.T) { m := All(badChartDir, values, namespace, strict).Messages - if len(m) != 8 { + if len(m) != 7 { t.Errorf("Number of errors %v", len(m)) t.Errorf("All didn't fail with expected errors, got %#v", m) } // There should be one INFO, 2 WARNINGs and one ERROR messages, check for them - var i, w, e, e2, e3, e4, e5, e6 bool + var i, w, e, e2, e3, e4, e5 bool for _, msg := range m { if msg.Severity == support.InfoSev { if strings.Contains(msg.Err.Error(), "icon is recommended") { @@ -59,24 +59,21 @@ func TestBadChart(t *testing.T) { if strings.Contains(msg.Err.Error(), "name is required") { e2 = true } - if strings.Contains(msg.Err.Error(), "directory name (badchartfile) and chart name () must be the same") { - e3 = true - } if strings.Contains(msg.Err.Error(), "apiVersion is required. The value must be either \"v1\" or \"v2\"") { - e4 = true + e3 = true } if strings.Contains(msg.Err.Error(), "chart type is not valid in apiVersion") { - e5 = true + e4 = true } if strings.Contains(msg.Err.Error(), "dependencies are not valid in the Chart file with apiVersion") { - e6 = true + e5 = true } } } - if !e || !e2 || !e3 || !e4 || !e5 || !e6 || !w || !i { + if !e || !e2 || !e3 || !e4 || !e5 || !w || !i { t.Errorf("Didn't find all the expected errors, got %#v", m) } } diff --git a/pkg/lint/rules/chartfile_test.go b/pkg/lint/rules/chartfile_test.go index fe306f653..d032dd12f 100644 --- a/pkg/lint/rules/chartfile_test.go +++ b/pkg/lint/rules/chartfile_test.go @@ -188,36 +188,32 @@ func TestChartfile(t *testing.T) { Chartfile(&linter) msgs := linter.Messages - if len(msgs) != 7 { - t.Errorf("Expected 7 errors, got %d", len(msgs)) + if len(msgs) != 6 { + t.Errorf("Expected 6 errors, got %d", len(msgs)) } if !strings.Contains(msgs[0].Err.Error(), "name is required") { t.Errorf("Unexpected message 0: %s", msgs[0].Err) } - if !strings.Contains(msgs[1].Err.Error(), "directory name (badchartfile) and chart name () must be the same") { + if !strings.Contains(msgs[1].Err.Error(), "apiVersion is required. The value must be either \"v1\" or \"v2\"") { t.Errorf("Unexpected message 1: %s", msgs[1].Err) } - if !strings.Contains(msgs[2].Err.Error(), "apiVersion is required. The value must be either \"v1\" or \"v2\"") { + if !strings.Contains(msgs[2].Err.Error(), "version '0.0.0.0' is not a valid SemVer") { t.Errorf("Unexpected message 2: %s", msgs[2].Err) } - if !strings.Contains(msgs[3].Err.Error(), "version '0.0.0.0' is not a valid SemVer") { + if !strings.Contains(msgs[3].Err.Error(), "icon is recommended") { t.Errorf("Unexpected message 3: %s", msgs[3].Err) } - if !strings.Contains(msgs[4].Err.Error(), "icon is recommended") { + if !strings.Contains(msgs[4].Err.Error(), "chart type is not valid in apiVersion") { t.Errorf("Unexpected message 4: %s", msgs[4].Err) } - if !strings.Contains(msgs[5].Err.Error(), "chart type is not valid in apiVersion") { + if !strings.Contains(msgs[5].Err.Error(), "dependencies are not valid in the Chart file with apiVersion") { t.Errorf("Unexpected message 5: %s", msgs[5].Err) } - if !strings.Contains(msgs[6].Err.Error(), "dependencies are not valid in the Chart file with apiVersion") { - t.Errorf("Unexpected message 6: %s", msgs[6].Err) - } - } From 6473234f43cbec7603124d684cb630dfdc4b1419 Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Sun, 24 Nov 2019 22:25:10 -0500 Subject: [PATCH 23/46] fix(plugin): Add missing -n known flag Signed-off-by: Marc Khouzam --- cmd/helm/load_plugins.go | 2 +- cmd/helm/plugin_test.go | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/cmd/helm/load_plugins.go b/cmd/helm/load_plugins.go index 53e082e96..d68cf46ee 100644 --- a/cmd/helm/load_plugins.go +++ b/cmd/helm/load_plugins.go @@ -127,7 +127,7 @@ func loadPlugins(baseCmd *cobra.Command, out io.Writer) { func manuallyProcessArgs(args []string) ([]string, []string) { known := []string{} unknown := []string{} - kvargs := []string{"--kube-context", "--namespace", "--kubeconfig", "--registry-config", "--repository-cache", "--repository-config"} + kvargs := []string{"--kube-context", "--namespace", "-n", "--kubeconfig", "--registry-config", "--repository-cache", "--repository-config"} knownArg := func(a string) bool { for _, pre := range kvargs { if strings.HasPrefix(a, pre+"=") { diff --git a/cmd/helm/plugin_test.go b/cmd/helm/plugin_test.go index 7bc8fe70c..3fd3a4197 100644 --- a/cmd/helm/plugin_test.go +++ b/cmd/helm/plugin_test.go @@ -30,14 +30,27 @@ func TestManuallyProcessArgs(t *testing.T) { "--debug", "--foo", "bar", "--kubeconfig=/home/foo", + "--kubeconfig", "/home/foo", + "--kube-context=test1", "--kube-context", "test1", + "-n=test2", "-n", "test2", + "--namespace=test2", + "--namespace", "test2", "--home=/tmp", "command", } expectKnown := []string{ - "--debug", "--kubeconfig=/home/foo", "--kube-context", "test1", "-n", "test2", + "--debug", + "--kubeconfig=/home/foo", + "--kubeconfig", "/home/foo", + "--kube-context=test1", + "--kube-context", "test1", + "-n=test2", + "-n", "test2", + "--namespace=test2", + "--namespace", "test2", } expectUnknown := []string{ From 5179f8d698d30b243fd9aa646fac76224510b28b Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Sun, 24 Nov 2019 22:50:22 -0500 Subject: [PATCH 24/46] fix(plugin): Avoid duplication of flag list Signed-off-by: Marc Khouzam --- cmd/helm/load_plugins.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/cmd/helm/load_plugins.go b/cmd/helm/load_plugins.go index d68cf46ee..ed99a5164 100644 --- a/cmd/helm/load_plugins.go +++ b/cmd/helm/load_plugins.go @@ -136,11 +136,21 @@ func manuallyProcessArgs(args []string) ([]string, []string) { } return false } + + isKnown := func(v string) string { + for _, i := range kvargs { + if i == v { + return v + } + } + return "" + } + for i := 0; i < len(args); i++ { switch a := args[i]; a { case "--debug": known = append(known, a) - case "--kube-context", "--namespace", "-n", "--kubeconfig", "--registry-config", "--repository-cache", "--repository-config": + case isKnown(a): known = append(known, a, args[i+1]) i++ default: From e3f49085ccb7b8f35e988554392d5f7ec5e066ad Mon Sep 17 00:00:00 2001 From: Andreas Stenius Date: Mon, 25 Nov 2019 12:38:57 +0100 Subject: [PATCH 25/46] chart_downloader: add test to verify that http opts are used correctly. (#7055) Signed-off-by: Andreas Stenius --- pkg/downloader/chart_downloader_test.go | 61 +++++++++++++++++++ pkg/downloader/testdata/repositories.yaml | 7 ++- .../repository/testing-ca-file-index.yaml | 14 +++++ 3 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 pkg/downloader/testdata/repository/testing-ca-file-index.yaml diff --git a/pkg/downloader/chart_downloader_test.go b/pkg/downloader/chart_downloader_test.go index 80249e240..abecf81eb 100644 --- a/pkg/downloader/chart_downloader_test.go +++ b/pkg/downloader/chart_downloader_test.go @@ -80,6 +80,67 @@ func TestResolveChartRef(t *testing.T) { } } +func TestResolveChartOpts(t *testing.T) { + tests := []struct { + name, ref, version string + expect []getter.Option + }{ + { + name: "repo with CA-file", + ref: "testing-ca-file/foo", + expect: []getter.Option{ + getter.WithURL("https://example.com/foo-1.2.3.tgz"), + getter.WithTLSClientConfig("cert", "key", "ca"), + }, + }, + } + + c := ChartDownloader{ + Out: os.Stderr, + RepositoryConfig: repoConfig, + RepositoryCache: repoCache, + Getters: getter.All(&cli.EnvSettings{ + RepositoryConfig: repoConfig, + RepositoryCache: repoCache, + }), + } + + // snapshot options + snapshot_opts := c.Options + + for _, tt := range tests { + // reset chart downloader options for each test case + c.Options = snapshot_opts + + expect, err := getter.NewHTTPGetter(tt.expect...) + if err != nil { + t.Errorf("%s: failed to setup http client: %s", tt.name, err) + continue + } + + u, err := c.ResolveChartVersion(tt.ref, tt.version) + if err != nil { + t.Errorf("%s: failed with error %s", tt.name, err) + continue + } + + got, err := getter.NewHTTPGetter( + append( + c.Options, + getter.WithURL(u.String()), + )... + ) + if err != nil { + t.Errorf("%s: failed to create http client: %s", tt.name, err) + continue + } + + if got != expect { + t.Errorf("%s: expected %s, got %s", tt.name, expect, got) + } + } +} + func TestVerifyChart(t *testing.T) { v, err := VerifyChart("testdata/signtest-0.1.0.tgz", "testdata/helm-test-key.pub") if err != nil { diff --git a/pkg/downloader/testdata/repositories.yaml b/pkg/downloader/testdata/repositories.yaml index 374d95c8a..430865269 100644 --- a/pkg/downloader/testdata/repositories.yaml +++ b/pkg/downloader/testdata/repositories.yaml @@ -15,4 +15,9 @@ repositories: - name: testing-relative url: "http://example.com/helm" - name: testing-relative-trailing-slash - url: "http://example.com/helm/" \ No newline at end of file + url: "http://example.com/helm/" + - name: testing-ca-file + url: "https://example.com" + certFile: "cert" + keyFile: "key" + caFile: "ca" diff --git a/pkg/downloader/testdata/repository/testing-ca-file-index.yaml b/pkg/downloader/testdata/repository/testing-ca-file-index.yaml new file mode 100644 index 000000000..17cdde1c6 --- /dev/null +++ b/pkg/downloader/testdata/repository/testing-ca-file-index.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +entries: + foo: + - name: foo + description: Foo Chart + home: https://helm.sh/helm + keywords: [] + maintainers: [] + sources: + - https://github.com/helm/charts + urls: + - https://example.com/foo-1.2.3.tgz + version: 1.2.3 + checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d From d6c13616fa7beff70931255cce62ab336ce1531d Mon Sep 17 00:00:00 2001 From: Andreas Stenius Date: Mon, 25 Nov 2019 13:10:15 +0100 Subject: [PATCH 26/46] chart_downloader: add TLS client config to options from repo config. (#7055) Signed-off-by: Andreas Stenius --- pkg/downloader/chart_downloader.go | 2 ++ pkg/downloader/chart_downloader_test.go | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/downloader/chart_downloader.go b/pkg/downloader/chart_downloader.go index 8e251bc89..9b9293455 100644 --- a/pkg/downloader/chart_downloader.go +++ b/pkg/downloader/chart_downloader.go @@ -214,6 +214,8 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, er c.Options = append(c.Options, getter.WithBasicAuth(r.Config.Username, r.Config.Password)) } + c.Options = append(c.Options, getter.WithTLSClientConfig(r.Config.CertFile, r.Config.KeyFile, r.Config.CAFile)) + // Next, we need to load the index, and actually look up the chart. idxFile := filepath.Join(c.RepositoryCache, helmpath.CacheIndexFile(r.Config.Name)) i, err := repo.LoadIndexFile(idxFile) diff --git a/pkg/downloader/chart_downloader_test.go b/pkg/downloader/chart_downloader_test.go index abecf81eb..71a56d482 100644 --- a/pkg/downloader/chart_downloader_test.go +++ b/pkg/downloader/chart_downloader_test.go @@ -135,7 +135,7 @@ func TestResolveChartOpts(t *testing.T) { continue } - if got != expect { + if *(got.(*getter.HTTPGetter)) != *(expect.(*getter.HTTPGetter)) { t.Errorf("%s: expected %s, got %s", tt.name, expect, got) } } From d3ad6f9c78113fd4c577b31e41277796220e4c0f Mon Sep 17 00:00:00 2001 From: Andreas Stenius Date: Mon, 25 Nov 2019 13:23:08 +0100 Subject: [PATCH 27/46] cli/pull: pass TLS config to chart downloader from flags. (#7055) Signed-off-by: Andreas Stenius --- pkg/action/pull.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/action/pull.go b/pkg/action/pull.go index aaf63861e..b0a3d2598 100644 --- a/pkg/action/pull.go +++ b/pkg/action/pull.go @@ -63,6 +63,7 @@ func (p *Pull) Run(chartRef string) (string, error) { Getters: getter.All(p.Settings), Options: []getter.Option{ getter.WithBasicAuth(p.Username, p.Password), + getter.WithTLSClientConfig(p.CertFile, p.KeyFile, p.CaFile), }, RepositoryConfig: p.Settings.RepositoryConfig, RepositoryCache: p.Settings.RepositoryCache, From 0f0aa6b43261b3f4b95fd7ee14107de2630dd446 Mon Sep 17 00:00:00 2001 From: Andreas Stenius Date: Mon, 25 Nov 2019 13:37:20 +0100 Subject: [PATCH 28/46] chart_downloader: avoid overriding TLS options from command flags when not setup in repo config. Signed-off-by: Andreas Stenius --- pkg/downloader/chart_downloader.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/downloader/chart_downloader.go b/pkg/downloader/chart_downloader.go index 9b9293455..f3d4321c5 100644 --- a/pkg/downloader/chart_downloader.go +++ b/pkg/downloader/chart_downloader.go @@ -214,7 +214,9 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, er c.Options = append(c.Options, getter.WithBasicAuth(r.Config.Username, r.Config.Password)) } - c.Options = append(c.Options, getter.WithTLSClientConfig(r.Config.CertFile, r.Config.KeyFile, r.Config.CAFile)) + if r.Config.CertFile != "" || r.Config.KeyFile != "" || r.Config.CAFile != "" { + c.Options = append(c.Options, getter.WithTLSClientConfig(r.Config.CertFile, r.Config.KeyFile, r.Config.CAFile)) + } // Next, we need to load the index, and actually look up the chart. idxFile := filepath.Join(c.RepositoryCache, helmpath.CacheIndexFile(r.Config.Name)) From 4c4328398ecc4f6eef07524f4bcddaca153b4570 Mon Sep 17 00:00:00 2001 From: Andreas Stenius Date: Mon, 25 Nov 2019 14:17:24 +0100 Subject: [PATCH 29/46] chart_downloader: fix lint issue. Signed-off-by: Andreas Stenius --- pkg/downloader/chart_downloader_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/downloader/chart_downloader_test.go b/pkg/downloader/chart_downloader_test.go index 71a56d482..e0692c8c8 100644 --- a/pkg/downloader/chart_downloader_test.go +++ b/pkg/downloader/chart_downloader_test.go @@ -87,7 +87,7 @@ func TestResolveChartOpts(t *testing.T) { }{ { name: "repo with CA-file", - ref: "testing-ca-file/foo", + ref: "testing-ca-file/foo", expect: []getter.Option{ getter.WithURL("https://example.com/foo-1.2.3.tgz"), getter.WithTLSClientConfig("cert", "key", "ca"), @@ -106,11 +106,11 @@ func TestResolveChartOpts(t *testing.T) { } // snapshot options - snapshot_opts := c.Options + snapshotOpts := c.Options for _, tt := range tests { // reset chart downloader options for each test case - c.Options = snapshot_opts + c.Options = snapshotOpts expect, err := getter.NewHTTPGetter(tt.expect...) if err != nil { @@ -128,7 +128,7 @@ func TestResolveChartOpts(t *testing.T) { append( c.Options, getter.WithURL(u.String()), - )... + )..., ) if err != nil { t.Errorf("%s: failed to create http client: %s", tt.name, err) From 32b4e2e5e998ee11559d132eae796f2b22fac0f7 Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Sun, 24 Nov 2019 23:08:13 -0500 Subject: [PATCH 30/46] fix(plugin): Avoid crash on missing flag When calling a plugin, if a global flag requiring a parameter was missing the parameter, helm would crash. For example: helm 2to3 --namespace Signed-off-by: Marc Khouzam --- cmd/helm/load_plugins.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmd/helm/load_plugins.go b/cmd/helm/load_plugins.go index ed99a5164..f2fb5c01d 100644 --- a/cmd/helm/load_plugins.go +++ b/cmd/helm/load_plugins.go @@ -151,8 +151,11 @@ func manuallyProcessArgs(args []string) ([]string, []string) { case "--debug": known = append(known, a) case isKnown(a): - known = append(known, a, args[i+1]) + known = append(known, a) i++ + if i < len(args) { + known = append(known, args[i]) + } default: if knownArg(a) { known = append(known, a) From 48704034a9f37a23eee192b32e15249b299e0767 Mon Sep 17 00:00:00 2001 From: chloel Date: Tue, 26 Nov 2019 16:48:15 -0500 Subject: [PATCH 31/46] fix: ignore pax header files in chart validation Signed-off-by: chloel --- pkg/chart/loader/archive.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkg/chart/loader/archive.go b/pkg/chart/loader/archive.go index 3c50fe379..7e187a170 100644 --- a/pkg/chart/loader/archive.go +++ b/pkg/chart/loader/archive.go @@ -126,6 +126,12 @@ func LoadArchiveFiles(in io.Reader) ([]*BufferedFile, error) { continue } + switch hd.Typeflag { + // We don't want to process these extension header files. + case tar.TypeXGlobalHeader, tar.TypeXHeader: + continue + } + // Archive could contain \ if generated on Windows delimiter := "/" if strings.ContainsRune(hd.Name, '\\') { From af59e32654f048d62b053afb0860a3bec77444fb Mon Sep 17 00:00:00 2001 From: Remco Haszing Date: Thu, 28 Nov 2019 13:01:20 +0100 Subject: [PATCH 32/46] docs(install): clarify the --replace flag (#7089) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs(install): clarify the --replace flag The description of the `--replace` flag was unclear, as it can’t be used to replace active releases. Signed-off-by: Remco Haszing * docs(install): reword replace flag description Signed-off-by: Remco Haszing --- 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 40935db17..0c14c013b 100644 --- a/cmd/helm/install.go +++ b/cmd/helm/install.go @@ -130,7 +130,7 @@ func newInstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { func addInstallFlags(f *pflag.FlagSet, client *action.Install, valueOpts *values.Options) { f.BoolVar(&client.DryRun, "dry-run", false, "simulate an install") f.BoolVar(&client.DisableHooks, "no-hooks", false, "prevent hooks from running during install") - f.BoolVar(&client.Replace, "replace", false, "re-use the given name, even if that name is already used. This is unsafe in production") + f.BoolVar(&client.Replace, "replace", false, "re-use the given name, only if that name is a deleted release which remains in the history. This is unsafe in production") f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)") f.BoolVar(&client.Wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment, StatefulSet, or ReplicaSet are in a ready state before marking the release as successful. It will wait for as long as --timeout") f.BoolVarP(&client.GenerateName, "generate-name", "g", false, "generate the name (and omit the NAME parameter)") From b8605c8d36960a552cdf74104f02762ef07e2713 Mon Sep 17 00:00:00 2001 From: Geoff Baskwill Date: Wed, 27 Nov 2019 19:46:34 -0500 Subject: [PATCH 33/46] test(pkg): add unit tests for tar file edge cases Adding unit tests for an issue that has come up multiple times where the archive processing code doesn't take into account the `tar.TypeXHeader` / `tar.TypeXGlobalHeader` entries that GitHub adds when creating a release archive for a chart, for example `https://github.com/org/repo/master.tar.gz`. Signed-off-by: Geoff Baskwill --- pkg/chart/loader/archive_test.go | 110 +++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 pkg/chart/loader/archive_test.go diff --git a/pkg/chart/loader/archive_test.go b/pkg/chart/loader/archive_test.go new file mode 100644 index 000000000..7d8c8b51e --- /dev/null +++ b/pkg/chart/loader/archive_test.go @@ -0,0 +1,110 @@ +/* +Copyright The Helm Authors. + +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 loader + +import ( + "archive/tar" + "bytes" + "compress/gzip" + "testing" +) + +func TestLoadArchiveFiles(t *testing.T) { + tcs := []struct { + name string + generate func(w *tar.Writer) + check func(t *testing.T, files []*BufferedFile, err error) + }{ + { + name: "empty input should return no files", + generate: func(w *tar.Writer) {}, + check: func(t *testing.T, files []*BufferedFile, err error) { + if err.Error() != "no files in chart archive" { + t.Fatalf(`expected "no files in chart archive", got [%#v]`, err) + } + }, + }, + { + name: "should ignore files with XGlobalHeader type", + generate: func(w *tar.Writer) { + // simulate the presence of a `pax_global_header` file like you would get when + // processing a GitHub release archive. + _ = w.WriteHeader(&tar.Header{ + Typeflag: tar.TypeXGlobalHeader, + Name: "pax_global_header", + }) + + // we need to have at least one file, otherwise we'll get the "no files in chart archive" error + _ = w.WriteHeader(&tar.Header{ + Typeflag: tar.TypeReg, + Name: "dir/empty", + }) + }, + check: func(t *testing.T, files []*BufferedFile, err error) { + if err != nil { + t.Fatalf(`got unwanted error [%#v] for tar file with pax_global_header content`, err) + } + + if len(files) != 1 { + t.Fatalf(`expected to get one file but got [%v]`, files) + } + }, + }, + { + name: "should ignore files with TypeXHeader type", + generate: func(w *tar.Writer) { + // simulate the presence of a `pax_header` file like you might get when + // processing a GitHub release archive. + _ = w.WriteHeader(&tar.Header{ + Typeflag: tar.TypeXHeader, + Name: "pax_header", + }) + + // we need to have at least one file, otherwise we'll get the "no files in chart archive" error + _ = w.WriteHeader(&tar.Header{ + Typeflag: tar.TypeReg, + Name: "dir/empty", + }) + }, + check: func(t *testing.T, files []*BufferedFile, err error) { + if err != nil { + t.Fatalf(`got unwanted error [%#v] for tar file with pax_header content`, err) + } + + if len(files) != 1 { + t.Fatalf(`expected to get one file but got [%v]`, files) + } + }, + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + buf := &bytes.Buffer{} + gzw := gzip.NewWriter(buf) + tw := tar.NewWriter(gzw) + + tc.generate(tw) + + _ = tw.Close() + _ = gzw.Close() + + files, err := LoadArchiveFiles(buf) + tc.check(t, files, err) + }) + } +} From 750b870aedd35b69b9ca1e1517635fa70367a309 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B5=B7=E7=9A=84=E6=BE=9C=E8=89=B2?= <33822635+zwwhdls@users.noreply.github.com> Date: Mon, 2 Dec 2019 22:57:51 +0800 Subject: [PATCH 34/46] fix stack overflow error (#7114) * fixed #7111 Signed-off-by: zwwhdls * update error message Signed-off-by: zwwhdls * add test case Signed-off-by: zwwhdls * fix lint error Signed-off-by: zwwhdls --- pkg/engine/engine.go | 8 ++++++++ pkg/engine/engine_test.go | 15 +++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index dae0b6be7..5a7d54993 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -93,11 +93,19 @@ func warnWrap(warn string) string { // initFunMap creates the Engine's FuncMap and adds context-specific functions. func (e Engine) initFunMap(t *template.Template, referenceTpls map[string]renderable) { funcMap := funcMap() + includedNames := make([]string, 0) // Add the 'include' function here so we can close over t. funcMap["include"] = func(name string, data interface{}) (string, error) { var buf strings.Builder + for _, n := range includedNames { + if n == name { + return "", errors.Wrapf(fmt.Errorf("unable to excute template"), "rendering template has a nested reference name: %s", name) + } + } + includedNames = append(includedNames, name) err := t.ExecuteTemplate(&buf, name, data) + includedNames = includedNames[:len(includedNames)-1] return buf.String(), err } diff --git a/pkg/engine/engine_test.go b/pkg/engine/engine_test.go index 031527dd0..f3609fcbd 100644 --- a/pkg/engine/engine_test.go +++ b/pkg/engine/engine_test.go @@ -460,6 +460,15 @@ func TestAlterFuncMap_include(t *testing.T) { }, } + // Check nested reference in include FuncMap + d := &chart.Chart{ + Metadata: &chart.Metadata{Name: "nested"}, + Templates: []*chart.File{ + {Name: "templates/quote", Data: []byte(`{{include "nested/templates/quote" . | indent 2}} dead.`)}, + {Name: "templates/_partial", Data: []byte(`{{.Release.Name}} - he`)}, + }, + } + v := chartutil.Values{ "Values": "", "Chart": c.Metadata, @@ -477,6 +486,12 @@ func TestAlterFuncMap_include(t *testing.T) { if got := out["conrad/templates/quote"]; got != expect { t.Errorf("Expected %q, got %q (%v)", expect, got, out) } + + _, err = Render(d, v) + expectErrName := "nested/templates/quote" + if err == nil { + t.Errorf("Expected err of nested reference name: %v", expectErrName) + } } func TestAlterFuncMap_require(t *testing.T) { From bf4cc97bbe7ca994894a87df63fdf7da6ee0841a Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Tue, 3 Dec 2019 08:48:44 -0500 Subject: [PATCH 35/46] fix(cli): IsReachable check for "get values" The 'helm get values' has its own Run() method in the action package. So, unlike the other 'get' variants, it needs to check for the reachability of the cluster itself. Signed-off-by: Marc Khouzam --- pkg/action/get_values.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/action/get_values.go b/pkg/action/get_values.go index 5bc3a7005..9c32db213 100644 --- a/pkg/action/get_values.go +++ b/pkg/action/get_values.go @@ -39,6 +39,10 @@ func NewGetValues(cfg *Configuration) *GetValues { // Run executes 'helm get values' against the given release. func (g *GetValues) Run(name string) (map[string]interface{}, error) { + if err := g.cfg.KubeClient.IsReachable(); err != nil { + return nil, err + } + rel, err := g.cfg.releaseContent(name, g.Version) if err != nil { return nil, err From cc33e394c7c464be1940a2f737a1f24887e4ca6f Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Thu, 28 Nov 2019 16:26:55 -0500 Subject: [PATCH 36/46] fix(tests): mapfile is not available on MacOS The mapfile command is not available on a standard MacOS install. To faciliate doing `make test` which runs validate-license.sh, let's use an array instead of mapfile. Signed-off-by: Marc Khouzam --- scripts/validate-license.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/validate-license.sh b/scripts/validate-license.sh index 00bd38ea2..dc247436f 100755 --- a/scripts/validate-license.sh +++ b/scripts/validate-license.sh @@ -27,14 +27,16 @@ find_files() { \( -name '*.go' -o -name '*.sh' \) } -mapfile -t failed_license_header < <(find_files | xargs grep -L 'Licensed under the Apache License, Version 2.0 (the "License")') +# Use "|| :" to ignore the error code when grep returns empty +failed_license_header=($(find_files | xargs grep -L 'Licensed under the Apache License, Version 2.0 (the "License")' || :)) if (( ${#failed_license_header[@]} > 0 )); then echo "Some source files are missing license headers." printf '%s\n' "${failed_license_header[@]}" exit 1 fi -mapfile -t failed_copyright_header < <(find_files | xargs grep -L 'Copyright The Helm Authors.') +# Use "|| :" to ignore the error code when grep returns empty +failed_copyright_header=($(find_files | xargs grep -L 'Copyright The Helm Authors.' || :)) if (( ${#failed_copyright_header[@]} > 0 )); then echo "Some source files are missing the copyright header." printf '%s\n' "${failed_copyright_header[@]}" From 01aa1bd3a299f6a456c5472394fd5138a20b87ce Mon Sep 17 00:00:00 2001 From: Graham Goudeau Date: Tue, 3 Dec 2019 14:06:55 -0500 Subject: [PATCH 37/46] Add a flag to allow template to output CRDs Signed-off-by: Graham Goudeau --- cmd/helm/template.go | 9 +++ cmd/helm/template_test.go | 5 ++ .../testdata/output/template-with-crds.txt | 72 +++++++++++++++++++ .../subpop/charts/subchart1/crds/crdA.yaml | 13 ++++ 4 files changed, 99 insertions(+) create mode 100644 cmd/helm/testdata/output/template-with-crds.txt create mode 100644 pkg/chartutil/testdata/subpop/charts/subchart1/crds/crdA.yaml diff --git a/cmd/helm/template.go b/cmd/helm/template.go index 5312d798f..b660cc69f 100644 --- a/cmd/helm/template.go +++ b/cmd/helm/template.go @@ -44,6 +44,7 @@ faked locally. Additionally, none of the server-side testing of chart validity func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { var validate bool + var includeCrds bool client := action.NewInstall(cfg) valueOpts := &values.Options{} var extraAPIs []string @@ -67,7 +68,14 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { var manifests bytes.Buffer + if includeCrds { + for _, f := range rel.Chart.CRDs() { + fmt.Fprintf(&manifests, "---\n# Source: %s\n%s\n", f.Name, f.Data) + } + } + fmt.Fprintln(&manifests, strings.TrimSpace(rel.Manifest)) + if !client.DisableHooks { for _, m := range rel.Hooks { fmt.Fprintf(&manifests, "---\n# Source: %s\n%s\n", m.Path, m.Manifest) @@ -120,6 +128,7 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { f.StringArrayVarP(&showFiles, "show-only", "s", []string{}, "only show manifests rendered from the given templates") f.StringVar(&client.OutputDir, "output-dir", "", "writes the executed templates to files in output-dir instead of stdout") f.BoolVar(&validate, "validate", false, "validate your manifests against the Kubernetes cluster you are currently pointing at. This is the same validation performed on an install") + f.BoolVar(&includeCrds, "include-crds", false, "include CRDs in the templated output") f.StringArrayVarP(&extraAPIs, "api-versions", "a", []string{}, "Kubernetes api versions used for Capabilities.APIVersions") return cmd diff --git a/cmd/helm/template_test.go b/cmd/helm/template_test.go index 8aa2f6c06..735829432 100644 --- a/cmd/helm/template_test.go +++ b/cmd/helm/template_test.go @@ -79,6 +79,11 @@ func TestTemplateCmd(t *testing.T) { cmd: fmt.Sprintf("template --api-versions helm.k8s.io/test '%s'", chartPath), golden: "output/template-with-api-version.txt", }, + { + name: "template with CRDs", + cmd: fmt.Sprintf("template '%s' --include-crds", chartPath), + golden: "output/template-with-crds.txt", + }, } runTestCmd(t, tests) } diff --git a/cmd/helm/testdata/output/template-with-crds.txt b/cmd/helm/testdata/output/template-with-crds.txt new file mode 100644 index 000000000..9fa1c7e6d --- /dev/null +++ b/cmd/helm/testdata/output/template-with-crds.txt @@ -0,0 +1,72 @@ +--- +# Source: crds/crdA.yaml +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: testCRDs +spec: + group: testCRDGroups + names: + kind: TestCRD + listKind: TestCRDList + plural: TestCRDs + shortNames: + - tc + singular: authconfig + +--- +# Source: subchart1/charts/subcharta/templates/service.yaml +apiVersion: v1 +kind: Service +metadata: + name: subcharta + labels: + helm.sh/chart: "subcharta-0.1.0" +spec: + type: ClusterIP + ports: + - port: 80 + targetPort: 80 + protocol: TCP + name: apache + selector: + app.kubernetes.io/name: subcharta +--- +# Source: subchart1/charts/subchartb/templates/service.yaml +apiVersion: v1 +kind: Service +metadata: + name: subchartb + labels: + helm.sh/chart: "subchartb-0.1.0" +spec: + type: ClusterIP + ports: + - port: 80 + targetPort: 80 + protocol: TCP + name: nginx + selector: + app.kubernetes.io/name: subchartb +--- +# Source: subchart1/templates/service.yaml +apiVersion: v1 +kind: Service +metadata: + name: subchart1 + labels: + helm.sh/chart: "subchart1-0.1.0" + app.kubernetes.io/instance: "RELEASE-NAME" + kube-version/major: "1" + kube-version/minor: "16" + kube-version/version: "v1.16.0" + kube-api-version/test: v1 +spec: + type: ClusterIP + ports: + - port: 80 + targetPort: 80 + protocol: TCP + name: nginx + selector: + app.kubernetes.io/name: subchart1 diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/crds/crdA.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/crds/crdA.yaml new file mode 100644 index 000000000..fca77fd4b --- /dev/null +++ b/pkg/chartutil/testdata/subpop/charts/subchart1/crds/crdA.yaml @@ -0,0 +1,13 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: testCRDs +spec: + group: testCRDGroups + names: + kind: TestCRD + listKind: TestCRDList + plural: TestCRDs + shortNames: + - tc + singular: authconfig From e062146db3a9a9bec0e64f5db939416441b1af38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B5=B7=E7=9A=84=E6=BE=9C=E8=89=B2?= <33822635+zwwhdls@users.noreply.github.com> Date: Wed, 4 Dec 2019 22:25:50 +0800 Subject: [PATCH 38/46] fix "Chart.lock is out of sync with Chart.yaml" (#7119) * fixed #7101 Signed-off-by: zwwhdls * add test case Signed-off-by: zwwhdls * fix lint error Signed-off-by: zwwhdls * rename testcase Signed-off-by: zwwhdls --- pkg/downloader/manager.go | 7 ++++ pkg/downloader/manager_test.go | 68 ++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/pkg/downloader/manager.go b/pkg/downloader/manager.go index 4e9d691d8..81dd53614 100644 --- a/pkg/downloader/manager.go +++ b/pkg/downloader/manager.go @@ -149,6 +149,13 @@ func (m *Manager) Update() error { return err } + // downloadAll might overwrite dependency version, recalculate lock digest + newDigest, err := resolver.HashReq(req, lock.Dependencies) + if err != nil { + return err + } + lock.Digest = newDigest + // If the lock file hasn't changed, don't write a new one. oldLock := c.Lock if oldLock != nil && oldLock.Digest == lock.Digest { diff --git a/pkg/downloader/manager_test.go b/pkg/downloader/manager_test.go index 6e32b3e85..dd83c3dc2 100644 --- a/pkg/downloader/manager_test.go +++ b/pkg/downloader/manager_test.go @@ -181,6 +181,74 @@ func TestGetRepoNames(t *testing.T) { } } +func TestUpdateBeforeBuild(t *testing.T) { + // Set up a fake repo + srv, err := repotest.NewTempServer("testdata/*.tgz*") + if err != nil { + t.Fatal(err) + } + defer srv.Stop() + if err := srv.LinkIndices(); err != nil { + t.Fatal(err) + } + dir := func(p ...string) string { + return filepath.Join(append([]string{srv.Root()}, p...)...) + } + + // Save dep + d := &chart.Chart{ + Metadata: &chart.Metadata{ + Name: "dep-chart", + Version: "0.1.0", + APIVersion: "v1", + }, + } + if err := chartutil.SaveDir(d, dir()); err != nil { + t.Fatal(err) + } + // Save a chart + c := &chart.Chart{ + Metadata: &chart.Metadata{ + Name: "with-dependency", + Version: "0.1.0", + APIVersion: "v1", + Dependencies: []*chart.Dependency{{ + Name: d.Metadata.Name, + Version: ">=0.1.0", + Repository: "file://../dep-chart", + }}, + }, + } + if err := chartutil.SaveDir(c, dir()); err != nil { + t.Fatal(err) + } + + // Set-up a manager + b := bytes.NewBuffer(nil) + g := getter.Providers{getter.Provider{ + Schemes: []string{"http", "https"}, + New: getter.NewHTTPGetter, + }} + m := &Manager{ + ChartPath: dir(c.Metadata.Name), + Out: b, + Getters: g, + RepositoryConfig: dir("repositories.yaml"), + RepositoryCache: dir(), + } + + // Update before Build. see issue: https://github.com/helm/helm/issues/7101 + err = m.Update() + if err != nil { + t.Fatal(err) + } + + err = m.Build() + if err != nil { + t.Fatal(err) + } +} + // This function is the skeleton test code of failing tests for #6416 and #6871 and bugs due to #5874. // // This function is used by below tests that ensures success of build operation From a61c930de399e64fdafe5337a9c16a8625843758 Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Wed, 4 Dec 2019 10:23:10 -0500 Subject: [PATCH 39/46] Fixing the code of conduct pointer When the v3 branch was started the code of conduct pointed to the Kubernetes one which pointed to the CNCF. Helm moved to be a CNCF project and the v2 code of conduct was pointed directly at the CNCF one. This was missed on the v3 branch. This updates the pointer. Signed-off-by: Matt Farina --- code-of-conduct.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code-of-conduct.md b/code-of-conduct.md index 0d15c00cf..91ccaf035 100644 --- a/code-of-conduct.md +++ b/code-of-conduct.md @@ -1,3 +1,3 @@ -# Kubernetes Community Code of Conduct +# Community Code of Conduct -Please refer to our [Kubernetes Community Code of Conduct](https://git.k8s.io/community/code-of-conduct.md) +Helm follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md). From 2a66e1a0e1f9b820309cd658aecf10896ac65ca5 Mon Sep 17 00:00:00 2001 From: Karuppiah Natarajan Date: Wed, 4 Dec 2019 22:43:42 +0530 Subject: [PATCH 40/46] use sigs.k8s.io/yaml instead of gopkg.in/yaml.v2 this is for consistency as everywhere we use sigs.k8s.io/yaml package Signed-off-by: Karuppiah Natarajan --- cmd/helm/repo_add.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/helm/repo_add.go b/cmd/helm/repo_add.go index 1c84ad8d6..e6afce3d5 100644 --- a/cmd/helm/repo_add.go +++ b/cmd/helm/repo_add.go @@ -29,7 +29,7 @@ import ( "github.com/gofrs/flock" "github.com/pkg/errors" "github.com/spf13/cobra" - "gopkg.in/yaml.v2" + "sigs.k8s.io/yaml" "helm.sh/helm/v3/cmd/helm/require" "helm.sh/helm/v3/pkg/getter" From a1dcb3c2e453f778456ed600549a384605e1a692 Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Thu, 5 Dec 2019 15:48:02 -0500 Subject: [PATCH 41/46] Restoring fetch-dist and sign Make targets These make targets are used as part of the release process. They had yet to be brought over to the v3 branch from the v2 branch as they were developed after the branching happened. Signed-off-by: Matt Farina --- Makefile | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index e48a20fd5..611222e28 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,8 @@ -BINDIR := $(CURDIR)/bin -DIST_DIRS := find * -type d -exec -TARGETS := darwin/amd64 linux/amd64 linux/386 linux/arm linux/arm64 linux/ppc64le windows/amd64 -BINNAME ?= helm +BINDIR := $(CURDIR)/bin +DIST_DIRS := find * -type d -exec +TARGETS := darwin/amd64 linux/amd64 linux/386 linux/arm linux/arm64 linux/ppc64le windows/amd64 +TARGET_OBJS ?= darwin-amd64.tar.gz darwin-amd64.tar.gz.sha256 linux-amd64.tar.gz linux-amd64.tar.gz.sha256 linux-386.tar.gz linux-386.tar.gz.sha256 linux-arm.tar.gz linux-arm.tar.gz.sha256 linux-arm64.tar.gz linux-arm64.tar.gz.sha256 linux-ppc64le.tar.gz linux-ppc64le.tar.gz.sha256 windows-amd64.zip windows-amd64.zip.sha256 +BINNAME ?= helm GOPATH = $(shell go env GOPATH) DEP = $(GOPATH)/bin/dep @@ -138,6 +139,20 @@ dist: $(DIST_DIRS) zip -r helm-${VERSION}-{}.zip {} \; \ ) +.PHONY: fetch-dist +fetch-dist: + mkdir -p _dist + cd _dist && \ + for obj in ${TARGET_OBJS} ; do \ + curl -sSL -o helm-${VERSION}-$${obj} https://get.helm.sh/helm-${VERSION}-$${obj} ; \ + done + +.PHONY: sign +sign: + for f in _dist/*.{gz,zip,sha256} ; do \ + gpg --armor --detach-sign $${f} ; \ + done + .PHONY: checksum checksum: for f in _dist/*.{gz,zip} ; do \ From 361db773881415398ce05637f9f56bd6f2820c85 Mon Sep 17 00:00:00 2001 From: Robert Brennan Date: Thu, 5 Dec 2019 16:45:53 -0500 Subject: [PATCH 42/46] Fix godoc badge Signed-off-by: Robert Brennan --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bb1e40ca6..73fd189d7 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![CircleCI](https://circleci.com/gh/helm/helm.svg?style=shield)](https://circleci.com/gh/helm/helm) [![Go Report Card](https://goreportcard.com/badge/github.com/helm/helm)](https://goreportcard.com/report/github.com/helm/helm) -[![GoDoc](https://godoc.org/helm.sh/helm?status.svg)](https://godoc.org/helm.sh/helm) +[![GoDoc](https://img.shields.io/static/v1?label=godoc&message=reference&color=blue)](https://pkg.go.dev/helm.sh/helm/v3) [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/3131/badge)](https://bestpractices.coreinfrastructure.org/projects/3131) Helm is a tool for managing Charts. Charts are packages of pre-configured Kubernetes resources. From 36a8001e2adc0e5cf1bc3a06c75777a0b2ee15b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B5=B7=E7=9A=84=E6=BE=9C=E8=89=B2?= <33822635+zwwhdls@users.noreply.github.com> Date: Fri, 6 Dec 2019 17:32:12 +0800 Subject: [PATCH 43/46] fix this inconsistency in the docs (#7157) Signed-off-by: zwwhdls --- cmd/helm/package.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/cmd/helm/package.go b/cmd/helm/package.go index dd4bb2d17..f82c0950d 100644 --- a/cmd/helm/package.go +++ b/cmd/helm/package.go @@ -36,9 +36,6 @@ This command packages a chart into a versioned chart archive file. If a path is given, this will look at that path for a chart (which must contain a Chart.yaml file) and then package that directory. -If no path is given, this will look in the present working directory for a -Chart.yaml file, and (if found) build the current directory into a chart. - Versioned chart archives are used by Helm package repositories. ` From 0e42a77ae6ae266cfa299de3b3bfd204f35b9f7a Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Fri, 6 Dec 2019 09:14:39 -0700 Subject: [PATCH 44/46] improved the error message for failed package signing (#6948) Signed-off-by: Matt Butcher --- cmd/helm/package.go | 8 ++++++++ pkg/provenance/sign.go | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/cmd/helm/package.go b/cmd/helm/package.go index f82c0950d..9f7961f95 100644 --- a/cmd/helm/package.go +++ b/cmd/helm/package.go @@ -37,6 +37,14 @@ is given, this will look at that path for a chart (which must contain a Chart.yaml file) and then package that directory. Versioned chart archives are used by Helm package repositories. + +To sign a chart, use the '--sign' flag. In most cases, you should also +provide '--keyring path/to/secret/keys' and '--key keyname'. + + $ helm package --sign ./mychart --key mykey --keyring ~/.gnupg/secring.gpg + +If '--keyring' is not specified, Helm usually defaults to the public keyring +unless your environment is otherwise configured. ` func newPackageCmd(out io.Writer) *cobra.Command { diff --git a/pkg/provenance/sign.go b/pkg/provenance/sign.go index 6032eb063..5d16779f1 100644 --- a/pkg/provenance/sign.go +++ b/pkg/provenance/sign.go @@ -169,7 +169,7 @@ func (s *Signatory) DecryptKey(fn PassphraseFetcher) error { if s.Entity == nil { return errors.New("private key not found") } else if s.Entity.PrivateKey == nil { - return errors.New("provided key is not a private key") + return errors.New("provided key is not a private key. Try providing a keyring with secret keys") } // Nothing else to do if key is not encrypted. @@ -203,7 +203,7 @@ func (s *Signatory) ClearSign(chartpath string) (string, error) { if s.Entity == nil { return "", errors.New("private key not found") } else if s.Entity.PrivateKey == nil { - return "", errors.New("provided key is not a private key") + return "", errors.New("provided key is not a private key. Try providing a keyring with secret keys") } if fi, err := os.Stat(chartpath); err != nil { From e47c67c427e9831d3df20befc7fa139404829ddc Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Fri, 6 Dec 2019 09:14:51 -0700 Subject: [PATCH 45/46] fix: clarified behavior of 'list --deleted' (#6950) Signed-off-by: Matt Butcher --- cmd/helm/list.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/helm/list.go b/cmd/helm/list.go index 83edfe156..0da0c21d3 100644 --- a/cmd/helm/list.go +++ b/cmd/helm/list.go @@ -97,7 +97,7 @@ func newListCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { f.BoolVarP(&client.ByDate, "date", "d", false, "sort by release date") f.BoolVarP(&client.SortReverse, "reverse", "r", false, "reverse the sort order") f.BoolVarP(&client.All, "all", "a", false, "show all releases, not just the ones marked deployed or failed") - f.BoolVar(&client.Uninstalled, "uninstalled", false, "show uninstalled releases") + f.BoolVar(&client.Uninstalled, "uninstalled", false, "show uninstalled releases (if 'helm uninstall --keep-history' was used)") f.BoolVar(&client.Superseded, "superseded", false, "show superseded releases") f.BoolVar(&client.Uninstalling, "uninstalling", false, "show releases that are currently being uninstalled") f.BoolVar(&client.Deployed, "deployed", false, "show deployed releases. If no other is specified, this will be automatically enabled") From cb81b07125585309d0cf5a17ce57dcd10429d8a1 Mon Sep 17 00:00:00 2001 From: Fabian Ruff Date: Fri, 22 Nov 2019 16:27:08 +0100 Subject: [PATCH 46/46] Reintroduce --is-upgrade to template command This change allows correct rendering of manifests for the upgrade case using `helm template`. Signed-off-by: Fabian Ruff --- cmd/helm/template.go | 1 + pkg/action/install.go | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/cmd/helm/template.go b/cmd/helm/template.go index b660cc69f..a47631c0d 100644 --- a/cmd/helm/template.go +++ b/cmd/helm/template.go @@ -129,6 +129,7 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { f.StringVar(&client.OutputDir, "output-dir", "", "writes the executed templates to files in output-dir instead of stdout") f.BoolVar(&validate, "validate", false, "validate your manifests against the Kubernetes cluster you are currently pointing at. This is the same validation performed on an install") f.BoolVar(&includeCrds, "include-crds", false, "include CRDs in the templated output") + f.BoolVar(&client.IsUpgrade, "is-upgrade", false, "set .Release.IsUpgrade instead of .Release.IsInstall") f.StringArrayVarP(&extraAPIs, "api-versions", "a", []string{}, "Kubernetes api versions used for Capabilities.APIVersions") return cmd diff --git a/pkg/action/install.go b/pkg/action/install.go index 8cd270693..b08f2fb67 100644 --- a/pkg/action/install.go +++ b/pkg/action/install.go @@ -86,6 +86,8 @@ type Install struct { // APIVersions allows a manual set of supported API Versions to be passed // (for things like templating). These are ignored if ClientOnly is false APIVersions chartutil.VersionSet + // Used by helm template to render charts with .Relase.IsUpgrade. Ignored if Dry-Run is false + IsUpgrade bool } // ChartPathOptions captures common options used for controlling chart paths @@ -197,11 +199,14 @@ func (i *Install) Run(chrt *chart.Chart, vals map[string]interface{}) (*release. return nil, err } + //special case for helm template --is-upgrade + isUpgrade := i.IsUpgrade && i.DryRun options := chartutil.ReleaseOptions{ Name: i.ReleaseName, Namespace: i.Namespace, Revision: 1, - IsInstall: true, + IsInstall: !isUpgrade, + IsUpgrade: isUpgrade, } valuesToRender, err := chartutil.ToRenderValues(chrt, vals, options, caps) if err != nil { @@ -237,7 +242,7 @@ func (i *Install) Run(chrt *chart.Chart, vals map[string]interface{}) (*release. // we'll end up in a state where we will delete those resources upon // deleting the release because the manifest will be pointing at that // resource - if !i.ClientOnly { + if !i.ClientOnly && !isUpgrade { if err := existingResourceConflict(resources); err != nil { return nil, errors.Wrap(err, "rendered manifests contain a resource that already exists. Unable to continue with install") }