From 36c9c0e5202789f0f2c87fabf816900259de0fb8 Mon Sep 17 00:00:00 2001 From: Simon Alling Date: Tue, 19 Nov 2019 08:41:15 +0100 Subject: [PATCH 001/281] Simplify chart installable check The error conveys at least as much information as the boolean. Signed-off-by: Simon Alling --- cmd/helm/install.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/cmd/helm/install.go b/cmd/helm/install.go index 40935db17..4255125ae 100644 --- a/cmd/helm/install.go +++ b/cmd/helm/install.go @@ -176,8 +176,7 @@ func runInstall(args []string, client *action.Install, valueOpts *values.Options return nil, err } - validInstallableChart, err := isChartInstallable(chartRequested) - if !validInstallableChart { + if err := checkIfInstallable(chartRequested); err != nil { return nil, err } @@ -209,13 +208,13 @@ func runInstall(args []string, client *action.Install, valueOpts *values.Options return client.Run(chartRequested, vals) } -// isChartInstallable validates if a chart can be installed +// checkIfInstallable validates if a chart can be installed // // Application chart type is only installable -func isChartInstallable(ch *chart.Chart) (bool, error) { +func checkIfInstallable(ch *chart.Chart) error { switch ch.Metadata.Type { case "", "application": - return true, nil + return nil } - return false, errors.Errorf("%s charts are not installable", ch.Metadata.Type) + return errors.Errorf("%s charts are not installable", ch.Metadata.Type) } From 451e2158ad24594d8c67e2e4a99aefc5ffa84a9f Mon Sep 17 00:00:00 2001 From: Nguyen Hai Truong Date: Mon, 6 Jan 2020 22:54:47 +0700 Subject: [PATCH 002/281] Remove duplicated words (#7336) Although it is spelling mistakes, it might make an affects while reading. Signed-off-by: Nguyen Hai Truong Signed-off-by: EItanya --- internal/third_party/dep/fs/fs_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/third_party/dep/fs/fs_test.go b/internal/third_party/dep/fs/fs_test.go index bf4b803f8..a9678d8c1 100644 --- a/internal/third_party/dep/fs/fs_test.go +++ b/internal/third_party/dep/fs/fs_test.go @@ -170,7 +170,7 @@ func TestCopyDir(t *testing.T) { func TestCopyDirFail_SrcInaccessible(t *testing.T) { if runtime.GOOS == "windows" { // XXX: setting permissions works differently in - // Microsoft Windows. Skipping this this until a + // Microsoft Windows. Skipping this until a // compatible implementation is provided. t.Skip("skipping on windows") } @@ -209,7 +209,7 @@ func TestCopyDirFail_SrcInaccessible(t *testing.T) { func TestCopyDirFail_DstInaccessible(t *testing.T) { if runtime.GOOS == "windows" { // XXX: setting permissions works differently in - // Microsoft Windows. Skipping this this until a + // Microsoft Windows. Skipping this until a // compatible implementation is provided. t.Skip("skipping on windows") } @@ -309,7 +309,7 @@ func TestCopyDirFailOpen(t *testing.T) { // Microsoft Windows. os.Chmod(..., 0222) below is not // enough for the file to be readonly, and os.Chmod(..., // 0000) returns an invalid argument error. Skipping - // this this until a compatible implementation is + // this until a compatible implementation is // provided. t.Skip("skipping on windows") } @@ -478,7 +478,7 @@ func TestCopyFileSymlink(t *testing.T) { func TestCopyFileFail(t *testing.T) { if runtime.GOOS == "windows" { // XXX: setting permissions works differently in - // Microsoft Windows. Skipping this this until a + // Microsoft Windows. Skipping this until a // compatible implementation is provided. t.Skip("skipping on windows") } @@ -521,7 +521,7 @@ func TestCopyFileFail(t *testing.T) { } // setupInaccessibleDir creates a temporary location with a single -// directory in it, in such a way that that directory is not accessible +// directory in it, in such a way that directory is not accessible // after this function returns. // // op is called with the directory as argument, so that it can create From b7ff1e29327d33b6fe9b44b11d84c182d1adea45 Mon Sep 17 00:00:00 2001 From: "Jorge I. Gasca" <42793610+jorge-gasca@users.noreply.github.com> Date: Tue, 7 Jan 2020 12:25:15 -0500 Subject: [PATCH 003/281] fix(cmd): Fixes logging on action conf init error (#6909) * fix(cmd): Fixes logging on action conf init error Errors relating to initializing the action config cause helm to exit silently, except when in debug mode. This now emits the useful error. Closes #6863 Signed-off-by: Jorge Gasca * Remove unnecessary formatting of err struct Signed-off-by: Jorge Gasca Signed-off-by: EItanya --- cmd/helm/helm.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index 2c3c9e401..112d5123f 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -68,8 +68,7 @@ func main() { cmd := newRootCmd(actionConfig, os.Stdout, os.Args[1:]) if err := actionConfig.Init(settings.RESTClientGetter(), settings.Namespace(), os.Getenv("HELM_DRIVER"), debug); err != nil { - debug("%+v", err) - os.Exit(1) + log.Fatal(err) } if err := cmd.Execute(); err != nil { From 00769c4512c9c145eafb84f60e8a473de83b0209 Mon Sep 17 00:00:00 2001 From: Hu Shuai Date: Wed, 8 Jan 2020 17:19:00 +0800 Subject: [PATCH 004/281] Fix a typo "update" -> "updates" (#7346) Signed-off-by: Hu Shuai Signed-off-by: EItanya --- pkg/storage/storage.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go index 58a7eb06f..6528c48ba 100644 --- a/pkg/storage/storage.go +++ b/pkg/storage/storage.go @@ -66,7 +66,7 @@ func (s *Storage) Create(rls *rspb.Release) error { return s.Driver.Create(makeKey(rls.Name, rls.Version), rls) } -// Update update the release in storage. An error is returned if the +// Update updates the release in storage. An error is returned if the // storage backend fails to update the release or if the release // does not exist. func (s *Storage) Update(rls *rspb.Release) error { From 17dc43f0547d4fd7205de07428b97fbc4a5276fd Mon Sep 17 00:00:00 2001 From: EItanya Date: Wed, 8 Jan 2020 13:21:31 -0500 Subject: [PATCH 005/281] added config file string Signed-off-by: EItanya --- cmd/helm/root.go | 1 + internal/experimental/registry/client.go | 18 +++++++++++------- internal/experimental/registry/client_opts.go | 7 +++++++ 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/cmd/helm/root.go b/cmd/helm/root.go index 3405299fd..ea0654c4b 100644 --- a/cmd/helm/root.go +++ b/cmd/helm/root.go @@ -416,6 +416,7 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string registryClient, err := registry.NewClient( registry.ClientOptDebug(settings.Debug), registry.ClientOptWriter(out), + registry.ClientOptCredentialsFile(settings.RepositoryConfig), ) if err != nil { // TODO: don't panic here, refactor newRootCmd to return error diff --git a/internal/experimental/registry/client.go b/internal/experimental/registry/client.go index d52d9f3e0..48d4d9ecc 100644 --- a/internal/experimental/registry/client.go +++ b/internal/experimental/registry/client.go @@ -41,11 +41,13 @@ const ( type ( // Client works with OCI-compliant registries and local Helm chart cache Client struct { - debug bool - out io.Writer - authorizer *Authorizer - resolver *Resolver - cache *Cache + debug bool + // path to repository config file e.g. ~/.docker/config.json + credentialsFile string + out io.Writer + authorizer *Authorizer + resolver *Resolver + cache *Cache } ) @@ -58,9 +60,11 @@ func NewClient(opts ...ClientOption) (*Client, error) { opt(client) } // set defaults if fields are missing + if client.credentialsFile == "" { + client.credentialsFile = helmpath.CachePath("registry", CredentialsFileBasename) + } if client.authorizer == nil { - credentialsFile := helmpath.CachePath("registry", CredentialsFileBasename) - authClient, err := auth.NewClient(credentialsFile) + authClient, err := auth.NewClient(client.credentialsFile) if err != nil { return nil, err } diff --git a/internal/experimental/registry/client_opts.go b/internal/experimental/registry/client_opts.go index cd295813a..76b527492 100644 --- a/internal/experimental/registry/client_opts.go +++ b/internal/experimental/registry/client_opts.go @@ -60,3 +60,10 @@ func ClientOptCache(cache *Cache) ClientOption { client.cache = cache } } + +// ClientOptCache returns a function that sets the cache setting on a client options set +func ClientOptCredentialsFile(credentialsFile string) ClientOption { + return func(client *Client) { + client.credentialsFile = credentialsFile + } +} From 2f534f97424ce4eaa3356e199e453e94bd65b4c6 Mon Sep 17 00:00:00 2001 From: EItanya Date: Wed, 8 Jan 2020 14:00:51 -0500 Subject: [PATCH 006/281] fix test Signed-off-by: EItanya --- cmd/helm/root.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/helm/root.go b/cmd/helm/root.go index ea0654c4b..1cee11458 100644 --- a/cmd/helm/root.go +++ b/cmd/helm/root.go @@ -416,7 +416,7 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string registryClient, err := registry.NewClient( registry.ClientOptDebug(settings.Debug), registry.ClientOptWriter(out), - registry.ClientOptCredentialsFile(settings.RepositoryConfig), + registry.ClientOptCredentialsFile(settings.RegistryConfig), ) if err != nil { // TODO: don't panic here, refactor newRootCmd to return error From 640d527190376a8b4945575f767065ebd47a83cb Mon Sep 17 00:00:00 2001 From: EItanya Date: Wed, 8 Jan 2020 14:03:06 -0500 Subject: [PATCH 007/281] removed panic, and replaced with error Signed-off-by: EItanya --- cmd/helm/helm.go | 5 ++++- cmd/helm/helm_test.go | 6 +++++- cmd/helm/root.go | 7 +++---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index 112d5123f..952719623 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -65,7 +65,10 @@ func main() { initKubeLogs() actionConfig := new(action.Configuration) - cmd := newRootCmd(actionConfig, os.Stdout, os.Args[1:]) + cmd, err := newRootCmd(actionConfig, os.Stdout, os.Args[1:]) + if err != nil { + log.Fatal(err) + } if err := actionConfig.Init(settings.RESTClientGetter(), settings.Namespace(), os.Getenv("HELM_DRIVER"), debug); err != nil { log.Fatal(err) diff --git a/cmd/helm/helm_test.go b/cmd/helm/helm_test.go index 924e8e9d3..20e7ed3e9 100644 --- a/cmd/helm/helm_test.go +++ b/cmd/helm/helm_test.go @@ -107,7 +107,11 @@ func executeActionCommandC(store *storage.Storage, cmd string) (*cobra.Command, Log: func(format string, v ...interface{}) {}, } - root := newRootCmd(actionConfig, buf, args) + root, err := newRootCmd(actionConfig, buf, args) + if err != nil { + return nil, "", err + } + root.SetOutput(buf) root.SetArgs(args) diff --git a/cmd/helm/root.go b/cmd/helm/root.go index 1cee11458..45fbe544d 100644 --- a/cmd/helm/root.go +++ b/cmd/helm/root.go @@ -345,7 +345,7 @@ By default, the default directories depend on the Operating System. The defaults +------------------+---------------------------+--------------------------------+-------------------------+ ` -func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string) *cobra.Command { +func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string) (*cobra.Command, error) { cmd := &cobra.Command{ Use: "helm", Short: "The Helm package manager for Kubernetes.", @@ -419,8 +419,7 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string registry.ClientOptCredentialsFile(settings.RegistryConfig), ) if err != nil { - // TODO: don't panic here, refactor newRootCmd to return error - panic(err) + return nil, err } actionConfig.RegistryClient = registryClient cmd.AddCommand( @@ -431,5 +430,5 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string // Find and add plugins loadPlugins(cmd, out) - return cmd + return cmd, nil } From f604105547dc260f4388792de44a6eb9d2a1353f Mon Sep 17 00:00:00 2001 From: EItanya Date: Wed, 8 Jan 2020 14:11:06 -0500 Subject: [PATCH 008/281] fixed to mirror master Signed-off-by: EItanya --- cmd/helm/helm.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index 952719623..ead4a2c49 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -67,11 +67,13 @@ func main() { actionConfig := new(action.Configuration) cmd, err := newRootCmd(actionConfig, os.Stdout, os.Args[1:]) if err != nil { - log.Fatal(err) + debug("%+v", err) + os.Exit(1) } if err := actionConfig.Init(settings.RESTClientGetter(), settings.Namespace(), os.Getenv("HELM_DRIVER"), debug); err != nil { - log.Fatal(err) + debug("%+v", err) + os.Exit(1) } if err := cmd.Execute(); err != nil { From de1996e500c75faccbb8aadc1d2e8b788f83ac3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Lindh=C3=A9?= Date: Fri, 15 Nov 2019 12:45:47 +0100 Subject: [PATCH 009/281] Add comments about release Version variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I was looking into the `get` command, and got tripped up by the `Version` variable. It was unclear to me what Version represents, since it's called REVISION when doing e.g., `helm list`. But even after knowing this, it was not very clear to me why we (implicitly) set the Version variable to 0 but never seem to use it. `mhickey` explained to me on Slack that this gets the latest revision of the release. Makes sense, but I added a comment about that too, to clarify. Signed-off-by: Andreas Lindhé --- pkg/action/get.go | 1 + pkg/release/release.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/action/get.go b/pkg/action/get.go index c776eb696..f44b53307 100644 --- a/pkg/action/get.go +++ b/pkg/action/get.go @@ -26,6 +26,7 @@ import ( type Get struct { cfg *Configuration + // Initializing Version to 0 will get the latest revision of the release. Version int } diff --git a/pkg/release/release.go b/pkg/release/release.go index a436998aa..8582a86f3 100644 --- a/pkg/release/release.go +++ b/pkg/release/release.go @@ -33,7 +33,7 @@ type Release struct { Manifest string `json:"manifest,omitempty"` // Hooks are all of the hooks declared for this release. Hooks []*Hook `json:"hooks,omitempty"` - // Version is an int which represents the version of the release. + // Version is an int which represents the revision of the release. Version int `json:"version,omitempty"` // Namespace is the kubernetes namespace of the release. Namespace string `json:"namespace,omitempty"` From b9a8e98bcafd6482dbb540c9ab189e2d246bf9ca Mon Sep 17 00:00:00 2001 From: Naseem Date: Sat, 11 Jan 2020 00:38:55 -0500 Subject: [PATCH 010/281] Add hpa boilerplate Signed-off-by: Naseem --- cmd/helm/create_test.go | 4 ++-- pkg/chartutil/create.go | 46 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/cmd/helm/create_test.go b/cmd/helm/create_test.go index 0a9b7b9a5..bbb8d394a 100644 --- a/cmd/helm/create_test.go +++ b/cmd/helm/create_test.go @@ -106,7 +106,7 @@ func TestCreateStarterCmd(t *testing.T) { t.Errorf("Wrong API version: %q", c.Metadata.APIVersion) } - expectedNumberOfTemplates := 8 + expectedNumberOfTemplates := 9 if l := len(c.Templates); l != expectedNumberOfTemplates { t.Errorf("Expected %d templates, got %d", expectedNumberOfTemplates, l) } @@ -174,7 +174,7 @@ func TestCreateStarterAbsoluteCmd(t *testing.T) { t.Errorf("Wrong API version: %q", c.Metadata.APIVersion) } - expectedNumberOfTemplates := 8 + expectedNumberOfTemplates := 9 if l := len(c.Templates); l != expectedNumberOfTemplates { t.Errorf("Expected %d templates, got %d", expectedNumberOfTemplates, l) } diff --git a/pkg/chartutil/create.go b/pkg/chartutil/create.go index 390f12f4c..90bf4ec48 100644 --- a/pkg/chartutil/create.go +++ b/pkg/chartutil/create.go @@ -53,6 +53,8 @@ const ( ServiceName = TemplatesDir + sep + "service.yaml" // ServiceAccountName is the name of the example serviceaccount file. ServiceAccountName = TemplatesDir + sep + "serviceaccount.yaml" + // HorizontalPodAutoscalerName is the name of the example hpa file. + HorizontalPodAutoscalerName = TemplatesDir + sep + "hpa.yaml" // NotesName is the name of the example NOTES.txt file. NotesName = TemplatesDir + sep + "NOTES.txt" // HelpersName is the name of the example helpers file. @@ -149,6 +151,13 @@ resources: {} # cpu: 100m # memory: 128Mi +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + nodeSelector: {} tolerations: [] @@ -231,7 +240,9 @@ metadata: labels: {{- include ".labels" . | nindent 4 }} spec: +{{- if not .Values.autoscaling.enabled }} replicas: {{ .Values.replicaCount }} +{{- end }} selector: matchLabels: {{- include ".selectorLabels" . | nindent 6 }} @@ -312,6 +323,36 @@ metadata: {{- end -}} ` +const defaultHorizontalPodAutoscaler = `{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2beta1 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include ".fullname" . }} + labels: + {{- include ".labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include ".fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} +` + const defaultNotes = `1. Get the application URL by running these commands: {{- if .Values.ingress.enabled }} {{- range $host := .Values.ingress.hosts }} @@ -517,6 +558,11 @@ func Create(name, dir string) (string, error) { path: filepath.Join(cdir, ServiceAccountName), content: transform(defaultServiceAccount, name), }, + { + // hpa.yaml + path: filepath.Join(cdir, HorizontalPodAutoscalerName), + content: transform(defaultHorizontalPodAutoscaler, name), + }, { // NOTES.txt path: filepath.Join(cdir, NotesName), From 9a2ff7802ff84fcc260c0eff940fa0af3ea91137 Mon Sep 17 00:00:00 2001 From: Daniel Strobusch <1847260+dastrobu@users.noreply.github.com> Date: Thu, 16 Jan 2020 21:43:17 +0100 Subject: [PATCH 011/281] fix(helm): sort hooks by kind for equal weight Use the same install order for hooks as for normal resources (non-hooks) for hooks with equal weight. This makes resource handling more consistent and helps, when there are hook consisting of several resources like e.g. a service account and a job using this service account. The sort functions are changed from an in place search to an out of place sort to avoid inout parameters. Closes #7416. Signed-off-by: Daniel Strobusch <1847260+dastrobu@users.noreply.github.com> --- pkg/action/hooks.go | 3 +- pkg/releaseutil/kind_sorter.go | 78 +++++++++++++++++++------ pkg/releaseutil/kind_sorter_test.go | 53 ++++++++++++++++- pkg/releaseutil/manifest_sorter.go | 2 +- pkg/releaseutil/manifest_sorter_test.go | 2 +- 5 files changed, 114 insertions(+), 24 deletions(-) diff --git a/pkg/action/hooks.go b/pkg/action/hooks.go index a161f9377..40c1ffdb6 100644 --- a/pkg/action/hooks.go +++ b/pkg/action/hooks.go @@ -38,7 +38,8 @@ func (cfg *Configuration) execHook(rl *release.Release, hook release.HookEvent, } } - sort.Sort(hookByWeight(executingHooks)) + // hooke are pre-ordered by kind, so keep order stable + sort.Stable(hookByWeight(executingHooks)) for _, h := range executingHooks { // Set default delete policy to before-hook-creation diff --git a/pkg/releaseutil/kind_sorter.go b/pkg/releaseutil/kind_sorter.go index 92ffa03f2..3f47ea55d 100644 --- a/pkg/releaseutil/kind_sorter.go +++ b/pkg/releaseutil/kind_sorter.go @@ -16,7 +16,11 @@ limitations under the License. package releaseutil -import "sort" +import ( + "sort" + + "helm.sh/helm/v3/pkg/release" +) // KindSortOrder is an ordering of Kinds. type KindSortOrder []string @@ -99,46 +103,84 @@ var UninstallOrder KindSortOrder = []string{ "Namespace", } -// sortByKind does an in-place sort of manifests by Kind. +// sort manifests by kind (out of place sort) // // Results are sorted by 'ordering', keeping order of items with equal kind/priority -func sortByKind(manifests []Manifest, ordering KindSortOrder) []Manifest { - ks := newKindSorter(manifests, ordering) +func manifestsSortedByKind(manifests []Manifest, ordering KindSortOrder) []Manifest { + k := make([]string, len(manifests)) + for i, h := range manifests { + k[i] = h.Head.Kind + } + ks := newKindSorter(k, ordering) sort.Stable(ks) - return ks.manifests + + // apply permutation + sorted := make([]Manifest, len(manifests)) + for i, p := range ks.permutation { + sorted[i] = manifests[p] + } + return sorted +} + +// sort hooks by kind (out of place sort) +// +// Results are sorted by 'ordering', keeping order of items with equal kind/priority +func hooksSortedByKind(hooks []*release.Hook, ordering KindSortOrder) []*release.Hook { + k := make([]string, len(hooks)) + for i, h := range hooks { + k[i] = h.Kind + } + + ks := newKindSorter(k, ordering) + sort.Stable(ks) + + // apply permutation + sorted := make([]*release.Hook, len(hooks)) + for i, p := range ks.permutation { + sorted[i] = hooks[p] + } + return sorted } type kindSorter struct { - ordering map[string]int - manifests []Manifest + permutation []int + ordering map[string]int + kinds []string } -func newKindSorter(m []Manifest, s KindSortOrder) *kindSorter { +func newKindSorter(kinds []string, s KindSortOrder) *kindSorter { o := make(map[string]int, len(s)) for v, k := range s { o[k] = v } + p := make([]int, len(kinds)) + for i := range p { + p[i] = i + } return &kindSorter{ - manifests: m, - ordering: o, + permutation: p, + kinds: kinds, + ordering: o, } } -func (k *kindSorter) Len() int { return len(k.manifests) } +func (k *kindSorter) Len() int { return len(k.kinds) } -func (k *kindSorter) Swap(i, j int) { k.manifests[i], k.manifests[j] = k.manifests[j], k.manifests[i] } +func (k *kindSorter) Swap(i, j int) { + k.permutation[i], k.permutation[j] = k.permutation[j], k.permutation[i] +} func (k *kindSorter) Less(i, j int) bool { - a := k.manifests[i] - b := k.manifests[j] - first, aok := k.ordering[a.Head.Kind] - second, bok := k.ordering[b.Head.Kind] + a := k.kinds[k.permutation[i]] + b := k.kinds[k.permutation[j]] + first, aok := k.ordering[a] + second, bok := k.ordering[b] if !aok && !bok { // if both are unknown then sort alphabetically by kind, keep original order if same kind - if a.Head.Kind != b.Head.Kind { - return a.Head.Kind < b.Head.Kind + if a != b { + return a < b } return first < second } diff --git a/pkg/releaseutil/kind_sorter_test.go b/pkg/releaseutil/kind_sorter_test.go index 4747e8252..777813667 100644 --- a/pkg/releaseutil/kind_sorter_test.go +++ b/pkg/releaseutil/kind_sorter_test.go @@ -19,6 +19,8 @@ package releaseutil import ( "bytes" "testing" + + "helm.sh/helm/v3/pkg/release" ) func TestKindSorter(t *testing.T) { @@ -175,7 +177,7 @@ func TestKindSorter(t *testing.T) { t.Fatalf("Expected %d names in order, got %d", want, got) } defer buf.Reset() - for _, r := range sortByKind(manifests, test.order) { + for _, r := range manifestsSortedByKind(manifests, test.order) { buf.WriteString(r.Name) } if got := buf.String(); got != test.expected { @@ -236,7 +238,7 @@ func TestKindSorterKeepOriginalOrder(t *testing.T) { var buf bytes.Buffer t.Run(test.description, func(t *testing.T) { defer buf.Reset() - for _, r := range sortByKind(manifests, test.order) { + for _, r := range manifestsSortedByKind(manifests, test.order) { buf.WriteString(r.Name) } if got := buf.String(); got != test.expected { @@ -257,7 +259,7 @@ func TestKindSorterNamespaceAgainstUnknown(t *testing.T) { } manifests := []Manifest{unknown, namespace} - sortByKind(manifests, InstallOrder) + manifests = manifestsSortedByKind(manifests, InstallOrder) expectedOrder := []Manifest{namespace, unknown} for i, manifest := range manifests { @@ -266,3 +268,48 @@ func TestKindSorterNamespaceAgainstUnknown(t *testing.T) { } } } + +// test hook sorting with a small subset of kinds, since it uses the same algorithm as manifestsSortedByKind +func TestKindSorterForHooks(t *testing.T) { + hooks := []*release.Hook{ + { + Name: "i", + Kind: "ClusterRole", + }, + { + Name: "j", + Kind: "ClusterRoleBinding", + }, + { + Name: "c", + Kind: "LimitRange", + }, + { + Name: "a", + Kind: "Namespace", + }, + } + + for _, test := range []struct { + description string + order KindSortOrder + expected string + }{ + {"install", InstallOrder, "acij"}, + {"uninstall", UninstallOrder, "jica"}, + } { + var buf bytes.Buffer + t.Run(test.description, func(t *testing.T) { + if got, want := len(test.expected), len(hooks); got != want { + t.Fatalf("Expected %d names in order, got %d", want, got) + } + defer buf.Reset() + for _, r := range hooksSortedByKind(hooks, test.order) { + buf.WriteString(r.Name) + } + if got := buf.String(); got != test.expected { + t.Errorf("Expected %q, got %q", test.expected, got) + } + }) + } +} diff --git a/pkg/releaseutil/manifest_sorter.go b/pkg/releaseutil/manifest_sorter.go index 24b0c3c95..454977b00 100644 --- a/pkg/releaseutil/manifest_sorter.go +++ b/pkg/releaseutil/manifest_sorter.go @@ -108,7 +108,7 @@ func SortManifests(files map[string]string, apis chartutil.VersionSet, ordering } } - return result.hooks, sortByKind(result.generic, ordering), nil + return hooksSortedByKind(result.hooks, ordering), manifestsSortedByKind(result.generic, ordering), nil } // sort takes a manifestFile object which may contain multiple resource definition diff --git a/pkg/releaseutil/manifest_sorter_test.go b/pkg/releaseutil/manifest_sorter_test.go index 0d2d6660a..29b0d85cf 100644 --- a/pkg/releaseutil/manifest_sorter_test.go +++ b/pkg/releaseutil/manifest_sorter_test.go @@ -219,7 +219,7 @@ metadata: } } - sorted = sortByKind(sorted, InstallOrder) + sorted = manifestsSortedByKind(sorted, InstallOrder) for i, m := range generic { if m.Content != sorted[i].Content { t.Errorf("Expected %q, got %q", m.Content, sorted[i].Content) From 671ceb5514900cbbd40fffc4691710f044aa73c4 Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Thu, 30 Jan 2020 15:12:16 -0800 Subject: [PATCH 012/281] ref(kind_sorter): use an in-place sort for sortManifestsByKind, reduce code duplication Signed-off-by: Matthew Fisher --- pkg/releaseutil/kind_sorter.go | 84 +++++++------------------ pkg/releaseutil/kind_sorter_test.go | 22 +++++-- pkg/releaseutil/manifest_sorter.go | 2 +- pkg/releaseutil/manifest_sorter_test.go | 2 +- 4 files changed, 40 insertions(+), 70 deletions(-) diff --git a/pkg/releaseutil/kind_sorter.go b/pkg/releaseutil/kind_sorter.go index 3f47ea55d..5b131b3b0 100644 --- a/pkg/releaseutil/kind_sorter.go +++ b/pkg/releaseutil/kind_sorter.go @@ -103,84 +103,42 @@ var UninstallOrder KindSortOrder = []string{ "Namespace", } -// sort manifests by kind (out of place sort) +// sort manifests by kind. // // Results are sorted by 'ordering', keeping order of items with equal kind/priority -func manifestsSortedByKind(manifests []Manifest, ordering KindSortOrder) []Manifest { - k := make([]string, len(manifests)) - for i, h := range manifests { - k[i] = h.Head.Kind - } - ks := newKindSorter(k, ordering) - sort.Stable(ks) +func sortManifestsByKind(manifests []Manifest, ordering KindSortOrder) []Manifest { + sort.SliceStable(manifests, func(i, j int) bool { + return lessByKind(manifests[i], manifests[j], manifests[i].Head.Kind, manifests[j].Head.Kind, ordering) + }) - // apply permutation - sorted := make([]Manifest, len(manifests)) - for i, p := range ks.permutation { - sorted[i] = manifests[p] - } - return sorted + return manifests } -// sort hooks by kind (out of place sort) +// sort hooks by kind, using an out-of-place sort to preserve the input parameters. // // Results are sorted by 'ordering', keeping order of items with equal kind/priority -func hooksSortedByKind(hooks []*release.Hook, ordering KindSortOrder) []*release.Hook { - k := make([]string, len(hooks)) - for i, h := range hooks { - k[i] = h.Kind - } - - ks := newKindSorter(k, ordering) - sort.Stable(ks) - - // apply permutation - sorted := make([]*release.Hook, len(hooks)) - for i, p := range ks.permutation { - sorted[i] = hooks[p] - } - return sorted -} +func sortHooksByKind(hooks []*release.Hook, ordering KindSortOrder) []*release.Hook { + h := hooks + sort.SliceStable(h, func(i, j int) bool { + return lessByKind(h[i], h[j], h[i].Kind, h[j].Kind, ordering) + }) -type kindSorter struct { - permutation []int - ordering map[string]int - kinds []string + return h } -func newKindSorter(kinds []string, s KindSortOrder) *kindSorter { - o := make(map[string]int, len(s)) - for v, k := range s { - o[k] = v +func lessByKind(a interface{}, b interface{}, kindA string, kindB string, o KindSortOrder) bool { + ordering := make(map[string]int, len(o)) + for v, k := range o { + ordering[k] = v } - p := make([]int, len(kinds)) - for i := range p { - p[i] = i - } - return &kindSorter{ - permutation: p, - kinds: kinds, - ordering: o, - } -} - -func (k *kindSorter) Len() int { return len(k.kinds) } - -func (k *kindSorter) Swap(i, j int) { - k.permutation[i], k.permutation[j] = k.permutation[j], k.permutation[i] -} - -func (k *kindSorter) Less(i, j int) bool { - a := k.kinds[k.permutation[i]] - b := k.kinds[k.permutation[j]] - first, aok := k.ordering[a] - second, bok := k.ordering[b] + first, aok := ordering[kindA] + second, bok := ordering[kindB] if !aok && !bok { // if both are unknown then sort alphabetically by kind, keep original order if same kind - if a != b { - return a < b + if kindA != kindB { + return kindA < kindB } return first < second } diff --git a/pkg/releaseutil/kind_sorter_test.go b/pkg/releaseutil/kind_sorter_test.go index 777813667..341f528a0 100644 --- a/pkg/releaseutil/kind_sorter_test.go +++ b/pkg/releaseutil/kind_sorter_test.go @@ -177,12 +177,18 @@ func TestKindSorter(t *testing.T) { t.Fatalf("Expected %d names in order, got %d", want, got) } defer buf.Reset() - for _, r := range manifestsSortedByKind(manifests, test.order) { + orig := manifests + for _, r := range sortManifestsByKind(manifests, test.order) { buf.WriteString(r.Name) } if got := buf.String(); got != test.expected { t.Errorf("Expected %q, got %q", test.expected, got) } + for i, manifest := range orig { + if manifest != manifests[i] { + t.Fatal("Expected input to sortManifestsByKind to stay the same") + } + } }) } } @@ -238,7 +244,7 @@ func TestKindSorterKeepOriginalOrder(t *testing.T) { var buf bytes.Buffer t.Run(test.description, func(t *testing.T) { defer buf.Reset() - for _, r := range manifestsSortedByKind(manifests, test.order) { + for _, r := range sortManifestsByKind(manifests, test.order) { buf.WriteString(r.Name) } if got := buf.String(); got != test.expected { @@ -259,7 +265,7 @@ func TestKindSorterNamespaceAgainstUnknown(t *testing.T) { } manifests := []Manifest{unknown, namespace} - manifests = manifestsSortedByKind(manifests, InstallOrder) + manifests = sortManifestsByKind(manifests, InstallOrder) expectedOrder := []Manifest{namespace, unknown} for i, manifest := range manifests { @@ -269,7 +275,7 @@ func TestKindSorterNamespaceAgainstUnknown(t *testing.T) { } } -// test hook sorting with a small subset of kinds, since it uses the same algorithm as manifestsSortedByKind +// test hook sorting with a small subset of kinds, since it uses the same algorithm as sortManifestsByKind func TestKindSorterForHooks(t *testing.T) { hooks := []*release.Hook{ { @@ -304,9 +310,15 @@ func TestKindSorterForHooks(t *testing.T) { t.Fatalf("Expected %d names in order, got %d", want, got) } defer buf.Reset() - for _, r := range hooksSortedByKind(hooks, test.order) { + orig := hooks + for _, r := range sortHooksByKind(hooks, test.order) { buf.WriteString(r.Name) } + for i, hook := range orig { + if hook != hooks[i] { + t.Fatal("Expected input to sortHooksByKind to stay the same") + } + } if got := buf.String(); got != test.expected { t.Errorf("Expected %q, got %q", test.expected, got) } diff --git a/pkg/releaseutil/manifest_sorter.go b/pkg/releaseutil/manifest_sorter.go index 454977b00..e83414500 100644 --- a/pkg/releaseutil/manifest_sorter.go +++ b/pkg/releaseutil/manifest_sorter.go @@ -108,7 +108,7 @@ func SortManifests(files map[string]string, apis chartutil.VersionSet, ordering } } - return hooksSortedByKind(result.hooks, ordering), manifestsSortedByKind(result.generic, ordering), nil + return sortHooksByKind(result.hooks, ordering), sortManifestsByKind(result.generic, ordering), nil } // sort takes a manifestFile object which may contain multiple resource definition diff --git a/pkg/releaseutil/manifest_sorter_test.go b/pkg/releaseutil/manifest_sorter_test.go index 29b0d85cf..20d809317 100644 --- a/pkg/releaseutil/manifest_sorter_test.go +++ b/pkg/releaseutil/manifest_sorter_test.go @@ -219,7 +219,7 @@ metadata: } } - sorted = manifestsSortedByKind(sorted, InstallOrder) + sorted = sortManifestsByKind(sorted, InstallOrder) for i, m := range generic { if m.Content != sorted[i].Content { t.Errorf("Expected %q, got %q", m.Content, sorted[i].Content) From 1ab52fa79c100332bc8014095cf7aed6937cae8a Mon Sep 17 00:00:00 2001 From: Matt Morrissette Date: Fri, 21 Feb 2020 09:22:42 -0800 Subject: [PATCH 013/281] fix(helm): stdin values for helm upgrade --install Signed-off-by: Matt Morrissette --- cmd/helm/helm_test.go | 20 +++++++++-- cmd/helm/upgrade.go | 32 +++++++++--------- cmd/helm/upgrade_test.go | 64 ++++++++++++++++++++++++++++++++++++ pkg/action/rollback.go | 2 +- pkg/storage/driver/memory.go | 5 +++ 5 files changed, 105 insertions(+), 18 deletions(-) diff --git a/cmd/helm/helm_test.go b/cmd/helm/helm_test.go index 94646a5a3..9ba9d78fb 100644 --- a/cmd/helm/helm_test.go +++ b/cmd/helm/helm_test.go @@ -97,10 +97,15 @@ func storageFixture() *storage.Storage { } func executeActionCommandC(store *storage.Storage, cmd string) (*cobra.Command, string, error) { + return executeActionCommandStdinC(store, nil, cmd) +} + +func executeActionCommandStdinC(store *storage.Storage, in *os.File, cmd string) (*cobra.Command, string, error) { args, err := shellwords.Parse(cmd) if err != nil { return nil, "", err } + buf := new(bytes.Buffer) actionConfig := &action.Configuration{ @@ -111,15 +116,26 @@ func executeActionCommandC(store *storage.Storage, cmd string) (*cobra.Command, } root := newRootCmd(actionConfig, buf, args) - root.SetOutput(buf) + root.SetOut(buf) + root.SetErr(buf) root.SetArgs(args) + oldStdin := os.Stdin + if in != nil { + root.SetIn(in) + os.Stdin = in + } + if mem, ok := store.Driver.(*driver.Memory); ok { mem.SetNamespace(settings.Namespace()) } c, err := root.ExecuteC() - return c, buf.String(), err + result := buf.String() + + os.Stdin = oldStdin + + return c, result, err } // cmdTestCase describes a test case that works with releases. diff --git a/cmd/helm/upgrade.go b/cmd/helm/upgrade.go index dea866e4d..dd5a6c478 100644 --- a/cmd/helm/upgrade.go +++ b/cmd/helm/upgrade.go @@ -75,21 +75,8 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { client.Namespace = settings.Namespace() - if client.Version == "" && client.Devel { - debug("setting version to >0.0.0-0") - client.Version = ">0.0.0-0" - } - - vals, err := valueOpts.MergeValues(getter.All(settings)) - if err != nil { - return err - } - - chartPath, err := client.ChartPathOptions.LocateChart(args[1], settings) - if err != nil { - return err - } - + // Fixes #7002 - Support reading values from STDIN for `upgrade` command + // Must load values AFTER determining if we have to call install so that values loaded from stdin are are not read twice if client.Install { // If a release does not exist, install it. If another error occurs during // the check, ignore the error and continue with the upgrade. @@ -121,6 +108,21 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { } } + if client.Version == "" && client.Devel { + debug("setting version to >0.0.0-0") + client.Version = ">0.0.0-0" + } + + chartPath, err := client.ChartPathOptions.LocateChart(args[1], settings) + if err != nil { + return err + } + + vals, err := valueOpts.MergeValues(getter.All(settings)) + if err != nil { + return err + } + // Check chart dependencies to make sure all are present in /charts ch, err := loader.Load(chartPath) if err != nil { diff --git a/cmd/helm/upgrade_test.go b/cmd/helm/upgrade_test.go index 3cecbe6d3..1e413811a 100644 --- a/cmd/helm/upgrade_test.go +++ b/cmd/helm/upgrade_test.go @@ -19,6 +19,7 @@ package main import ( "fmt" "io/ioutil" + "os" "path/filepath" "strings" "testing" @@ -224,6 +225,69 @@ func TestUpgradeWithValuesFile(t *testing.T) { } +func TestUpgradeWithValuesFromStdin(t *testing.T) { + + releaseName := "funny-bunny-v5" + relMock, ch, chartPath := prepareMockRelease(releaseName, t) + + defer resetEnv()() + + store := storageFixture() + + store.Create(relMock(releaseName, 3, ch)) + + in, err := os.Open("testdata/testcharts/upgradetest/values.yaml") + if err != nil { + t.Errorf("unexpected error, got '%v'", err) + } + + cmd := fmt.Sprintf("upgrade %s --values - '%s'", releaseName, chartPath) + _, _, err = executeActionCommandStdinC(store, in, cmd) + if err != nil { + t.Errorf("unexpected error, got '%v'", err) + } + + updatedRel, err := store.Get(releaseName, 4) + if err != nil { + t.Errorf("unexpected error, got '%v'", err) + } + + if !strings.Contains(updatedRel.Manifest, "drink: beer") { + t.Errorf("The value is not set correctly. manifest: %s", updatedRel.Manifest) + } +} + +func TestUpgradeInstallWithValuesFromStdin(t *testing.T) { + + releaseName := "funny-bunny-v6" + _, _, chartPath := prepareMockRelease(releaseName, t) + + defer resetEnv()() + + store := storageFixture() + + in, err := os.Open("testdata/testcharts/upgradetest/values.yaml") + if err != nil { + t.Errorf("unexpected error, got '%v'", err) + } + + cmd := fmt.Sprintf("upgrade %s -f - --install '%s'", releaseName, chartPath) + _, _, err = executeActionCommandStdinC(store, in, cmd) + if err != nil { + t.Errorf("unexpected error, got '%v'", err) + } + + updatedRel, err := store.Get(releaseName, 1) + if err != nil { + t.Errorf("unexpected error, got '%v'", err) + } + + if !strings.Contains(updatedRel.Manifest, "drink: beer") { + t.Errorf("The value is not set correctly. manifest: %s", updatedRel.Manifest) + } + +} + func prepareMockRelease(releaseName string, t *testing.T) (func(n string, v int, ch *chart.Chart) *release.Release, *chart.Chart, string) { tmpChart := ensure.TempDir(t) configmapData, err := ioutil.ReadFile("testdata/testcharts/upgradetest/templates/configmap.yaml") diff --git a/pkg/action/rollback.go b/pkg/action/rollback.go index 942c9d8af..81812983f 100644 --- a/pkg/action/rollback.go +++ b/pkg/action/rollback.go @@ -211,7 +211,7 @@ func (r *Rollback) performRollback(currentRelease, targetRelease *release.Releas } deployed, err := r.cfg.Releases.DeployedAll(currentRelease.Name) - if err != nil { + if err != nil && !strings.Contains(err.Error(), "has no deployed releases") { return nil, err } // Supersede all previous deployments, see issue #2941. diff --git a/pkg/storage/driver/memory.go b/pkg/storage/driver/memory.go index a99b36ef0..91378f588 100644 --- a/pkg/storage/driver/memory.go +++ b/pkg/storage/driver/memory.go @@ -141,6 +141,11 @@ func (mem *Memory) Query(keyvals map[string]string) ([]*rspb.Release, error) { break } } + + if len(ls) == 0 { + return nil, ErrReleaseNotFound + } + return ls, nil } From b864fcd0f4a86430b867ad3c48544944197d3b29 Mon Sep 17 00:00:00 2001 From: Mikhail Gusarov Date: Mon, 2 Mar 2020 19:05:14 +0100 Subject: [PATCH 014/281] Make the charts cache safe in presence of several Helm instances If several instances of Helm are run at the same moment and try to download the same chart, some of them might see an empty or incomplete file in cache. Prevent that by saving the dowloaded file atomically. Closes #7600 Signed-off-by: Mikhail Gusarov --- pkg/downloader/chart_downloader.go | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/pkg/downloader/chart_downloader.go b/pkg/downloader/chart_downloader.go index f3d4321c5..a9956f477 100644 --- a/pkg/downloader/chart_downloader.go +++ b/pkg/downloader/chart_downloader.go @@ -72,6 +72,31 @@ type ChartDownloader struct { RepositoryCache string } +// atomicWriteFile atomically (as atomic as os.Rename allows) writes a file to a +// disk. +func atomicWriteFile(filename string, body io.Reader, mode os.FileMode) error { + tempFile, err := ioutil.TempFile(filepath.Split(filename)) + if err != nil { + return err + } + tempName := tempFile.Name() + + if _, err := io.Copy(tempFile, body); err != nil { + tempFile.Close() // return value is ignored as we are already on error path + return err + } + + if err := tempFile.Close(); err != nil { + return err + } + + if err := os.Chmod(tempName, mode); err != nil { + return err + } + + return os.Rename(tempName, filename) +} + // DownloadTo retrieves a chart. Depending on the settings, it may also download a provenance file. // // If Verify is set to VerifyNever, the verification will be nil. @@ -101,7 +126,7 @@ func (c *ChartDownloader) DownloadTo(ref, version, dest string) (string, *proven name := filepath.Base(u.Path) destfile := filepath.Join(dest, name) - if err := ioutil.WriteFile(destfile, data.Bytes(), 0644); err != nil { + if err := atomicWriteFile(destfile, data, 0644); err != nil { return destfile, nil, err } @@ -117,7 +142,7 @@ func (c *ChartDownloader) DownloadTo(ref, version, dest string) (string, *proven return destfile, ver, nil } provfile := destfile + ".prov" - if err := ioutil.WriteFile(provfile, body.Bytes(), 0644); err != nil { + if err := atomicWriteFile(provfile, body, 0644); err != nil { return destfile, nil, err } From 51dd8313bcbc03db3f266dc6217d6fce70fe39d3 Mon Sep 17 00:00:00 2001 From: Anshul Verma Date: Tue, 10 Mar 2020 03:35:59 +0530 Subject: [PATCH 015/281] Solve the issue #7749 where proper formating was not being done if --short(-q) option was used with other formating options like json, yaml Signed-off-by: Anshul Verma --- cmd/helm/list.go | 21 ++++++++++++++++++++ cmd/helm/list_test.go | 10 ++++++++++ cmd/helm/testdata/output/list-short-json.txt | 0 cmd/helm/testdata/output/list-short-yaml.txt | 0 4 files changed, 31 insertions(+) create mode 100644 cmd/helm/testdata/output/list-short-json.txt create mode 100644 cmd/helm/testdata/output/list-short-yaml.txt diff --git a/cmd/helm/list.go b/cmd/helm/list.go index 4b652088d..59364381e 100644 --- a/cmd/helm/list.go +++ b/cmd/helm/list.go @@ -83,6 +83,27 @@ func newListCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { } if client.Short { + + names := make([]string, 0) + for _, res := range results { + //fmt.Fprintln(out, res.Name) + names = append(names, res.Name) + } + + outputFlag := cmd.Flag("output") + if outputFlag.Changed { + switch outputFlag.Value.String() { + case "json": + output.EncodeJSON(out, names) + return nil + case "yaml": + output.EncodeYAML(out, names) + return nil + default: + return outfmt.Write(out, newReleaseListWriter(results)) + } + } + for _, res := range results { fmt.Fprintln(out, res.Name) } diff --git a/cmd/helm/list_test.go b/cmd/helm/list_test.go index fe773a803..dadb57b94 100644 --- a/cmd/helm/list_test.go +++ b/cmd/helm/list_test.go @@ -198,6 +198,16 @@ func TestListCmd(t *testing.T) { cmd: "list --short", golden: "output/list-short.txt", rels: releaseFixture, + }, { + name: "list releases in short output format", + cmd: "list --short --output yaml", + golden: "output/list-short-yaml.txt", + rels: releaseFixture, + }, { + name: "list releases in short output format", + cmd: "list --short --output json", + golden: "output/list-short-json.txt", + rels: releaseFixture, }, { name: "list superseded releases", cmd: "list --superseded", diff --git a/cmd/helm/testdata/output/list-short-json.txt b/cmd/helm/testdata/output/list-short-json.txt new file mode 100644 index 000000000..e69de29bb diff --git a/cmd/helm/testdata/output/list-short-yaml.txt b/cmd/helm/testdata/output/list-short-yaml.txt new file mode 100644 index 000000000..e69de29bb From 7470337d32cef71043a3da1a23a6b3004e618ed6 Mon Sep 17 00:00:00 2001 From: Anshul Verma Date: Tue, 10 Mar 2020 03:42:43 +0530 Subject: [PATCH 016/281] Solve the issue #7749 where proper formating was not being done if --short(-q) option was used with other formating options like json, yaml Signed-off-by: Anshul Verma --- cmd/helm/testdata/output/list-short-json.txt | 1 + cmd/helm/testdata/output/list-short-yaml.txt | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/cmd/helm/testdata/output/list-short-json.txt b/cmd/helm/testdata/output/list-short-json.txt index e69de29bb..1ab52136b 100644 --- a/cmd/helm/testdata/output/list-short-json.txt +++ b/cmd/helm/testdata/output/list-short-json.txt @@ -0,0 +1 @@ +["hummingbird","iguana","rocket","starlord"] \ No newline at end of file diff --git a/cmd/helm/testdata/output/list-short-yaml.txt b/cmd/helm/testdata/output/list-short-yaml.txt index e69de29bb..41459282f 100644 --- a/cmd/helm/testdata/output/list-short-yaml.txt +++ b/cmd/helm/testdata/output/list-short-yaml.txt @@ -0,0 +1,4 @@ +- hummingbird +- iguana +- rocket +- starlord \ No newline at end of file From c354de80e5ab2a4ff07007353451dcb1b50b5801 Mon Sep 17 00:00:00 2001 From: Anshul Verma Date: Tue, 10 Mar 2020 03:47:21 +0530 Subject: [PATCH 017/281] Solve the issue #7749 where proper formating was not being done if --short(-q) option was used with other formating options like json, yaml Signed-off-by: Anshul Verma --- cmd/helm/testdata/output/list-short-json.txt | 2 +- cmd/helm/testdata/output/list-short-yaml.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/helm/testdata/output/list-short-json.txt b/cmd/helm/testdata/output/list-short-json.txt index 1ab52136b..acbf1e44d 100644 --- a/cmd/helm/testdata/output/list-short-json.txt +++ b/cmd/helm/testdata/output/list-short-json.txt @@ -1 +1 @@ -["hummingbird","iguana","rocket","starlord"] \ No newline at end of file +["hummingbird","iguana","rocket","starlord"] diff --git a/cmd/helm/testdata/output/list-short-yaml.txt b/cmd/helm/testdata/output/list-short-yaml.txt index 41459282f..86fb3d670 100644 --- a/cmd/helm/testdata/output/list-short-yaml.txt +++ b/cmd/helm/testdata/output/list-short-yaml.txt @@ -1,4 +1,4 @@ - hummingbird - iguana - rocket -- starlord \ No newline at end of file +- starlord From 4113fc8951a891a934028081417fc70e1a1b1f63 Mon Sep 17 00:00:00 2001 From: Anshul Verma Date: Wed, 11 Mar 2020 01:04:50 +0530 Subject: [PATCH 018/281] Solve the issue #7749 where proper formating was not being done if --short(-q) option was used with other formating options like json, yaml Signed-off-by: Anshul Verma --- cmd/helm/list.go | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/cmd/helm/list.go b/cmd/helm/list.go index 59364381e..08d6beb79 100644 --- a/cmd/helm/list.go +++ b/cmd/helm/list.go @@ -86,28 +86,26 @@ func newListCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { names := make([]string, 0) for _, res := range results { - //fmt.Fprintln(out, res.Name) names = append(names, res.Name) } outputFlag := cmd.Flag("output") - if outputFlag.Changed { - switch outputFlag.Value.String() { - case "json": - output.EncodeJSON(out, names) - return nil - case "yaml": - output.EncodeYAML(out, names) - return nil - default: - return outfmt.Write(out, newReleaseListWriter(results)) - } - } - for _, res := range results { - fmt.Fprintln(out, res.Name) + switch outputFlag.Value.String() { + case "json": + output.EncodeJSON(out, names) + return nil + case "yaml": + output.EncodeYAML(out, names) + return nil + case "table": + for _, res := range results { + fmt.Fprintln(out, res.Name) + } + return nil + default: + return outfmt.Write(out, newReleaseListWriter(results)) } - return nil } return outfmt.Write(out, newReleaseListWriter(results)) From cca68288063d235a4c32ef1ba822dd487317c8dc Mon Sep 17 00:00:00 2001 From: liuming216448 Date: Fri, 13 Mar 2020 18:36:19 +0800 Subject: [PATCH 019/281] fix: allow to rollback to previous version even if no deployed releases(#6978) Signed-off-by: liuming --- pkg/action/rollback.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pkg/action/rollback.go b/pkg/action/rollback.go index 942c9d8af..0e09f8b6f 100644 --- a/pkg/action/rollback.go +++ b/pkg/action/rollback.go @@ -210,8 +210,14 @@ func (r *Rollback) performRollback(currentRelease, targetRelease *release.Releas } } + targetRelease.Info.Status = release.StatusDeployed + deployed, err := r.cfg.Releases.DeployedAll(currentRelease.Name) if err != nil { + if strings.Contains(err.Error(), "has no deployed releases") { + r.cfg.Log(err.Error()) + return targetRelease, nil + } return nil, err } // Supersede all previous deployments, see issue #2941. @@ -221,7 +227,5 @@ func (r *Rollback) performRollback(currentRelease, targetRelease *release.Releas r.cfg.recordRelease(rel) } - targetRelease.Info.Status = release.StatusDeployed - return targetRelease, nil } From 9a0e7d8a317947afcaa19f1e6fcf8df3df31fa77 Mon Sep 17 00:00:00 2001 From: Martin Hickey Date: Wed, 18 Mar 2020 12:22:06 +0000 Subject: [PATCH 020/281] Improve error message to check in unit test Signed-off-by: Martin Hickey --- pkg/lint/lint_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/lint/lint_test.go b/pkg/lint/lint_test.go index 2a982d088..b51939d76 100644 --- a/pkg/lint/lint_test.go +++ b/pkg/lint/lint_test.go @@ -93,7 +93,7 @@ func TestBadValues(t *testing.T) { if len(m) < 1 { t.Fatalf("All didn't fail with expected errors, got %#v", m) } - if !strings.Contains(m[0].Err.Error(), "cannot unmarshal") { + if !strings.Contains(m[0].Err.Error(), "unable to parse YAML") { t.Errorf("All didn't have the error for invalid key format: %s", m[0].Err) } } From cead0efa69d97cb5b6d5a21fc0f05971b5128886 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 23 Mar 2020 18:54:38 +0100 Subject: [PATCH 021/281] helm create command's templates more consistent - Removed most right whitespace chomps except those directly following a template definition where it make sense to not lead with a blank line. The system applied is now to almost always left whitespace chomp but also whitespace chomp right if its the first thing in a file or template definition. - Updated indentation to be systematic throughout all the boilerplace files. Signed-off-by: Erik Sundell --- pkg/chartutil/create.go | 78 ++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/pkg/chartutil/create.go b/pkg/chartutil/create.go index 24eb1e277..16fbcd929 100644 --- a/pkg/chartutil/create.go +++ b/pkg/chartutil/create.go @@ -199,29 +199,29 @@ metadata: {{- toYaml . | nindent 4 }} {{- end }} spec: -{{- if .Values.ingress.tls }} + {{- if .Values.ingress.tls }} tls: - {{- range .Values.ingress.tls }} + {{- range .Values.ingress.tls }} - hosts: - {{- range .hosts }} + {{- range .hosts }} - {{ . | quote }} - {{- end }} + {{- end }} secretName: {{ .secretName }} + {{- end }} {{- end }} -{{- end }} rules: - {{- range .Values.ingress.hosts }} + {{- range .Values.ingress.hosts }} - host: {{ .host | quote }} http: paths: - {{- range .paths }} + {{- range .paths }} - path: {{ . }} backend: serviceName: {{ $fullName }} servicePort: {{ $svcPort }} - {{- end }} + {{- end }} + {{- end }} {{- end }} -{{- end }} ` const defaultDeployment = `apiVersion: apps/v1 @@ -240,10 +240,10 @@ spec: labels: {{- include ".selectorLabels" . | nindent 8 }} spec: - {{- with .Values.imagePullSecrets }} + {{- with .Values.imagePullSecrets }} imagePullSecrets: {{- toYaml . | nindent 8 }} - {{- end }} + {{- end }} serviceAccountName: {{ include ".serviceAccountName" . }} securityContext: {{- toYaml .Values.podSecurityContext | nindent 8 }} @@ -271,14 +271,14 @@ spec: nodeSelector: {{- toYaml . | nindent 8 }} {{- end }} - {{- with .Values.affinity }} + {{- with .Values.affinity }} affinity: {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.tolerations }} + {{- end }} + {{- with .Values.tolerations }} tolerations: {{- toYaml . | nindent 8 }} - {{- end }} + {{- end }} ` const defaultService = `apiVersion: v1 @@ -309,7 +309,7 @@ metadata: annotations: {{- toYaml . | nindent 4 }} {{- end }} -{{- end -}} +{{- end }} ` const defaultNotes = `1. Get the application URL by running these commands: @@ -340,8 +340,8 @@ const defaultHelpers = `{{/* vim: set filetype=mustache: */}} Expand the name of the chart. */}} {{- define ".name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} {{/* Create a default fully qualified app name. @@ -349,24 +349,24 @@ We truncate at 63 chars because some Kubernetes name fields are limited to this If release name contains chart name it will be used as a full name. */}} {{- define ".fullname" -}} -{{- if .Values.fullnameOverride -}} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- $name := default .Chart.Name .Values.nameOverride -}} -{{- if contains $name .Release.Name -}} -{{- .Release.Name | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} -{{- end -}} -{{- end -}} -{{- end -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} {{/* Create chart name and version as used by the chart label. */}} {{- define ".chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} -{{- end -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} {{/* Common labels @@ -378,7 +378,7 @@ helm.sh/chart: {{ include ".chart" . }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} {{- end }} app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end -}} +{{- end }} {{/* Selector labels @@ -386,18 +386,18 @@ Selector labels {{- define ".selectorLabels" -}} app.kubernetes.io/name: {{ include ".name" . }} app.kubernetes.io/instance: {{ .Release.Name }} -{{- end -}} +{{- end }} {{/* Create the name of the service account to use */}} {{- define ".serviceAccountName" -}} -{{- if .Values.serviceAccount.create -}} - {{ default (include ".fullname" .) .Values.serviceAccount.name }} -{{- else -}} - {{ default "default" .Values.serviceAccount.name }} -{{- end -}} -{{- end -}} +{{- if .Values.serviceAccount.create }} +{{- default (include ".fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} ` const defaultTestConnection = `apiVersion: v1 From cd9c9922ecee1fabfec51746d9e4b6f7da4ea3e6 Mon Sep 17 00:00:00 2001 From: Ihor Dvoretskyi Date: Thu, 26 Mar 2020 10:35:46 -0400 Subject: [PATCH 022/281] Snapcraft installation instructions added Helm is available as a snap - https://snapcraft.io/helm; added this to the installation options Signed-off-by: Ihor Dvoretskyi --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 745a60c2b..bb6908fdb 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ If you want to use a package manager: - [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`. +- [Snapcraft](https://snapcraft.io/) users can use `snap install helm --classic` To rapidly get Helm up and running, start with the [Quick Start Guide](https://docs.helm.sh/using_helm/#quickstart-guide). From 00201ffaa8e0c725b1acd429f5e5a712f30378d1 Mon Sep 17 00:00:00 2001 From: Jon Leonard Date: Thu, 26 Mar 2020 11:12:30 -0400 Subject: [PATCH 023/281] pass subchart notes option to install client Signed-off-by: Jon Leonard --- cmd/helm/upgrade.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/helm/upgrade.go b/cmd/helm/upgrade.go index af8ff68e3..48eac0b0f 100644 --- a/cmd/helm/upgrade.go +++ b/cmd/helm/upgrade.go @@ -112,6 +112,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { instClient.Atomic = client.Atomic instClient.PostRenderer = client.PostRenderer instClient.DisableOpenAPIValidation = client.DisableOpenAPIValidation + instClient.SubNotes = client.SubNotes rel, err := runInstall(args, instClient, valueOpts, out) if err != nil { From e2b70b2f4b94c071e8fe08487cffb006bcde1df7 Mon Sep 17 00:00:00 2001 From: Jon Leonard Date: Thu, 26 Mar 2020 13:20:30 -0400 Subject: [PATCH 024/281] add testing for upgrade --install with subchart notes Signed-off-by: Jon Leonard --- .../upgrade-install-with-subchart-notes.txt | 11 +++++++ .../chart-with-subchart-notes/Chart.yaml | 4 +++ .../charts/subchart-with-notes/Chart.yaml | 4 +++ .../subchart-with-notes/templates/NOTES.txt | 1 + .../charts/subchart-with-notes/values.yaml | 0 .../requirements.yaml | 3 ++ .../templates/NOTES.txt | 1 + .../chart-with-subchart-notes/values.yaml | 0 cmd/helm/upgrade_test.go | 32 +++++++++++++++++++ 9 files changed, 56 insertions(+) create mode 100644 cmd/helm/testdata/output/upgrade-install-with-subchart-notes.txt create mode 100644 cmd/helm/testdata/testcharts/chart-with-subchart-notes/Chart.yaml create mode 100644 cmd/helm/testdata/testcharts/chart-with-subchart-notes/charts/subchart-with-notes/Chart.yaml create mode 100644 cmd/helm/testdata/testcharts/chart-with-subchart-notes/charts/subchart-with-notes/templates/NOTES.txt create mode 100644 cmd/helm/testdata/testcharts/chart-with-subchart-notes/charts/subchart-with-notes/values.yaml create mode 100644 cmd/helm/testdata/testcharts/chart-with-subchart-notes/requirements.yaml create mode 100644 cmd/helm/testdata/testcharts/chart-with-subchart-notes/templates/NOTES.txt create mode 100644 cmd/helm/testdata/testcharts/chart-with-subchart-notes/values.yaml diff --git a/cmd/helm/testdata/output/upgrade-install-with-subchart-notes.txt b/cmd/helm/testdata/output/upgrade-install-with-subchart-notes.txt new file mode 100644 index 000000000..767b8c5d9 --- /dev/null +++ b/cmd/helm/testdata/output/upgrade-install-with-subchart-notes.txt @@ -0,0 +1,11 @@ +Release "wacky-bunny" has been upgraded. Happy Helming! +NAME: wacky-bunny +LAST DEPLOYED: Fri Sep 2 22:04:05 1977 +NAMESPACE: default +STATUS: deployed +REVISION: 2 +TEST SUITE: None +NOTES: +SUBCHART NOTES + +PARENT NOTES diff --git a/cmd/helm/testdata/testcharts/chart-with-subchart-notes/Chart.yaml b/cmd/helm/testdata/testcharts/chart-with-subchart-notes/Chart.yaml new file mode 100644 index 000000000..6ea036678 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-subchart-notes/Chart.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +description: Chart with subchart notes +name: chart-with-subchart-notes +version: 0.0.1 diff --git a/cmd/helm/testdata/testcharts/chart-with-subchart-notes/charts/subchart-with-notes/Chart.yaml b/cmd/helm/testdata/testcharts/chart-with-subchart-notes/charts/subchart-with-notes/Chart.yaml new file mode 100644 index 000000000..5ac27f2fd --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-subchart-notes/charts/subchart-with-notes/Chart.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +description: Subchart with notes +name: subchart-with-notes +version: 0.0.1 diff --git a/cmd/helm/testdata/testcharts/chart-with-subchart-notes/charts/subchart-with-notes/templates/NOTES.txt b/cmd/helm/testdata/testcharts/chart-with-subchart-notes/charts/subchart-with-notes/templates/NOTES.txt new file mode 100644 index 000000000..1f61a294e --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-subchart-notes/charts/subchart-with-notes/templates/NOTES.txt @@ -0,0 +1 @@ +SUBCHART NOTES diff --git a/cmd/helm/testdata/testcharts/chart-with-subchart-notes/charts/subchart-with-notes/values.yaml b/cmd/helm/testdata/testcharts/chart-with-subchart-notes/charts/subchart-with-notes/values.yaml new file mode 100644 index 000000000..e69de29bb diff --git a/cmd/helm/testdata/testcharts/chart-with-subchart-notes/requirements.yaml b/cmd/helm/testdata/testcharts/chart-with-subchart-notes/requirements.yaml new file mode 100644 index 000000000..6351bf042 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-subchart-notes/requirements.yaml @@ -0,0 +1,3 @@ +dependencies: + - name: subchart-with-notes + version: 0.0.1 diff --git a/cmd/helm/testdata/testcharts/chart-with-subchart-notes/templates/NOTES.txt b/cmd/helm/testdata/testcharts/chart-with-subchart-notes/templates/NOTES.txt new file mode 100644 index 000000000..9e166d370 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-subchart-notes/templates/NOTES.txt @@ -0,0 +1 @@ +PARENT NOTES diff --git a/cmd/helm/testdata/testcharts/chart-with-subchart-notes/values.yaml b/cmd/helm/testdata/testcharts/chart-with-subchart-notes/values.yaml new file mode 100644 index 000000000..e69de29bb diff --git a/cmd/helm/upgrade_test.go b/cmd/helm/upgrade_test.go index 3cecbe6d3..4956c0247 100644 --- a/cmd/helm/upgrade_test.go +++ b/cmd/helm/upgrade_test.go @@ -196,6 +196,38 @@ func TestUpgradeWithStringValue(t *testing.T) { } +func TestUpgradeInstallWithSubchartNotes(t *testing.T) { + + releaseName := "wacky-bunny-v1" + relMock, ch, _ := prepareMockRelease(releaseName, t) + + defer resetEnv()() + + store := storageFixture() + + store.Create(relMock(releaseName, 1, ch)) + + cmd := fmt.Sprintf("upgrade %s -i --render-subchart-notes '%s'", releaseName, "testdata/testcharts/chart-with-subchart-notes") + _, _, err := executeActionCommandC(store, cmd) + if err != nil { + t.Errorf("unexpected error, got '%v'", err) + } + + upgradedRel, err := store.Get(releaseName, 2) + if err != nil { + t.Errorf("unexpected error, got '%v'", err) + } + + if !strings.Contains(upgradedRel.Info.Notes, "PARENT NOTES") { + t.Errorf("The parent notes are not set correctly. NOTES: %s", upgradedRel.Info.Notes) + } + + if !strings.Contains(upgradedRel.Info.Notes, "SUBCHART NOTES") { + t.Errorf("The subchart notes are not set correctly. NOTES: %s", upgradedRel.Info.Notes) + } + +} + func TestUpgradeWithValuesFile(t *testing.T) { releaseName := "funny-bunny-v4" From a7e79ee434abe23a9d6977f08516cb21da8b39ae Mon Sep 17 00:00:00 2001 From: Jon Leonard Date: Thu, 26 Mar 2020 13:29:38 -0400 Subject: [PATCH 025/281] Delete unneeded chart output Signed-off-by: Jon Leonard --- .../output/upgrade-install-with-subchart-notes.txt | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 cmd/helm/testdata/output/upgrade-install-with-subchart-notes.txt diff --git a/cmd/helm/testdata/output/upgrade-install-with-subchart-notes.txt b/cmd/helm/testdata/output/upgrade-install-with-subchart-notes.txt deleted file mode 100644 index 767b8c5d9..000000000 --- a/cmd/helm/testdata/output/upgrade-install-with-subchart-notes.txt +++ /dev/null @@ -1,11 +0,0 @@ -Release "wacky-bunny" has been upgraded. Happy Helming! -NAME: wacky-bunny -LAST DEPLOYED: Fri Sep 2 22:04:05 1977 -NAMESPACE: default -STATUS: deployed -REVISION: 2 -TEST SUITE: None -NOTES: -SUBCHART NOTES - -PARENT NOTES From 97c68adc4d53c13fb32432010a8f7c5172e3549d Mon Sep 17 00:00:00 2001 From: Tuan Date: Sat, 28 Mar 2020 09:39:41 +0800 Subject: [PATCH 026/281] Add fromYamlArray and fromJsonArray template helpers (#7712) Signed-off-by: Tuan Nguyen --- pkg/engine/funcs.go | 42 +++++++++++++++++++++++++++++++++++----- pkg/engine/funcs_test.go | 20 +++++++++++++++++++ 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/pkg/engine/funcs.go b/pkg/engine/funcs.go index dac105e74..e5769cbe0 100644 --- a/pkg/engine/funcs.go +++ b/pkg/engine/funcs.go @@ -48,11 +48,13 @@ func funcMap() template.FuncMap { // Add some extra functionality extra := template.FuncMap{ - "toToml": toTOML, - "toYaml": toYAML, - "fromYaml": fromYAML, - "toJson": toJSON, - "fromJson": fromJSON, + "toToml": toTOML, + "toYaml": toYAML, + "fromYaml": fromYAML, + "fromYamlArray": fromYAMLArray, + "toJson": toJSON, + "fromJson": fromJSON, + "fromJsonArray": fromJSONArray, // This is a placeholder for the "include" function, which is // late-bound to a template. By declaring it here, we preserve the @@ -97,6 +99,21 @@ func fromYAML(str string) map[string]interface{} { return m } +// fromYAMLArray converts a YAML array into a []interface{}. +// +// This is not a general-purpose YAML parser, and will not parse all valid +// YAML documents. Additionally, because its intended use is within templates +// it tolerates errors. It will insert the returned error message string as +// the first and only item in the returned array. +func fromYAMLArray(str string) []interface{} { + a := []interface{}{} + + if err := yaml.Unmarshal([]byte(str), &a); err != nil { + a = []interface{}{err.Error()} + } + return a +} + // toTOML takes an interface, marshals it to toml, and returns a string. It will // always return a string, even on marshal error (empty string). // @@ -138,3 +155,18 @@ func fromJSON(str string) map[string]interface{} { } return m } + +// fromJSONArray converts a JSON array into a []interface{}. +// +// This is not a general-purpose JSON parser, and will not parse all valid +// JSON documents. Additionally, because its intended use is within templates +// it tolerates errors. It will insert the returned error message string as +// the first and only item in the returned array. +func fromJSONArray(str string) []interface{} { + a := []interface{}{} + + if err := json.Unmarshal([]byte(str), &a); err != nil { + a = []interface{}{err.Error()} + } + return a +} diff --git a/pkg/engine/funcs_test.go b/pkg/engine/funcs_test.go index a94ff257e..a405c1c47 100644 --- a/pkg/engine/funcs_test.go +++ b/pkg/engine/funcs_test.go @@ -45,6 +45,14 @@ func TestFuncs(t *testing.T) { tpl: `{{ fromYaml . }}`, expect: "map[hello:world]", vars: `hello: world`, + }, { + tpl: `{{ fromYamlArray . }}`, + expect: "[one 2 map[name:helm]]", + vars: "- one\n- 2\n- name: helm\n", + }, { + tpl: `{{ fromYamlArray . }}`, + expect: "[one 2 map[name:helm]]", + vars: `["one", 2, { "name": "helm" }]`, }, { // Regression for https://github.com/helm/helm/issues/2271 tpl: `{{ toToml . }}`, @@ -62,6 +70,14 @@ func TestFuncs(t *testing.T) { tpl: `{{ fromJson . }}`, expect: `map[Error:json: cannot unmarshal array into Go value of type map[string]interface {}]`, vars: `["one", "two"]`, + }, { + tpl: `{{ fromJsonArray . }}`, + expect: `[one 2 map[name:helm]]`, + vars: `["one", 2, { "name": "helm" }]`, + }, { + tpl: `{{ fromJsonArray . }}`, + expect: `[json: cannot unmarshal object into Go value of type []interface {}]`, + vars: `{"hello": "world"}`, }, { tpl: `{{ merge .dict (fromYaml .yaml) }}`, expect: `map[a:map[b:c]]`, @@ -74,6 +90,10 @@ func TestFuncs(t *testing.T) { tpl: `{{ fromYaml . }}`, expect: `map[Error:error unmarshaling JSON: while decoding JSON: json: cannot unmarshal array into Go value of type map[string]interface {}]`, vars: `["one", "two"]`, + }, { + tpl: `{{ fromYamlArray . }}`, + expect: `[error unmarshaling JSON: while decoding JSON: json: cannot unmarshal object into Go value of type []interface {}]`, + vars: `hello: world`, }} for _, tt := range tests { From 3706aa7ca666fda6d8301c55118fa1c092f124a2 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Mon, 30 Mar 2020 16:33:19 -0600 Subject: [PATCH 027/281] fix: update unit test for go 1.14 error string change (#7835) * fix: update unit test for go 1.14 error string change Signed-off-by: Matt Butcher * changed strategy based on conversation with Adam Signed-off-by: Matt Butcher --- pkg/repo/chartrepo_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/repo/chartrepo_test.go b/pkg/repo/chartrepo_test.go index c6b227acf..f50d6a2b6 100644 --- a/pkg/repo/chartrepo_test.go +++ b/pkg/repo/chartrepo_test.go @@ -308,7 +308,7 @@ func TestErrorFindChartInRepoURL(t *testing.T) { if _, err := FindChartInRepoURL("http://someserver/something", "nginx", "", "", "", "", g); err == nil { t.Errorf("Expected error for bad chart URL, but did not get any errors") - } else if !strings.Contains(err.Error(), `looks like "http://someserver/something" is not a valid chart repository or cannot be reached: Get http://someserver/something/index.yaml`) { + } else if !strings.Contains(err.Error(), `looks like "http://someserver/something" is not a valid chart repository or cannot be reached`) { t.Errorf("Expected error for bad chart URL, but got a different error (%v)", err) } From cfdd466192f672bf18655981cad249d3e82dad5f Mon Sep 17 00:00:00 2001 From: Jon Leonard Date: Tue, 31 Mar 2020 12:12:39 -0400 Subject: [PATCH 028/281] update test chart to helm3 format Signed-off-by: Jon Leonard --- .../testdata/testcharts/chart-with-subchart-notes/Chart.yaml | 5 ++++- .../charts/subchart-with-notes/Chart.yaml | 2 +- .../testcharts/chart-with-subchart-notes/requirements.yaml | 3 --- 3 files changed, 5 insertions(+), 5 deletions(-) delete mode 100644 cmd/helm/testdata/testcharts/chart-with-subchart-notes/requirements.yaml diff --git a/cmd/helm/testdata/testcharts/chart-with-subchart-notes/Chart.yaml b/cmd/helm/testdata/testcharts/chart-with-subchart-notes/Chart.yaml index 6ea036678..90545a6a3 100644 --- a/cmd/helm/testdata/testcharts/chart-with-subchart-notes/Chart.yaml +++ b/cmd/helm/testdata/testcharts/chart-with-subchart-notes/Chart.yaml @@ -1,4 +1,7 @@ -apiVersion: v1 +apiVersion: v2 description: Chart with subchart notes name: chart-with-subchart-notes version: 0.0.1 +dependencies: + - name: subchart-with-notes + version: 0.0.1 diff --git a/cmd/helm/testdata/testcharts/chart-with-subchart-notes/charts/subchart-with-notes/Chart.yaml b/cmd/helm/testdata/testcharts/chart-with-subchart-notes/charts/subchart-with-notes/Chart.yaml index 5ac27f2fd..f0fead9ee 100644 --- a/cmd/helm/testdata/testcharts/chart-with-subchart-notes/charts/subchart-with-notes/Chart.yaml +++ b/cmd/helm/testdata/testcharts/chart-with-subchart-notes/charts/subchart-with-notes/Chart.yaml @@ -1,4 +1,4 @@ -apiVersion: v1 +apiVersion: v2 description: Subchart with notes name: subchart-with-notes version: 0.0.1 diff --git a/cmd/helm/testdata/testcharts/chart-with-subchart-notes/requirements.yaml b/cmd/helm/testdata/testcharts/chart-with-subchart-notes/requirements.yaml deleted file mode 100644 index 6351bf042..000000000 --- a/cmd/helm/testdata/testcharts/chart-with-subchart-notes/requirements.yaml +++ /dev/null @@ -1,3 +0,0 @@ -dependencies: - - name: subchart-with-notes - version: 0.0.1 From f927275461d048a095c7720e296690ebf169af31 Mon Sep 17 00:00:00 2001 From: Jon Leonard Date: Tue, 31 Mar 2020 12:14:40 -0400 Subject: [PATCH 029/281] remove unneeded values files from testchart Signed-off-by: Jon Leonard --- .../charts/subchart-with-notes/values.yaml | 0 .../testdata/testcharts/chart-with-subchart-notes/values.yaml | 0 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 cmd/helm/testdata/testcharts/chart-with-subchart-notes/charts/subchart-with-notes/values.yaml delete mode 100644 cmd/helm/testdata/testcharts/chart-with-subchart-notes/values.yaml diff --git a/cmd/helm/testdata/testcharts/chart-with-subchart-notes/charts/subchart-with-notes/values.yaml b/cmd/helm/testdata/testcharts/chart-with-subchart-notes/charts/subchart-with-notes/values.yaml deleted file mode 100644 index e69de29bb..000000000 diff --git a/cmd/helm/testdata/testcharts/chart-with-subchart-notes/values.yaml b/cmd/helm/testdata/testcharts/chart-with-subchart-notes/values.yaml deleted file mode 100644 index e69de29bb..000000000 From 9ab40a26af264e2842f712f9c59a36b1086d77a9 Mon Sep 17 00:00:00 2001 From: Hu Shuai Date: Wed, 25 Mar 2020 21:59:02 +0800 Subject: [PATCH 030/281] Add unit test for pkg/chart/chart.go Signed-off-by: Hu Shuai --- pkg/chart/chart_test.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pkg/chart/chart_test.go b/pkg/chart/chart_test.go index ef623fff6..4f19bf87d 100644 --- a/pkg/chart/chart_test.go +++ b/pkg/chart/chart_test.go @@ -78,3 +78,21 @@ func TestSaveChartNoRawData(t *testing.T) { is.Equal([]*File(nil), res.Raw) } + +func TestMetadata(t *testing.T) { + chrt := Chart{ + Metadata: &Metadata{ + Name: "foo.yaml", + AppVersion: "1.0.0", + APIVersion: "v2", + Version: "1.0.0", + Type: "application", + }, + } + + is := assert.New(t) + + is.Equal("foo.yaml", chrt.Name()) + is.Equal("1.0.0", chrt.AppVersion()) + is.Equal(nil, chrt.Validate()) +} From 6414791e080ae71795b5b0f73bd127f19ca00016 Mon Sep 17 00:00:00 2001 From: Mario Valderrama <15158349+avorima@users.noreply.github.com> Date: Thu, 2 Apr 2020 23:09:45 +0200 Subject: [PATCH 031/281] Improve --show-only flag (#7816) * Improve --show-only flag * Ensure consistent manifest ordering --- cmd/helm/template.go | 19 +++++++++--- cmd/helm/template_test.go | 7 +++++ .../output/template-name-template.txt | 29 +++++++++++++++++++ cmd/helm/testdata/output/template-set.txt | 29 +++++++++++++++++++ .../output/template-show-only-glob.txt | 23 +++++++++++++++ .../testdata/output/template-values-files.txt | 29 +++++++++++++++++++ .../output/template-with-api-version.txt | 29 +++++++++++++++++++ .../testdata/output/template-with-crds.txt | 29 +++++++++++++++++++ cmd/helm/testdata/output/template.txt | 29 +++++++++++++++++++ .../subchart1/templates/subdir/role.yaml | 7 +++++ .../templates/subdir/rolebinding.yaml | 12 ++++++++ .../templates/subdir/serviceaccount.yaml | 4 +++ 12 files changed, 242 insertions(+), 4 deletions(-) create mode 100644 cmd/helm/testdata/output/template-show-only-glob.txt create mode 100644 pkg/chartutil/testdata/subpop/charts/subchart1/templates/subdir/role.yaml create mode 100644 pkg/chartutil/testdata/subpop/charts/subchart1/templates/subdir/rolebinding.yaml create mode 100644 pkg/chartutil/testdata/subpop/charts/subchart1/templates/subdir/serviceaccount.yaml diff --git a/cmd/helm/template.go b/cmd/helm/template.go index bd14cde1d..a4438b50c 100644 --- a/cmd/helm/template.go +++ b/cmd/helm/template.go @@ -22,6 +22,7 @@ import ( "io" "path/filepath" "regexp" + "sort" "strings" "github.com/spf13/cobra" @@ -86,12 +87,21 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { // if we have a list of files to render, then check that each of the // provided files exists in the chart. if len(showFiles) > 0 { + // This is necessary to ensure consistent manifest ordering when using --show-only + // with globs or directory names. splitManifests := releaseutil.SplitManifests(manifests.String()) + manifestsKeys := make([]string, 0, len(splitManifests)) + for k := range splitManifests { + manifestsKeys = append(manifestsKeys, k) + } + sort.Sort(releaseutil.BySplitManifestsOrder(manifestsKeys)) + manifestNameRegex := regexp.MustCompile("# Source: [^/]+/(.+)") var manifestsToRender []string for _, f := range showFiles { missing := true - for _, manifest := range splitManifests { + for _, manifestKey := range manifestsKeys { + manifest := splitManifests[manifestKey] submatch := manifestNameRegex.FindStringSubmatch(manifest) if len(submatch) == 0 { continue @@ -104,10 +114,11 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { // if the filepath provided matches a manifest path in the // chart, render that manifest - if f == manifestPath { - manifestsToRender = append(manifestsToRender, manifest) - missing = false + if matched, _ := filepath.Match(f, manifestPath); !matched { + continue } + manifestsToRender = append(manifestsToRender, manifest) + missing = false } if missing { return fmt.Errorf("could not find template %s in chart", f) diff --git a/cmd/helm/template_test.go b/cmd/helm/template_test.go index 3fd139fad..87ee79e5a 100644 --- a/cmd/helm/template_test.go +++ b/cmd/helm/template_test.go @@ -94,6 +94,13 @@ func TestTemplateCmd(t *testing.T) { cmd: fmt.Sprintf("template '%s' --show-only templates/service.yaml --show-only charts/subcharta/templates/service.yaml", chartPath), golden: "output/template-show-only-multiple.txt", }, + { + name: "template with show-only glob", + cmd: fmt.Sprintf("template '%s' --show-only templates/subdir/role*", chartPath), + golden: "output/template-show-only-glob.txt", + // Repeat to ensure manifest ordering regressions are caught + repeat: 10, + }, { name: "sorted output of manifests (order of filenames, then order of objects within each YAML file)", cmd: fmt.Sprintf("template '%s'", "testdata/testcharts/object-order"), diff --git a/cmd/helm/testdata/output/template-name-template.txt b/cmd/helm/testdata/output/template-name-template.txt index acba50360..0130a2a92 100644 --- a/cmd/helm/testdata/output/template-name-template.txt +++ b/cmd/helm/testdata/output/template-name-template.txt @@ -1,4 +1,33 @@ --- +# Source: subchart1/templates/subdir/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: subchart1-sa +--- +# Source: subchart1/templates/subdir/role.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: subchart1-role +rules: +- resources: ["*"] + verbs: ["get","list","watch"] +--- +# Source: subchart1/templates/subdir/rolebinding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: subchart1-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: subchart1-role +subjects: +- kind: ServiceAccount + name: subchart1-sa + namespace: default +--- # Source: subchart1/charts/subcharta/templates/service.yaml apiVersion: v1 kind: Service diff --git a/cmd/helm/testdata/output/template-set.txt b/cmd/helm/testdata/output/template-set.txt index b0924b5b6..ddaa8886b 100644 --- a/cmd/helm/testdata/output/template-set.txt +++ b/cmd/helm/testdata/output/template-set.txt @@ -1,4 +1,33 @@ --- +# Source: subchart1/templates/subdir/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: subchart1-sa +--- +# Source: subchart1/templates/subdir/role.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: subchart1-role +rules: +- resources: ["*"] + verbs: ["get","list","watch"] +--- +# Source: subchart1/templates/subdir/rolebinding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: subchart1-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: subchart1-role +subjects: +- kind: ServiceAccount + name: subchart1-sa + namespace: default +--- # Source: subchart1/charts/subcharta/templates/service.yaml apiVersion: v1 kind: Service diff --git a/cmd/helm/testdata/output/template-show-only-glob.txt b/cmd/helm/testdata/output/template-show-only-glob.txt new file mode 100644 index 000000000..0970e6cd3 --- /dev/null +++ b/cmd/helm/testdata/output/template-show-only-glob.txt @@ -0,0 +1,23 @@ +--- +# Source: subchart1/templates/subdir/role.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: subchart1-role +rules: +- resources: ["*"] + verbs: ["get","list","watch"] +--- +# Source: subchart1/templates/subdir/rolebinding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: subchart1-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: subchart1-role +subjects: +- kind: ServiceAccount + name: subchart1-sa + namespace: default diff --git a/cmd/helm/testdata/output/template-values-files.txt b/cmd/helm/testdata/output/template-values-files.txt index b0924b5b6..ddaa8886b 100644 --- a/cmd/helm/testdata/output/template-values-files.txt +++ b/cmd/helm/testdata/output/template-values-files.txt @@ -1,4 +1,33 @@ --- +# Source: subchart1/templates/subdir/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: subchart1-sa +--- +# Source: subchart1/templates/subdir/role.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: subchart1-role +rules: +- resources: ["*"] + verbs: ["get","list","watch"] +--- +# Source: subchart1/templates/subdir/rolebinding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: subchart1-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: subchart1-role +subjects: +- kind: ServiceAccount + name: subchart1-sa + namespace: default +--- # Source: subchart1/charts/subcharta/templates/service.yaml apiVersion: v1 kind: Service diff --git a/cmd/helm/testdata/output/template-with-api-version.txt b/cmd/helm/testdata/output/template-with-api-version.txt index da3559082..7a2a4d5bf 100644 --- a/cmd/helm/testdata/output/template-with-api-version.txt +++ b/cmd/helm/testdata/output/template-with-api-version.txt @@ -1,4 +1,33 @@ --- +# Source: subchart1/templates/subdir/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: subchart1-sa +--- +# Source: subchart1/templates/subdir/role.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: subchart1-role +rules: +- resources: ["*"] + verbs: ["get","list","watch"] +--- +# Source: subchart1/templates/subdir/rolebinding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: subchart1-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: subchart1-role +subjects: +- kind: ServiceAccount + name: subchart1-sa + namespace: default +--- # Source: subchart1/charts/subcharta/templates/service.yaml apiVersion: v1 kind: Service diff --git a/cmd/helm/testdata/output/template-with-crds.txt b/cmd/helm/testdata/output/template-with-crds.txt index 9fa1c7e6d..b04a4d5d2 100644 --- a/cmd/helm/testdata/output/template-with-crds.txt +++ b/cmd/helm/testdata/output/template-with-crds.txt @@ -15,6 +15,35 @@ spec: singular: authconfig --- +# Source: subchart1/templates/subdir/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: subchart1-sa +--- +# Source: subchart1/templates/subdir/role.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: subchart1-role +rules: +- resources: ["*"] + verbs: ["get","list","watch"] +--- +# Source: subchart1/templates/subdir/rolebinding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: subchart1-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: subchart1-role +subjects: +- kind: ServiceAccount + name: subchart1-sa + namespace: default +--- # Source: subchart1/charts/subcharta/templates/service.yaml apiVersion: v1 kind: Service diff --git a/cmd/helm/testdata/output/template.txt b/cmd/helm/testdata/output/template.txt index 080be618c..8301c2231 100644 --- a/cmd/helm/testdata/output/template.txt +++ b/cmd/helm/testdata/output/template.txt @@ -1,4 +1,33 @@ --- +# Source: subchart1/templates/subdir/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: subchart1-sa +--- +# Source: subchart1/templates/subdir/role.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: subchart1-role +rules: +- resources: ["*"] + verbs: ["get","list","watch"] +--- +# Source: subchart1/templates/subdir/rolebinding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: subchart1-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: subchart1-role +subjects: +- kind: ServiceAccount + name: subchart1-sa + namespace: default +--- # Source: subchart1/charts/subcharta/templates/service.yaml apiVersion: v1 kind: Service diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/templates/subdir/role.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/templates/subdir/role.yaml new file mode 100644 index 000000000..91b954e5f --- /dev/null +++ b/pkg/chartutil/testdata/subpop/charts/subchart1/templates/subdir/role.yaml @@ -0,0 +1,7 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ .Chart.Name }}-role +rules: +- resources: ["*"] + verbs: ["get","list","watch"] diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/templates/subdir/rolebinding.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/templates/subdir/rolebinding.yaml new file mode 100644 index 000000000..5d193f1a6 --- /dev/null +++ b/pkg/chartutil/testdata/subpop/charts/subchart1/templates/subdir/rolebinding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ .Chart.Name }}-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ .Chart.Name }}-role +subjects: +- kind: ServiceAccount + name: {{ .Chart.Name }}-sa + namespace: default diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/templates/subdir/serviceaccount.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/templates/subdir/serviceaccount.yaml new file mode 100644 index 000000000..7126c7d89 --- /dev/null +++ b/pkg/chartutil/testdata/subpop/charts/subchart1/templates/subdir/serviceaccount.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ .Chart.Name }}-sa From c12a9aee02ec07b78dce07274e4816d9863d765e Mon Sep 17 00:00:00 2001 From: lnattrass Date: Thu, 2 Apr 2020 17:49:20 -0400 Subject: [PATCH 032/281] fix(helm): Data race in kube/client Delete func. (#7820) helm uninstall has a data race in its Delete function. This resolves it using a mutex. Signed-off-by: Liam Nattrass --- pkg/kube/client.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkg/kube/client.go b/pkg/kube/client.go index b761c6d12..2cb7e45cf 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -246,12 +246,17 @@ func (c *Client) Update(original, target ResourceList, force bool) (*Result, err func (c *Client) Delete(resources ResourceList) (*Result, []error) { var errs []error res := &Result{} + mtx := sync.Mutex{} err := perform(resources, func(info *resource.Info) error { c.Log("Starting delete for %q %s", info.Name, info.Mapping.GroupVersionKind.Kind) if err := c.skipIfNotFound(deleteResource(info)); err != nil { + mtx.Lock() + defer mtx.Unlock() // Collect the error and continue on errs = append(errs, err) } else { + mtx.Lock() + defer mtx.Unlock() res.Deleted = append(res.Deleted, info) } return nil From f3350defec881dc36217f4774703f252d3c895af Mon Sep 17 00:00:00 2001 From: Andrey Voronkov Date: Sat, 4 Apr 2020 04:22:43 +0300 Subject: [PATCH 033/281] Avoid downloading same chart multiple times Signed-off-by: Andrey Voronkov --- pkg/downloader/manager.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/pkg/downloader/manager.go b/pkg/downloader/manager.go index ff451a6e8..00198de0c 100644 --- a/pkg/downloader/manager.go +++ b/pkg/downloader/manager.go @@ -239,6 +239,7 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error { fmt.Fprintf(m.Out, "Saving %d charts\n", len(deps)) var saveError error + churls := make(map[string]struct{}) for _, dep := range deps { // No repository means the chart is in charts directory if dep.Repository == "" { @@ -278,8 +279,6 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error { continue } - fmt.Fprintf(m.Out, "Downloading %s from repo %s\n", dep.Name, dep.Repository) - // Any failure to resolve/download a chart should fail: // https://github.com/helm/helm/issues/1439 churl, username, password, err := m.findChartURL(dep.Name, dep.Version, dep.Repository, repos) @@ -288,6 +287,13 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error { break } + if _, ok := churls[churl]; ok { + fmt.Fprintf(m.Out, "Already downloaded %s from repo %s\n", dep.Name, dep.Repository) + continue + } + + fmt.Fprintf(m.Out, "Downloading %s from repo %s\n", dep.Name, dep.Repository) + dl := ChartDownloader{ Out: m.Out, Verify: m.Verify, @@ -304,6 +310,8 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error { saveError = errors.Wrapf(err, "could not download %s", churl) break } + + churls[churl] = struct{}{} } if saveError == nil { From c4fc8b7de84122e4f7f19833655e0a1aae74ba81 Mon Sep 17 00:00:00 2001 From: Naseem Date: Sun, 5 Apr 2020 12:14:01 -0400 Subject: [PATCH 034/281] feat: add pod annotations With the rise of sidecar injectors, pod annotations configuration is becoming more and more important. Signed-off-by: Naseem --- pkg/chartutil/create.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkg/chartutil/create.go b/pkg/chartutil/create.go index b088f20ce..8620f46ad 100644 --- a/pkg/chartutil/create.go +++ b/pkg/chartutil/create.go @@ -111,6 +111,8 @@ serviceAccount: # If not set and create is true, a name is generated using the fullname template name: +podAnnotations: {} + podSecurityContext: {} # fsGroup: 2000 @@ -248,6 +250,10 @@ spec: {{- include ".selectorLabels" . | nindent 6 }} template: metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} labels: {{- include ".selectorLabels" . | nindent 8 }} spec: From f7972d075ed62dc23321beebd17fd21515debe94 Mon Sep 17 00:00:00 2001 From: Naseem Date: Sun, 5 Apr 2020 10:55:58 -0400 Subject: [PATCH 035/281] feat: allow image tag override While using the chart version as image tag is the sanest default, it is not uncommon to want to override this if using a custom image, or using helm to manage an in-house app running different tags across different environments. Signed-off-by: Naseem --- pkg/chartutil/create.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/chartutil/create.go b/pkg/chartutil/create.go index b088f20ce..f26ff47c8 100644 --- a/pkg/chartutil/create.go +++ b/pkg/chartutil/create.go @@ -97,6 +97,8 @@ replicaCount: 1 image: repository: nginx pullPolicy: IfNotPresent + # Overrides the image tag whose default is the chart version. + tag: "" imagePullSecrets: [] nameOverride: "" @@ -262,7 +264,7 @@ spec: - name: {{ .Chart.Name }} securityContext: {{- toYaml .Values.securityContext | nindent 12 }} - image: "{{ .Values.image.repository }}:{{ .Chart.AppVersion }}" + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" imagePullPolicy: {{ .Values.image.pullPolicy }} ports: - name: http From 08c612eedfcffe0fedc75111aed3688c1d13971f Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Wed, 8 Apr 2020 15:41:39 -0400 Subject: [PATCH 036/281] Adding notes on semver to create Chart.yaml The version field in the Chart.yaml has a comment describing it but it did not note the version needs to follow SemVer. There have been numerous questions, over time, about this format. Add note here so it's exposed in more places. Signed-off-by: Matt Farina --- pkg/chartutil/create.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/chartutil/create.go b/pkg/chartutil/create.go index ee414580f..daaaa897c 100644 --- a/pkg/chartutil/create.go +++ b/pkg/chartutil/create.go @@ -81,10 +81,12 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) version: 0.1.0 # This is the version number of the application being deployed. This version number should be -# incremented each time you make changes to the application. +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. appVersion: 1.16.0 ` From bd13b80b12c246acf8959f510c1b21f72b2ccebd Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Thu, 9 Apr 2020 17:30:18 -0600 Subject: [PATCH 037/281] fix: fixed bug in Dependency.List() (#7852) * fix: fixed bug in Dependency.List() A bug in Dependency.List() caused all compressed charts to flag their dependencies as "missing". Closes #4431 Signed-off-by: Matt Butcher * removed some files from test fixtures Signed-off-by: Matt Butcher --- .../output/dependency-list-archive.txt | 8 +- pkg/action/dependency.go | 51 ++-- pkg/action/dependency_test.go | 58 ++++ .../charts/chart-missing-deps/.helmignore | 5 + .../charts/chart-missing-deps/Chart.yaml | 20 ++ .../charts/chart-missing-deps/README.md | 232 ++++++++++++++++ .../chart-missing-deps/requirements.lock | 6 + .../chart-missing-deps/requirements.yaml | 7 + .../chart-missing-deps/templates/NOTES.txt | 38 +++ .../chart-missing-deps/templates/_helpers.tpl | 24 ++ .../charts/chart-missing-deps/values.yaml | 254 ++++++++++++++++++ ...art-with-compressed-dependencies-2.1.8.tgz | Bin 0 -> 10962 bytes .../.helmignore | 5 + .../Chart.yaml | 20 ++ .../README.md | 3 + .../charts/mariadb-4.3.1.tgz | Bin 0 -> 8401 bytes .../requirements.lock | 6 + .../requirements.yaml | 7 + .../templates/NOTES.txt | 1 + .../values.yaml | 254 ++++++++++++++++++ ...t-with-uncompressed-dependencies-2.1.8.tgz | Bin 0 -> 10953 bytes .../.helmignore | 5 + .../Chart.yaml | 20 ++ .../README.md | 3 + .../charts/mariadb/.helmignore | 1 + .../charts/mariadb/Chart.yaml | 21 ++ .../charts/mariadb/README.md | 143 ++++++++++ .../docker-entrypoint-initdb.d/README.md | 3 + .../charts/mariadb/templates/NOTES.txt | 35 +++ .../charts/mariadb/templates/_helpers.tpl | 53 ++++ .../templates/initialization-configmap.yaml | 12 + .../mariadb/templates/master-configmap.yaml | 15 ++ .../mariadb/templates/master-statefulset.yaml | 187 +++++++++++++ .../charts/mariadb/templates/master-svc.yaml | 29 ++ .../charts/mariadb/templates/secrets.yaml | 38 +++ .../mariadb/templates/slave-configmap.yaml | 15 ++ .../mariadb/templates/slave-statefulset.yaml | 193 +++++++++++++ .../charts/mariadb/templates/slave-svc.yaml | 31 +++ .../charts/mariadb/templates/test-runner.yaml | 44 +++ .../charts/mariadb/templates/tests.yaml | 9 + .../charts/mariadb/values.yaml | 233 ++++++++++++++++ .../requirements.lock | 6 + .../requirements.yaml | 7 + .../templates/NOTES.txt | 1 + .../values.yaml | 254 ++++++++++++++++++ .../testdata/output/compressed-deps-tgz.txt | 3 + .../testdata/output/compressed-deps.txt | 3 + pkg/action/testdata/output/missing-deps.txt | 3 + .../testdata/output/uncompressed-deps-tgz.txt | 3 + .../testdata/output/uncompressed-deps.txt | 3 + 50 files changed, 2343 insertions(+), 29 deletions(-) create mode 100644 pkg/action/dependency_test.go create mode 100755 pkg/action/testdata/charts/chart-missing-deps/.helmignore create mode 100755 pkg/action/testdata/charts/chart-missing-deps/Chart.yaml create mode 100755 pkg/action/testdata/charts/chart-missing-deps/README.md create mode 100755 pkg/action/testdata/charts/chart-missing-deps/requirements.lock create mode 100755 pkg/action/testdata/charts/chart-missing-deps/requirements.yaml create mode 100755 pkg/action/testdata/charts/chart-missing-deps/templates/NOTES.txt create mode 100755 pkg/action/testdata/charts/chart-missing-deps/templates/_helpers.tpl create mode 100755 pkg/action/testdata/charts/chart-missing-deps/values.yaml create mode 100644 pkg/action/testdata/charts/chart-with-compressed-dependencies-2.1.8.tgz create mode 100755 pkg/action/testdata/charts/chart-with-compressed-dependencies/.helmignore create mode 100755 pkg/action/testdata/charts/chart-with-compressed-dependencies/Chart.yaml create mode 100755 pkg/action/testdata/charts/chart-with-compressed-dependencies/README.md create mode 100644 pkg/action/testdata/charts/chart-with-compressed-dependencies/charts/mariadb-4.3.1.tgz create mode 100755 pkg/action/testdata/charts/chart-with-compressed-dependencies/requirements.lock create mode 100755 pkg/action/testdata/charts/chart-with-compressed-dependencies/requirements.yaml create mode 100755 pkg/action/testdata/charts/chart-with-compressed-dependencies/templates/NOTES.txt create mode 100755 pkg/action/testdata/charts/chart-with-compressed-dependencies/values.yaml create mode 100644 pkg/action/testdata/charts/chart-with-uncompressed-dependencies-2.1.8.tgz create mode 100755 pkg/action/testdata/charts/chart-with-uncompressed-dependencies/.helmignore create mode 100755 pkg/action/testdata/charts/chart-with-uncompressed-dependencies/Chart.yaml create mode 100755 pkg/action/testdata/charts/chart-with-uncompressed-dependencies/README.md create mode 100755 pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/.helmignore create mode 100755 pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/Chart.yaml create mode 100755 pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/README.md create mode 100755 pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/files/docker-entrypoint-initdb.d/README.md create mode 100755 pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/NOTES.txt create mode 100755 pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/_helpers.tpl create mode 100755 pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/initialization-configmap.yaml create mode 100755 pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/master-configmap.yaml create mode 100755 pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/master-statefulset.yaml create mode 100755 pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/master-svc.yaml create mode 100755 pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/secrets.yaml create mode 100755 pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/slave-configmap.yaml create mode 100755 pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/slave-statefulset.yaml create mode 100755 pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/slave-svc.yaml create mode 100755 pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/test-runner.yaml create mode 100755 pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/tests.yaml create mode 100755 pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/values.yaml create mode 100755 pkg/action/testdata/charts/chart-with-uncompressed-dependencies/requirements.lock create mode 100755 pkg/action/testdata/charts/chart-with-uncompressed-dependencies/requirements.yaml create mode 100755 pkg/action/testdata/charts/chart-with-uncompressed-dependencies/templates/NOTES.txt create mode 100755 pkg/action/testdata/charts/chart-with-uncompressed-dependencies/values.yaml create mode 100644 pkg/action/testdata/output/compressed-deps-tgz.txt create mode 100644 pkg/action/testdata/output/compressed-deps.txt create mode 100644 pkg/action/testdata/output/missing-deps.txt create mode 100644 pkg/action/testdata/output/uncompressed-deps-tgz.txt create mode 100644 pkg/action/testdata/output/uncompressed-deps.txt diff --git a/cmd/helm/testdata/output/dependency-list-archive.txt b/cmd/helm/testdata/output/dependency-list-archive.txt index a0fc13cd0..ffd4542b0 100644 --- a/cmd/helm/testdata/output/dependency-list-archive.txt +++ b/cmd/helm/testdata/output/dependency-list-archive.txt @@ -1,5 +1,5 @@ -NAME VERSION REPOSITORY STATUS -reqsubchart 0.1.0 https://example.com/charts missing -reqsubchart2 0.2.0 https://example.com/charts missing -reqsubchart3 >=0.1.0 https://example.com/charts missing +NAME VERSION REPOSITORY STATUS +reqsubchart 0.1.0 https://example.com/charts unpacked +reqsubchart2 0.2.0 https://example.com/charts unpacked +reqsubchart3 >=0.1.0 https://example.com/charts unpacked diff --git a/pkg/action/dependency.go b/pkg/action/dependency.go index 5781cc913..4a4b8ebad 100644 --- a/pkg/action/dependency.go +++ b/pkg/action/dependency.go @@ -55,15 +55,22 @@ func (d *Dependency) List(chartpath string, out io.Writer) error { return nil } - d.printDependencies(chartpath, out, c.Metadata.Dependencies) + d.printDependencies(chartpath, out, c) fmt.Fprintln(out) d.printMissing(chartpath, out, c.Metadata.Dependencies) return nil } -func (d *Dependency) dependencyStatus(chartpath string, dep *chart.Dependency) string { +func (d *Dependency) dependencyStatus(chartpath string, dep *chart.Dependency, parent *chart.Chart) string { filename := fmt.Sprintf("%s-%s.tgz", dep.Name, "*") + // If a chart is unpacked, this will check the unpacked chart's `charts/` directory for tarballs. + // Technically, this is COMPLETELY unnecessary, and should be removed in Helm 4. It is here + // to preserved backward compatibility. In Helm 2/3, there is a "difference" between + // the tgz version (which outputs "ok" if it unpacks) and the loaded version (which outouts + // "unpacked"). Early in Helm 2's history, this would have made a difference. But it no + // longer does. However, since this code shipped with Helm 3, the output must remain stable + // until Helm 4. switch archives, err := filepath.Glob(filepath.Join(chartpath, "charts", filename)); { case err != nil: return "bad pattern" @@ -91,58 +98,52 @@ func (d *Dependency) dependencyStatus(chartpath string, dep *chart.Dependency) s return "invalid version" } - if constraint.Check(v) { - return "ok" + if !constraint.Check(v) { + return "wrong version" } - return "wrong version" } return "ok" } } + // End unnecessary code. - folder := filepath.Join(chartpath, "charts", dep.Name) - if fi, err := os.Stat(folder); err != nil { - return "missing" - } else if !fi.IsDir() { - return "mispackaged" - } - - c, err := loader.Load(folder) - if err != nil { - return "corrupt" + var depChart *chart.Chart + for _, item := range parent.Dependencies() { + if item.Name() == dep.Name { + depChart = item + } } - if c.Name() != dep.Name { - return "misnamed" + if depChart == nil { + return "missing" } - if c.Metadata.Version != dep.Version { + if depChart.Metadata.Version != dep.Version { constraint, err := semver.NewConstraint(dep.Version) if err != nil { return "invalid version" } - v, err := semver.NewVersion(c.Metadata.Version) + v, err := semver.NewVersion(depChart.Metadata.Version) if err != nil { return "invalid version" } - if constraint.Check(v) { - return "unpacked" + if !constraint.Check(v) { + return "wrong version" } - return "wrong version" } return "unpacked" } // printDependencies prints all of the dependencies in the yaml file. -func (d *Dependency) printDependencies(chartpath string, out io.Writer, reqs []*chart.Dependency) { +func (d *Dependency) printDependencies(chartpath string, out io.Writer, c *chart.Chart) { table := uitable.New() table.MaxColWidth = 80 table.AddRow("NAME", "VERSION", "REPOSITORY", "STATUS") - for _, row := range reqs { - table.AddRow(row.Name, row.Version, row.Repository, d.dependencyStatus(chartpath, row)) + for _, row := range c.Metadata.Dependencies { + table.AddRow(row.Name, row.Version, row.Repository, d.dependencyStatus(chartpath, row, c)) } fmt.Fprintln(out, table) } diff --git a/pkg/action/dependency_test.go b/pkg/action/dependency_test.go new file mode 100644 index 000000000..158acbfb9 --- /dev/null +++ b/pkg/action/dependency_test.go @@ -0,0 +1,58 @@ +/* +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 action + +import ( + "bytes" + "testing" + + "helm.sh/helm/v3/internal/test" +) + +func TestList(t *testing.T) { + for _, tcase := range []struct { + chart string + golden string + }{ + { + chart: "testdata/charts/chart-with-compressed-dependencies", + golden: "output/compressed-deps.txt", + }, + { + chart: "testdata/charts/chart-with-compressed-dependencies-2.1.8.tgz", + golden: "output/compressed-deps-tgz.txt", + }, + { + chart: "testdata/charts/chart-with-uncompressed-dependencies", + golden: "output/uncompressed-deps.txt", + }, + { + chart: "testdata/charts/chart-with-uncompressed-dependencies-2.1.8.tgz", + golden: "output/uncompressed-deps-tgz.txt", + }, + { + chart: "testdata/charts/chart-missing-deps", + golden: "output/missing-deps.txt", + }, + } { + buf := bytes.Buffer{} + if err := NewDependency().List(tcase.chart, &buf); err != nil { + t.Fatal(err) + } + test.AssertGoldenBytes(t, buf.Bytes(), tcase.golden) + } +} diff --git a/pkg/action/testdata/charts/chart-missing-deps/.helmignore b/pkg/action/testdata/charts/chart-missing-deps/.helmignore new file mode 100755 index 000000000..e2cf7941f --- /dev/null +++ b/pkg/action/testdata/charts/chart-missing-deps/.helmignore @@ -0,0 +1,5 @@ +.git +# OWNERS file for Kubernetes +OWNERS +# example production yaml +values-production.yaml \ No newline at end of file diff --git a/pkg/action/testdata/charts/chart-missing-deps/Chart.yaml b/pkg/action/testdata/charts/chart-missing-deps/Chart.yaml new file mode 100755 index 000000000..8304984fd --- /dev/null +++ b/pkg/action/testdata/charts/chart-missing-deps/Chart.yaml @@ -0,0 +1,20 @@ +appVersion: 4.9.8 +description: Web publishing platform for building blogs and websites. +engine: gotpl +home: http://www.wordpress.com/ +icon: https://bitnami.com/assets/stacks/wordpress/img/wordpress-stack-220x234.png +keywords: +- wordpress +- cms +- blog +- http +- web +- application +- php +maintainers: +- email: containers@bitnami.com + name: bitnami-bot +name: chart-with-missing-deps +sources: +- https://github.com/bitnami/bitnami-docker-wordpress +version: 2.1.8 diff --git a/pkg/action/testdata/charts/chart-missing-deps/README.md b/pkg/action/testdata/charts/chart-missing-deps/README.md new file mode 100755 index 000000000..5859a17fa --- /dev/null +++ b/pkg/action/testdata/charts/chart-missing-deps/README.md @@ -0,0 +1,232 @@ +# WordPress + +[WordPress](https://wordpress.org/) is one of the most versatile open source content management systems on the market. A publishing platform for building blogs and websites. + +## TL;DR; + +```console +$ helm install stable/wordpress +``` + +## Introduction + +This chart bootstraps a [WordPress](https://github.com/bitnami/bitnami-docker-wordpress) deployment on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. + +It also packages the [Bitnami MariaDB chart](https://github.com/kubernetes/charts/tree/master/stable/mariadb) which is required for bootstrapping a MariaDB deployment for the database requirements of the WordPress application. + +## Prerequisites + +- Kubernetes 1.4+ with Beta APIs enabled +- PV provisioner support in the underlying infrastructure + +## Installing the Chart + +To install the chart with the release name `my-release`: + +```console +$ helm install --name my-release stable/wordpress +``` + +The command deploys WordPress on the Kubernetes cluster in the default configuration. The [configuration](#configuration) section lists the parameters that can be configured during installation. + +> **Tip**: List all releases using `helm list` + +## Uninstalling the Chart + +To uninstall/delete the `my-release` deployment: + +```console +$ helm delete my-release +``` + +The command removes all the Kubernetes components associated with the chart and deletes the release. + +## Configuration + +The following table lists the configurable parameters of the WordPress chart and their default values. + +| Parameter | Description | Default | +|----------------------------------|--------------------------------------------|---------------------------------------------------------| +| `image.registry` | WordPress image registry | `docker.io` | +| `image.repository` | WordPress image name | `bitnami/wordpress` | +| `image.tag` | WordPress image tag | `{VERSION}` | +| `image.pullPolicy` | Image pull policy | `Always` if `imageTag` is `latest`, else `IfNotPresent` | +| `image.pullSecrets` | Specify image pull secrets | `nil` | +| `wordpressUsername` | User of the application | `user` | +| `wordpressPassword` | Application password | _random 10 character long alphanumeric string_ | +| `wordpressEmail` | Admin email | `user@example.com` | +| `wordpressFirstName` | First name | `FirstName` | +| `wordpressLastName` | Last name | `LastName` | +| `wordpressBlogName` | Blog name | `User's Blog!` | +| `wordpressTablePrefix` | Table prefix | `wp_` | +| `allowEmptyPassword` | Allow DB blank passwords | `yes` | +| `smtpHost` | SMTP host | `nil` | +| `smtpPort` | SMTP port | `nil` | +| `smtpUser` | SMTP user | `nil` | +| `smtpPassword` | SMTP password | `nil` | +| `smtpUsername` | User name for SMTP emails | `nil` | +| `smtpProtocol` | SMTP protocol [`tls`, `ssl`] | `nil` | +| `replicaCount` | Number of WordPress Pods to run | `1` | +| `mariadb.enabled` | Deploy MariaDB container(s) | `true` | +| `mariadb.rootUser.password` | MariaDB admin password | `nil` | +| `mariadb.db.name` | Database name to create | `bitnami_wordpress` | +| `mariadb.db.user` | Database user to create | `bn_wordpress` | +| `mariadb.db.password` | Password for the database | _random 10 character long alphanumeric string_ | +| `externalDatabase.host` | Host of the external database | `localhost` | +| `externalDatabase.user` | Existing username in the external db | `bn_wordpress` | +| `externalDatabase.password` | Password for the above username | `nil` | +| `externalDatabase.database` | Name of the existing database | `bitnami_wordpress` | +| `externalDatabase.port` | Database port number | `3306` | +| `serviceType` | Kubernetes Service type | `LoadBalancer` | +| `serviceExternalTrafficPolicy` | Enable client source IP preservation | `Cluster` | +| `nodePorts.http` | Kubernetes http node port | `""` | +| `nodePorts.https` | Kubernetes https node port | `""` | +| `healthcheckHttps` | Use https for liveliness and readiness | `false` | +| `ingress.enabled` | Enable ingress controller resource | `false` | +| `ingress.hosts[0].name` | Hostname to your WordPress installation | `wordpress.local` | +| `ingress.hosts[0].path` | Path within the url structure | `/` | +| `ingress.hosts[0].tls` | Utilize TLS backend in ingress | `false` | +| `ingress.hosts[0].tlsSecret` | TLS Secret (certificates) | `wordpress.local-tls-secret` | +| `ingress.hosts[0].annotations` | Annotations for this host's ingress record | `[]` | +| `ingress.secrets[0].name` | TLS Secret Name | `nil` | +| `ingress.secrets[0].certificate` | TLS Secret Certificate | `nil` | +| `ingress.secrets[0].key` | TLS Secret Key | `nil` | +| `persistence.enabled` | Enable persistence using PVC | `true` | +| `persistence.existingClaim` | Enable persistence using an existing PVC | `nil` | +| `persistence.storageClass` | PVC Storage Class | `nil` (uses alpha storage class annotation) | +| `persistence.accessMode` | PVC Access Mode | `ReadWriteOnce` | +| `persistence.size` | PVC Storage Request | `10Gi` | +| `nodeSelector` | Node labels for pod assignment | `{}` | +| `tolerations` | List of node taints to tolerate | `[]` | +| `affinity` | Map of node/pod affinities | `{}` | + +The above parameters map to the env variables defined in [bitnami/wordpress](http://github.com/bitnami/bitnami-docker-wordpress). For more information please refer to the [bitnami/wordpress](http://github.com/bitnami/bitnami-docker-wordpress) image documentation. + +Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example, + +```console +$ helm install --name my-release \ + --set wordpressUsername=admin,wordpressPassword=password,mariadb.mariadbRootPassword=secretpassword \ + stable/wordpress +``` + +The above command sets the WordPress administrator account username and password to `admin` and `password` respectively. Additionally, it sets the MariaDB `root` user password to `secretpassword`. + +Alternatively, a YAML file that specifies the values for the above parameters can be provided while installing the chart. For example, + +```console +$ helm install --name my-release -f values.yaml stable/wordpress +``` + +> **Tip**: You can use the default [values.yaml](values.yaml) + +## Production and horizontal scaling + +The following repo contains the recommended production settings for wordpress capture in an alternative [values file](values-production.yaml). Please read carefully the comments in the values-production.yaml file to set up your environment appropriately. + +To horizontally scale this chart, first download the [values-production.yaml](values-production.yaml) file to your local folder, then: + +```console +$ helm install --name my-release -f ./values-production.yaml stable/wordpress +``` + +Note that [values-production.yaml](values-production.yaml) includes a replicaCount of 3, so there will be 3 WordPress pods. As a result, to use the /admin portal and to ensure you can scale wordpress you need to provide a ReadWriteMany PVC, if you don't have a provisioner for this type of storage, we recommend that you install the nfs provisioner and map it to a RWO volume. + +```console +$ helm install stable/nfs-server-provisioner --set persistence.enabled=true,persistence.size=10Gi +$ helm install --name my-release -f values-production.yaml --set persistence.storageClass=nfs stable/wordpress +``` + +## Persistence + +The [Bitnami WordPress](https://github.com/bitnami/bitnami-docker-wordpress) image stores the WordPress data and configurations at the `/bitnami` path of the container. + +Persistent Volume Claims are used to keep the data across deployments. This is known to work in GCE, AWS, and minikube. +See the [Configuration](#configuration) section to configure the PVC or to disable persistence. + +## Using an external database + +Sometimes you may want to have Wordpress connect to an external database rather than installing one inside your cluster, e.g. to use a managed database service, or use run a single database server for all your applications. To do this, the chart allows you to specify credentials for an external database under the [`externalDatabase` parameter](#configuration). You should also disable the MariaDB installation with the `mariadb.enabled` option. For example: + +```console +$ helm install stable/wordpress \ + --set mariadb.enabled=false,externalDatabase.host=myexternalhost,externalDatabase.user=myuser,externalDatabase.password=mypassword,externalDatabase.database=mydatabase,externalDatabase.port=3306 +``` + +Note also if you disable MariaDB per above you MUST supply values for the `externalDatabase` connection. + +## Ingress + +This chart provides support for ingress resources. If you have an +ingress controller installed on your cluster, such as [nginx-ingress](https://kubeapps.com/charts/stable/nginx-ingress) +or [traefik](https://kubeapps.com/charts/stable/traefik) you can utilize +the ingress controller to serve your WordPress application. + +To enable ingress integration, please set `ingress.enabled` to `true` + +### Hosts + +Most likely you will only want to have one hostname that maps to this +WordPress installation, however, it is possible to have more than one +host. To facilitate this, the `ingress.hosts` object is an array. + +For each item, please indicate a `name`, `tls`, `tlsSecret`, and any +`annotations` that you may want the ingress controller to know about. + +Indicating TLS will cause WordPress to generate HTTPS URLs, and +WordPress will be connected to at port 443. The actual secret that +`tlsSecret` references do not have to be generated by this chart. +However, please note that if TLS is enabled, the ingress record will not +work until this secret exists. + +For annotations, please see [this document](https://github.com/kubernetes/ingress-nginx/blob/master/docs/annotations.md). +Not all annotations are supported by all ingress controllers, but this +document does a good job of indicating which annotation is supported by +many popular ingress controllers. + +### TLS Secrets + +This chart will facilitate the creation of TLS secrets for use with the +ingress controller, however, this is not required. There are three +common use cases: + +* helm generates/manages certificate secrets +* user generates/manages certificates separately +* an additional tool (like [kube-lego](https://kubeapps.com/charts/stable/kube-lego)) +manages the secrets for the application + +In the first two cases, one will need a certificate and a key. We would +expect them to look like this: + +* certificate files should look like (and there can be more than one +certificate if there is a certificate chain) + +``` +-----BEGIN CERTIFICATE----- +MIID6TCCAtGgAwIBAgIJAIaCwivkeB5EMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV +... +jScrvkiBO65F46KioCL9h5tDvomdU1aqpI/CBzhvZn1c0ZTf87tGQR8NK7v7 +-----END CERTIFICATE----- +``` +* keys should look like: +``` +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAvLYcyu8f3skuRyUgeeNpeDvYBCDcgq+LsWap6zbX5f8oLqp4 +... +wrj2wDbCDCFmfqnSJ+dKI3vFLlEz44sAV8jX/kd4Y6ZTQhlLbYc= +-----END RSA PRIVATE KEY----- +```` + +If you are going to use Helm to manage the certificates, please copy +these values into the `certificate` and `key` values for a given +`ingress.secrets` entry. + +If you are going are going to manage TLS secrets outside of Helm, please +know that you can create a TLS secret by doing the following: + +``` +kubectl create secret tls wordpress.local-tls --key /path/to/key.key --cert /path/to/cert.crt +``` + +Please see [this example](https://github.com/kubernetes/contrib/tree/master/ingress/controllers/nginx/examples/tls) +for more information. diff --git a/pkg/action/testdata/charts/chart-missing-deps/requirements.lock b/pkg/action/testdata/charts/chart-missing-deps/requirements.lock new file mode 100755 index 000000000..cb3439862 --- /dev/null +++ b/pkg/action/testdata/charts/chart-missing-deps/requirements.lock @@ -0,0 +1,6 @@ +dependencies: +- name: mariadb + repository: https://kubernetes-charts.storage.googleapis.com/ + version: 4.3.1 +digest: sha256:82a0e5374376169d2ecf7d452c18a2ed93507f5d17c3393a1457f9ffad7e9b26 +generated: 2018-08-02T22:07:51.905271776Z diff --git a/pkg/action/testdata/charts/chart-missing-deps/requirements.yaml b/pkg/action/testdata/charts/chart-missing-deps/requirements.yaml new file mode 100755 index 000000000..a894b8b3b --- /dev/null +++ b/pkg/action/testdata/charts/chart-missing-deps/requirements.yaml @@ -0,0 +1,7 @@ +dependencies: +- name: mariadb + version: 4.x.x + repository: https://kubernetes-charts.storage.googleapis.com/ + condition: mariadb.enabled + tags: + - wordpress-database diff --git a/pkg/action/testdata/charts/chart-missing-deps/templates/NOTES.txt b/pkg/action/testdata/charts/chart-missing-deps/templates/NOTES.txt new file mode 100755 index 000000000..55626e4d1 --- /dev/null +++ b/pkg/action/testdata/charts/chart-missing-deps/templates/NOTES.txt @@ -0,0 +1,38 @@ +1. Get the WordPress URL: + +{{- if .Values.ingress.enabled }} + + You should be able to access your new WordPress installation through + + {{- range .Values.ingress.hosts }} + {{ if .tls }}https{{ else }}http{{ end }}://{{ .name }}/admin + {{- end }} + +{{- else if contains "LoadBalancer" .Values.serviceType }} + + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + Watch the status with: 'kubectl get svc --namespace {{ .Release.Namespace }} -w {{ template "fullname" . }}' + + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + echo "WordPress URL: http://$SERVICE_IP/" + echo "WordPress Admin URL: http://$SERVICE_IP/admin" + +{{- else if contains "ClusterIP" .Values.serviceType }} + + echo "WordPress URL: http://127.0.0.1:8080/" + echo "WordPress Admin URL: http://127.0.0.1:8080/admin" + kubectl port-forward --namespace {{ .Release.Namespace }} svc/{{ template "fullname" . }} 8080:80 + +{{- else if contains "NodePort" .Values.serviceType }} + + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo "WordPress URL: http://$NODE_IP:$NODE_PORT/" + echo "WordPress Admin URL: http://$NODE_IP:$NODE_PORT/admin" + +{{- end }} + +2. Login with the following credentials to see your blog + + echo Username: {{ .Values.wordpressUsername }} + echo Password: $(kubectl get secret --namespace {{ .Release.Namespace }} {{ template "fullname" . }} -o jsonpath="{.data.wordpress-password}" | base64 --decode) diff --git a/pkg/action/testdata/charts/chart-missing-deps/templates/_helpers.tpl b/pkg/action/testdata/charts/chart-missing-deps/templates/_helpers.tpl new file mode 100755 index 000000000..1e52d321c --- /dev/null +++ b/pkg/action/testdata/charts/chart-missing-deps/templates/_helpers.tpl @@ -0,0 +1,24 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "fullname" -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "mariadb.fullname" -}} +{{- printf "%s-%s" .Release.Name "mariadb" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/pkg/action/testdata/charts/chart-missing-deps/values.yaml b/pkg/action/testdata/charts/chart-missing-deps/values.yaml new file mode 100755 index 000000000..3cb66dafd --- /dev/null +++ b/pkg/action/testdata/charts/chart-missing-deps/values.yaml @@ -0,0 +1,254 @@ +## Bitnami WordPress image version +## ref: https://hub.docker.com/r/bitnami/wordpress/tags/ +## +image: + registry: docker.io + repository: bitnami/wordpress + tag: 4.9.8-debian-9 + ## Specify a imagePullPolicy + ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images + ## + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistrKeySecretName + +## User of the application +## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables +## +wordpressUsername: user + +## Application password +## Defaults to a random 10-character alphanumeric string if not set +## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables +## +# wordpressPassword: + +## Admin email +## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables +## +wordpressEmail: user@example.com + +## First name +## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables +## +wordpressFirstName: FirstName + +## Last name +## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables +## +wordpressLastName: LastName + +## Blog name +## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables +## +wordpressBlogName: User's Blog! + +## Table prefix +## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables +## +wordpressTablePrefix: wp_ + +## Set to `yes` to allow the container to be started with blank passwords +## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables +allowEmptyPassword: yes + +## SMTP mail delivery configuration +## ref: https://github.com/bitnami/bitnami-docker-wordpress/#smtp-configuration +## +# smtpHost: +# smtpPort: +# smtpUser: +# smtpPassword: +# smtpUsername: +# smtpProtocol: + +replicaCount: 1 + +externalDatabase: +## All of these values are only used when mariadb.enabled is set to false + ## Database host + host: localhost + + ## non-root Username for Wordpress Database + user: bn_wordpress + + ## Database password + password: "" + + ## Database name + database: bitnami_wordpress + + ## Database port number + port: 3306 + +## +## MariaDB chart configuration +## +mariadb: + ## Whether to deploy a mariadb server to satisfy the applications database requirements. To use an external database set this to false and configure the externalDatabase parameters + enabled: true + ## Disable MariaDB replication + replication: + enabled: false + + ## Create a database and a database user + ## ref: https://github.com/bitnami/bitnami-docker-mariadb/blob/master/README.md#creating-a-database-user-on-first-run + ## + db: + name: bitnami_wordpress + user: bn_wordpress + ## If the password is not specified, mariadb will generates a random password + ## + # password: + + ## MariaDB admin password + ## ref: https://github.com/bitnami/bitnami-docker-mariadb/blob/master/README.md#setting-the-root-password-on-first-run + ## + # rootUser: + # password: + + ## Enable persistence using Persistent Volume Claims + ## ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ + ## + master: + persistence: + enabled: true + ## mariadb data Persistent Volume Storage Class + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + # storageClass: "-" + accessMode: ReadWriteOnce + size: 8Gi + +## Kubernetes configuration +## For minikube, set this to NodePort, elsewhere use LoadBalancer or ClusterIP +## +serviceType: LoadBalancer +## +## serviceType: NodePort +## nodePorts: +## http: +## https: +nodePorts: + http: "" + https: "" +## Enable client source IP preservation +## ref http://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip +## +serviceExternalTrafficPolicy: Cluster + +## Allow health checks to be pointed at the https port +healthcheckHttps: false + +## Configure extra options for liveness and readiness probes +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes) +livenessProbe: + initialDelaySeconds: 120 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 +readinessProbe: + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 + +## Configure the ingress resource that allows you to access the +## Wordpress installation. Set up the URL +## ref: http://kubernetes.io/docs/user-guide/ingress/ +## +ingress: + ## Set to true to enable ingress record generation + enabled: false + + ## The list of hostnames to be covered with this ingress record. + ## Most likely this will be just one host, but in the event more hosts are needed, this is an array + hosts: + - name: wordpress.local + + ## Set this to true in order to enable TLS on the ingress record + ## A side effect of this will be that the backend wordpress service will be connected at port 443 + tls: false + + ## If TLS is set to true, you must declare what secret will store the key/certificate for TLS + tlsSecret: wordpress.local-tls + + ## Ingress annotations done as key:value pairs + ## If you're using kube-lego, you will want to add: + ## kubernetes.io/tls-acme: true + ## + ## For a full list of possible ingress annotations, please see + ## ref: https://github.com/kubernetes/ingress-nginx/blob/master/docs/annotations.md + ## + ## If tls is set to true, annotation ingress.kubernetes.io/secure-backends: "true" will automatically be set + annotations: + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: true + + secrets: + ## If you're providing your own certificates, please use this to add the certificates as secrets + ## key and certificate should start with -----BEGIN CERTIFICATE----- or + ## -----BEGIN RSA PRIVATE KEY----- + ## + ## name should line up with a tlsSecret set further up + ## If you're using kube-lego, this is unneeded, as it will create the secret for you if it is not set + ## + ## It is also possible to create and manage the certificates outside of this helm chart + ## Please see README.md for more information + # - name: wordpress.local-tls + # key: + # certificate: + +## Enable persistence using Persistent Volume Claims +## ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ +## +persistence: + enabled: true + ## wordpress data Persistent Volume Storage Class + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + # storageClass: "-" + ## + ## If you want to reuse an existing claim, you can pass the name of the PVC using + ## the existingClaim variable + # existingClaim: your-claim + accessMode: ReadWriteOnce + size: 10Gi + +## Configure resource requests and limits +## ref: http://kubernetes.io/docs/user-guide/compute-resources/ +## +resources: + requests: + memory: 512Mi + cpu: 300m + +## Node labels for pod assignment +## Ref: https://kubernetes.io/docs/user-guide/node-selection/ +## +nodeSelector: {} + +## Tolerations for pod assignment +## Ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ +## +tolerations: [] + +## Affinity for pod assignment +## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity +## +affinity: {} diff --git a/pkg/action/testdata/charts/chart-with-compressed-dependencies-2.1.8.tgz b/pkg/action/testdata/charts/chart-with-compressed-dependencies-2.1.8.tgz new file mode 100644 index 0000000000000000000000000000000000000000..7a22b1d827f593f101879554ae7831f2a40d2278 GIT binary patch literal 10962 zcmV;@DlOF?iwG0|00000|0w_~VMtOiV@ORlOnEsqVl!4SWK%V1T2nbTPgYhoO;>Dc zVQyr3R8em|NM&qo0PKBhbKAzU;Q5SS(U&@BZRHB!OOJ&2`qWWuCDFzYT}g@Stk)$D zfgw3p00x;ENM3%sss7tJ>K**0+dtgz9v&VX z9QFRv>2*2>`+tGXJ;z(Cq+B5Om(GL7s!r|?QcyyE#f%dg58$Tjpd`uu-1olozI6i3 zeMS{yGNJ1pWltnP63`Qvl1Nn3m(g_C?2XKo=oCwT4XG$YHfT3lz0^K8@NA@n!e&Js-5&BO+oH5%mgjjswM_-_q`;ZIM;Y4U-NIo zn5k}Xgh)65pW282C;;jJ#7IukJab1>IQmI5k2##C%*SdJi-ZXg({!Y8V7j#bx&igC zF>~{2-Pkqjd0p>YXN~n=f&Xt%m}33_0kE9^yGMuJewF|G-NP;aKStTvf$ud{!CT6L zGf4nIA~eBZh|1a70fxspcS@?%6sU-kW#pMNr$7_lc6N3g)ziQMV0c2fV6y?3ri7}e z3FSmk`FySy2SA{Skwb2PM+C+0cMibL4qPPIC*v6)4eMDNhG#S+{!G7qfyXEf1qVUl z$#Hm#X8Z||v6+}a6CfO(gh*g6p6me*IR=h}s(w0tO@+h*j>Qw*T{iteyM3LGFpII2 zYJ|1}>hpHWF>@y=32<8tz}q_s!)}s>A&Dohex`bI0Fn&U0ThSQBfU|A7KPyqxSe7Q zh%qz+I?gA1fqjMr_Y5}lUl65Szz9Qx;#BqFGmHcdfW#oCSP3YeARl{`dGY#*3B<2? z+o$oEOj0=`8n~JCYs;DF$+-z5H%MUDu5#PXc1O+Ez&B7209=S>=ep+4c&1;zMiF)- zsr|w+lT(%O7A0~O+%?4G4vudKqj7{|;ocxd&?v-Ql4n*$j;e*VTucSXaXvUCh;v!j zsn8YzhT?!m(CxTVk|JMV1}IFXC{82Hh!0#axhaw{#8gNk`#dw*$v3$(1MEOgBZx?> zx315O!xOzdN(BA`e?UarF51mm2>2 zkWLp@uqBBMsm-xl-d_XaPH%VLnvM6NdCw z@p&dw%a_i4fLzaGmwN00S?IxW;3wz z@WFDay~87sxYhog9gxp|qFfBj?=#A>-*R2E=NYT#@01v`?->=;r{TbH7*^u!IZb0R zfUe`<4?U}tW1k0l*bCLm6u5oWS)xg5~%cMcu3UOVtgBIL#QTGgMU5Xaza zpnH8g#bT<7J-|swr5a;u0LSb`zv4&`KAu%KLY{#Q4F8rAh9xy~4=$-hkW?4!%H}oI z;!Fw8R!Es=*~YHMsL}%@h@l7z%%tvOR(b$}rFMM@R}#g}-B6gCGZg=l(=PO&NksSY zTq!7ka!@5;g+JAHJg=-YbD2SCdlb@9J3?Gw);>Que(~zWi-H}c7)j*}xppX5sc|%R z$8z6rSsGgwskO8zAMfnIsn%sIJxX3tnt)R82oCnLwZA1{2ooG*CKc0M zxhPPzfk-J<`8Kb{vCDwehF@s2Y20xv6zaqjE6#Q8ShdL80a-^2Ev^1l0Y8cL#w5)R zE^zG2RhK%`nSCPQR~n`fhUXz75nop+&w3W_jp~PI`l+6rUfu!#`oTQan|k@fZC&nJ7b0{jnnli>J5Apd$dt6+bWz#2%@ zm^aAxG3T#nfCq4nQSg=#f#1ZwT|G{I#{>BGN1}H3pEJ6d-_?Jh3?dQ}Nn3j*3Hq7_ zSnkN$aJ-#jrsNd7q$v0vg(&u=3B%xdm`dt8J(F926zs&um$O8whJ~7D2PwU`BRFb< z)W5mfph1&7{HNR@umJo)idiDUnOOm|SB6z3#+qSOVw%m}Sz#SQ+ zDhphp)CejKjjN$?Nm5vn6Vt;bL*p^=t#NKv&oOEdy@e@8p_qa{#r`!ns%k<>ELBw` z6x}OCCny!N}c z)t*1M1G(8Ct{$YlQ)sN7>^ipcnfzO>K8cASD13oKBu#i42YdkCUdK^_l+Yl5D4z)u zVVa8U;ei7%MkGubzML}5r!))(aOeQ=RIPd8Y1hdhTt0mN^Ne0zF}chno+#y%VME?x zii9?3ILv6O%p28_tS;N<3N4AbKw+qe*;7VQny5~HIe+P_=WNnYXh)m=J+KZTCB)^f zCI8jiSOIEZDk4VH(CS*Pc6K?%5E3q=Ng!2rXCTn6*G2)nz(B;wES#A z85@q(#4ZXlT%m@ zILKkK0zRwi(>TVyksV6p?(g^2C?e#Ab+CK3gea1d)RfaNzeH767V1KX}l`6A>l^5xoQikQ`d^+t_goQ7dWtO@0u z6iL1S@g5{0)<%)-X`z0cLt&ZCjVC1jP|}PQTNVcJqM!nyGyy_Bw|aRS3#V6}Hpjk{ z!G@OP1}hu3G}cfmXoLjum8W;4j65}90ThEysG!r1=4+vh+$K$%qV0S?v?B*IfRU5C2KTdQGoJPJx3v@ zIl&8R`;s_|P^=C-syL>pP*Ti_uPF{A?Yq!DpJf~Zncb%#Qc{`3W6C1CbL=dVyGnFu zlTQi<^Roblc2KWx{yo_E+u3m{#$JtkBbVGOntAJ+c$+ovHfh>z%B)*z(i#6iohg@* z_*icfW`|%nb8!(aBvig63B8y2NV`36%WlV*mbt>1DTxgKmSUyQ#8Rw8M65~6K8=!89kZEu0Y%E$U;3O?kD(=h zgd=sfdeH5?BI=2sq*71r=wlgaXF`ZZIMlXBLIXgYlZkSX%Npkm#zp};X&ky7huD{r zL4hV8U8pCN4dCM^eGWlGqj%nSP@l#=PDF0xsNUMr!VxZt16O22C@}Ir`2gO%*CyR~ zENzI{W1=ytJB@D#D8=T_7Ar4ACFPaLgZs<=wkL|2yk1zXjy^(J*gd%NlY2O-xy8&-|cs+ z@n5}z&X)flqj(b{oE>=c_Vvm61&m3k9Rs;z+|jQbsTir44ypVFsoLXVhN95X$HZ>_ z_#v^v+w!<&1^&-0!26ET$p5`c{Ac&5f3%JNe3Y`2hkZKErFE`?z}mbTQ+935sN99E zh{iMihnNbWiP5i7=xx=AKT=tN|4U&eA@#4<9ixf=`v=wizkAR<+VcNnl;TPc9P7z5 z0S=W2?Rj%p1-%OH85fQ>p>z^rln|5F0dSLNlk~l=6Oak!VgP)KdIyJtZ+obN5Bf*@ z{iDO~;dem~`{Scv|Dfl0zePP9eAhqd9E}fx?vdZ`f7eId{ez?NcjGY%j_`M*-l1co zWr6|pI^A#G&bMx-ciHO=I!A+puJ>K%pm)?gIy(GcTd7oE*5&{EsCG^K-#e)0e{~P~ z+x)LbDNX!e;NK74hYbh>pT+^v*<_U{Ql0N%JeP%}tt>)s%fp*5EAYQw(zXo>Tt6MO zf&aU`ey_s+y`z42%m0s2mgIkSJ6_l8@0T+`e?VO90mGp>Fxdm{qfq}saR3!k*gOv_F09J2uXkD%#m=e0>2nhr`>xn#pq_H0=|mQ z2;Cib01$^;9ES2=*#VA5I8aB^Bo-7RBxQ;Uyv6}=Iu^HxVGn*HlW8~ubc0A(3T!qJ zXT>a5DfO)QqHMBq{;Fjn5>4#yhTK&vid~3i{I}3?a}2tmA0&)MSWI!sH2Kx#W0XSv zuN8MHrE~SHUrQGc$@$IoO_lL$cAjm{@1G&(*ThAdfU-hFz73cZvqWkOLPf^sG40*Iw-b~Mo{c& zKD(fhmAHgogNCu!Dp$jR@ep7K0S^U&mzXHmDWW*7hU(gyAODHk+? zXy*CxSVg(ih&<(w&}ko|Hjee*nIf^*pXg;>zt>1@(qEdXhFN^U4qezZz3^B}jz1KvAOV$Zb6!>{**8qH2uk zd}hkTGo>A?fuz(HwYA$KO3dtKEu9e*C5t+j)pc|+`f#Ok-f(c0Ak}^M@RbIR#DR-~ zfML#`bv*T-28sjTB*aHSy|(CGwbc`1r{Im`g9M-!1Y)G?o2He$ND_)w6x2sR?wAfzt@9%um&7Nxo{a zXX8aE+M(LvWv>XB%PpPHF74{ItH~sl&*$?_`wAh|4A(jg)KB8ztC)ksLv&;OK3dh* zwQKd9M^`i!?0<8+*`x1J3TxYaYP(Nu_o*fORF!u$*KGHy?Ot`iy(+?j5uXp{Z$1FX z&My|7V3f}=5=J9KuWojhslY^&0o-)FZYEO}9|KfrY;Ir5&rMZgxX)06Z9aHS1??Jd zeb?>mx0aryu7CcwemYn~{73Jg*QuQU?eA~nzaFPJCe?d{Awfdj*mFB2p~)Fnx7iU6 zBP@lNjhS$qOOmMDyWS$1n(RxfV|h)(@&qk4T0(B&#HCc(^e!^vU`7(FHQjUKc&n_L{^`pDcJ6 zN>_$5cxqGoe8AyYLExwxVWn6@f->+K4sa|bwZ>k46E34vil)p_JAl7FEv}$B##(6s zS6BvcDf~C5ae~D3S?i-Gw@J^&$dnG;KDFRZsg;NO;JN|!X@GYf=aSkxRX|XE3q^MP z;Q(|!I8S3hD%n@v(_)jxmA%2G`mJ=Sf!22*BUhHjt4zS$F$J#6B5++HX5bQV^>HcG z_n_X{`LDzYuHhzdU5SIK1h%Jce=JPi*z)!S^{L#k+qcq&+0ACBwk&X`8)gx3Q+1uE zn?QjcgOL|oW?hX@9DsgxT3YKX&B~>5IBS~J0~xDvN(Juv((-)W@{s-iYKlW?$a^AL z8Vb~~|Mz>H!^-}D(C=;YKOUug{Mh~mZb&rH*V&K|i&=u7MG`Ik6c6B=wj%KMH_pk2 zM9Q7&?HW26%rW&`b4-&<63udM{#NPxo(>~cJGkX2S@X>eW{dqH!wA{Qi$#bq2DeZ5Z{4QBDhGh5|{Fb6nogV^95(T;Eyv4eARbR+4^BbP zQBlH0bst%8j;i%~pwv>1Vz|YF7l}syHWA%$r#!QAmuvld3tCre!_xl7ImaSpIK+=dFWI=@$Yq6pnC_7lv zg{yOhxC7>XF>}0@Bnx~pd3$sE5hXO1*0>#|m7ST01znhpG3&#)j0SVoG~B^&DHYgO zoMJ`#~^dZE5EJql*1^)ZN;Dk5ej|ykTg)q>?;8gM9c`6bWF? z?A76U!AVrcXU6&a@UpP*r zaWPOWdjVP6zzY>zb>UBOkje!*)maPr@yQ45r}@XOY;i%*gobo7`x(y$&??8E*l#Md zN^Q)VX7}b+UIIi&_|un_b4hh z>io@{%d4~Fi;K5!&R-OkO~o_)z-Wcw^zr%6cy^A*Rr09aUh58KEA6f&>DHc^XB8`C z5R5#P3Rbgjc3!_+oSavuV`y)#lh(psz9Pib;-o}_^WeWYzC8Z^_~N7y5pi{UTs1Ca z0a2Zty*zz>e0loj_0_94FY3mxZrjVcXkN=Ae2t4*?D{KO;9?fyDvO=iPnwlysV`i~ z$H*kpS)O>lV3o~wSPB)bTke`zQeE(eTv4*4n<%PxEk14SLCf{!|7ArDsm^sb(N|f> z!e48}Q|o=9@7hSsc_X$wnVLrBRjI^oHuYDkUhb(H@7dHrt@&xs&x1u;;QuZQ0l65_i*y6bhsvc6}}!K zr7ry^io*F3Or6|=)$J5J(RswLXd2haQE9Fg<`l_lXGly3P@4}@Y^oDtswWC{JuUXW zSECi?e4KNGsy7S9smoZ94cgiPSR#gzZe!jQkY9z!9JJ1{akdlb3d{B0@=5Z_dT;rZ z_g`NB&1D@t;nOF3@Wl1y|D}R=b@Aqx^XDg5ua94yJZs7JgL)bFkEhvZ%m-2p*lo8Y zEyvY6&7Q#fMkS-Nv~z04ciql@v%WADBKZ-EG9nXAvhzdOyL0B>EX&%^rS4mrx-8yu zJ}Y#~fa|U(=L`=l6m)Zf)7+des5h?H7css)QYqtT4L>U^H)NJe(gzM-?zSEog_O^- zpwp;S$(@(>Y{BMUR=u52x205Fp!aaCtmDJ97+dT(UBmON%gX*|UA^cOorUusK8blER#qW^WVv=y{w3s4Xocvxh>t?bl=J?LFdy0QkacwKM-z`qD z?&^>t%Y_^-uih}QflKn%*NAv;=4d4zweRnR~g{3img^k*Ile$7QdTa9}7}A zV%CSjH939qg&sCDDds(vT7zDa5eO!GR~!_Bq4gVEtWj(s!zVjM>O z>Xj~_+-)40p*T1WlPP+gM$qldk5@1>7IY(fv3G2?jmnR4Jgr_*Zvm~>oXIXe zd0wCD3yqf&{z5SBwM6yZE#ia1{ku)rVeaV2ezpLUsveVHg({UQwQ#m3{csHt3A9rzPLO& zzxwIT#pQhCb#Sww;cj7b=^HI_re-t{~Dz?s*fAsPY`7baRE=%JWv!y#gQ~ZCw zGZ+8YJ=n_sM=7__RHx^tv?w z)r)b_WztSsx6dmlyqQ9&T8tTr@GWK6R<$&apjHs1Qv9H>Jwk#HI^Mq5v30D+qoAa= zMu<;~2P`euFaCK~bYo!wF8)w>vI9O%W*|pWABdqe?;$WKDBFJW*?IH z^l32nTA)e6lzrGKdcj#<#FPA=a-9qLeOc=lY*>(X5OK*LLDo#;cfx%9Iy~2}?V#@eO8MyYNBfq5FSx_Fr@S z|9-dn{nvv|ZyW#rDCME|`!8W=+fGa3?6dO!kZ+-NiB)Q0mK;O4aTJnKh+FW{NRf5E zQrUDo3ND5xd`fS#?}GCGc%sJn^Lm%o=YLP?YD7g+t~f>$|93hE75?vc`&<40QHnPq z;!jKb+j0;7KR-Es@#@5jf|bWu&j0;>@1Xkq*Iw`FaLfOXQFiPG>^SetulG-x;bzDAS_NNAFVDF4j1N_oMI1JU0wN z8en)!u1SIeg7)ACDabDV;V@!)cbLFvr`ZnVawr)(fXA*b0qemuQU zqyNu~H?Lugkw_Wlp5yH7z~#&TcyaDH!{JbU{rj&Vw__kN7bpxRqDCRkB726zp=xv* z3r2&~my>Xu%PHaJ+x{a;1s4n@a+>o5Fd<@^jy#`6Sx}Gp*ERR8nvr&)aB=|V>)8-3l~>^ARh9% zwaYE(%zh(YF96SojOy7GkYe#|=4n4YtgZQ!>60KuyYEWR-ap+b{k;nuEAoPnaKZKG zPZ2{A7Lq~L_x61h!w6@6;{XDciDM_I2Xmai!#CetlH{9j2Jn(_0dnqU#@rCbP$5PR zr3w6(m@Ha@)V^v5IK%?0*F}UE$aEg}rcGXb9?1+x^hQbnv(QEC+M@^#h;!-_Byf<^ zfF=pOP@!s2pbSmW&x=Ubx<0f8I0c$CwXeb{&}5Y{ zVEFOZlk1q0`v|p66}-ltO%#M32Z5xF$st8G@zJ6Oa-7IsOv#cBaU$Z z2@Uve15S7-^JO&K={m$Wi zK`a0|ck$BVh0pG+k?O75MP=im(!m!4dc{y2P<5q_+<1^LcM=pPQxvBWX2b`sz9@dR zBAlh$eQJQcgCArKV;M-}!O$y$>?WPMR>Lt0`Er_SC<;!K(AeD19mwe@u}Q}e<6DhK zLBVGji3N0O8R;R#!r8RqcEq`L&wyRlYSndDf-A$CGR(###=%tvr^L}J_*AhR5=IGM zW~ePk=$%vruWaS};Z@V;eB@MpZ@Hi^noKr)X7TKXf~5~4&l*6NQffV*4eJht5$FnB zup*#KNp%UJOF4c+lBwp?hl^KU8CwYrKvs9{wFb*Jdbyd<@$yZisTAR?33DG-M?8tO z36WPdb{B%-nSCgJ&Ew*So1xG}a25hahDA7YSU>{<3Yubo$@~~F{CIDjt^v+swDfQ( zoEN$#=)mgQF<|&WD3{P9A9pbP_D8Y-0b2UF$FlzJjP(v<65?$0w6e;`0_`#_!+BM<$gOk6*yl48g{sS| z#iF|aXO{o4M9@D!`=$K~N0iO7bcrlQq8*Zm2(IAQT|!I)ym3CNP1PQj;k-;)tXzq4 z0CYUg^1UFDjo_*7YPT}G&B~?5H0 z!&%KA9_sy_#3V{1@M+A|?Ls%$<|bpFNq{nPKQElsyyV4jnwpEiR3_&p;jGW2FF7XW z_&sa0gg`gV@68$VRfexZSXI^znAYXTw_8)|qi@>U9>w8`tZa%(zwT zxSHkPFu!lwjLRV2m>t)Af!fM`n@8aHW;5>V;P+-T?(5+9W;5wtA~qw z@B?KK$!(Uza`)0__K6N_R`>T?Fg7rnA%cMVX@p~8I0GU~ zkPQcvhu+BP1?2!6&vA}Jfmw{?<`vHN0Kq?xU%k|}wF(p0@mj=0LmSgM+uU+eHR0qY z3nPHrsqCT>2d-kB@4X)GxMZx?p|udK{!f}Jpi+)Y!SwG6eY}5K_+!^(4=7cX{Eh*^ zSnl^4qS(iJ)!u!t$r>J#pP^X*g(1D=Fr%qlR0-&erVRX)3mSor*;YYwWHC@4oW?*| zpji~F&uHigoA!J1j~PTM7jl6@>MNC*#8Mq71ip`HC_5R-rRQw}FpdSAC6vU%l`sS& zFBk%+y30I+fTs~Q**RIZvS)}~&JhQIKL{M_JR8I?3_U&_?#X|C3x~=QQ9r%O??aP2 zl6$N+j(097!)0cZETO9t8YIH?N@fWm<|W~V^#db0gP8=|x9_v88WKy5VSZT22-o2a_+vVQ(&6SvMMh&JVa_YW)ipZ(6k_WO^IQ|z9l zt}L2jR^AXiKHUQ;C4n+9(o*l#Rw7HuYPNtZolDB6F=1S%7g~v>wRXpOWtBeb4u4lK zI`1KEcd}iJ#1j{}F}`)}uB9>*jWOnZX{>L{wrtC`eEIVK0{{U3{|~x%-T)i{0A)$2 AQ~&?~ literal 0 HcmV?d00001 diff --git a/pkg/action/testdata/charts/chart-with-compressed-dependencies/.helmignore b/pkg/action/testdata/charts/chart-with-compressed-dependencies/.helmignore new file mode 100755 index 000000000..e2cf7941f --- /dev/null +++ b/pkg/action/testdata/charts/chart-with-compressed-dependencies/.helmignore @@ -0,0 +1,5 @@ +.git +# OWNERS file for Kubernetes +OWNERS +# example production yaml +values-production.yaml \ No newline at end of file diff --git a/pkg/action/testdata/charts/chart-with-compressed-dependencies/Chart.yaml b/pkg/action/testdata/charts/chart-with-compressed-dependencies/Chart.yaml new file mode 100755 index 000000000..602644caa --- /dev/null +++ b/pkg/action/testdata/charts/chart-with-compressed-dependencies/Chart.yaml @@ -0,0 +1,20 @@ +appVersion: 4.9.8 +description: Web publishing platform for building blogs and websites. +engine: gotpl +home: http://www.wordpress.com/ +icon: https://bitnami.com/assets/stacks/wordpress/img/wordpress-stack-220x234.png +keywords: +- wordpress +- cms +- blog +- http +- web +- application +- php +maintainers: +- email: containers@bitnami.com + name: bitnami-bot +name: chart-with-compressed-dependencies +sources: +- https://github.com/bitnami/bitnami-docker-wordpress +version: 2.1.8 diff --git a/pkg/action/testdata/charts/chart-with-compressed-dependencies/README.md b/pkg/action/testdata/charts/chart-with-compressed-dependencies/README.md new file mode 100755 index 000000000..3174417e0 --- /dev/null +++ b/pkg/action/testdata/charts/chart-with-compressed-dependencies/README.md @@ -0,0 +1,3 @@ +# WordPress + +This is a testing fork of the Wordpress chart. It is not operational. diff --git a/pkg/action/testdata/charts/chart-with-compressed-dependencies/charts/mariadb-4.3.1.tgz b/pkg/action/testdata/charts/chart-with-compressed-dependencies/charts/mariadb-4.3.1.tgz new file mode 100644 index 0000000000000000000000000000000000000000..5b38fa1c399527b1e3dc452bd90e5e8312c7051a GIT binary patch literal 8401 zcmV;?ATHk@iwG0|00000|0w_~VMtOiV@ORlOnEsqVl!4SWK%V1T2nbTPgYhoO;>Dc zVQyr3R8em|NM&qo0PKDLa@#o4;QY;}=#zIZaXyohf5h3)n%F;Rh*_B1Oqo9M6o9s@NuhMx)W)=mxsMh;T}RVfV#^ zaOq7+6n?k%((CnlhX)7xXRp_*{@mT$-}`QN|L~yq?C{ys-rje;-MzzJ?>p$NAC>u% z3Q72Py*u|+9o+Avkc9q%Tu>JG;cC|*Ns|A$+w*q4{R1aJ;d7cu{qP4Oi z1Hvbv`HjQ@AQ4lL3{ru>5{zBJQtqRwPgHvnLO>)L5`hkYm;icmeI94|jig+%T-6Bc0~CQ>G%-|bq_ z@faUF)K|pG-VCXXNkl!LMO`8UO3@XP_?IGU)1}ck``y*A+`YZtr@j3HFNw#_B~Gsy z4@BQ_!PZj0O~uF1ac2~C!8}Mfi%?E56;4EGED4Q~>&6%n8ur0w*~5PnP;&rcq6TE2 zxkDx$^CaukOrHjojcrWjBpvFZ*>>4aH(>rHa(9MtO`936T|J)Q0zV~;OXSW~#&`$b zzPIaqYsvU?l#C&-NSLDdIt##J{l9zg?BJlH|Mz=O_cr?fKFZb>d~d`zS9dfbV|2E* zfa7SU)06-k0oQWObLr1GNRqMWI$K+gZr^tRIF6~1eA)-wkTU%=VS-Ago>yQv0FsRJ zmB$TmNJ;Ge-2vFzg0lpDI+_A87@nkIc)~*JPtEI>I3j5%g<5|e9fj9qDjoqHnaT>T zT38;1M52(7b^t?xAW-V+uSaj0JmDxXmX8d$eDM8l_c9$Kk5QuVDC-8y7u{4KcgHCW zu&X-|-K~V9o1|e#-RmLUi2PTQQ6kVUiBRWpih*;p}>ql3O z#0<_&IK3hg-E5Y-*=V=)crAW?{Qy8MU#F(#PdGI%-;xL&MPH`5n6>!zi6SfGvJd=L=snigxBH!Tlp;)t7BPOg^Tx@k)-3st5s!&}>?de#D^TFMimUK)B+ zGi0TdY>$edMljbtmbzhBD{gvGu(p5CY*G*kJ0q`RZK4-mD2%B9)GvhAeTG~tR%+DN z6zX@&Y@R|?_pk#jR;Is!9TBBsz_Ok!NfQ=31zA!R zF-qk0CFK^1{!tYf!v}Nl&Lrc?WP1@3AqrRoIHEBIJ0MI2{r;`ibpNCH=)VeWtbx~Y zJ9W)EogKKIP=5je6-I3YQ^n)H;)5%y+))~j3-Hp|VgZkpf(eYxxebhGUBzh_?m*1u zy3?!)G#?SD7CYci7}L{RfH&(Mxd-rgoa_OM;po>hH3||3ga^Rl8KfV7dbOio!+(e4 z1miPF{LAek>9sWWBw6?6R-IY;dN3Uw>>M}~2fUD}dPDDOkkT7J|7+fSogWo-TW00e9;F2KV zD8%!gr~etGXrO7+f=J*cG|&w~Hg=J7)__aM#@SAi7cwKN{)BMiOXRLV=?cB8`25}Z z4}UXnS!gyRvp1<6{q4f2?(o8=j+rDsq!`N&cc-MCLbrsKLC3NKiOCrG8oFAExK)9% zR*zysyJ!0`MInW@#6ucUx$;WmvnWZWVQsyUm@hQ>lRkX@;sAv73S$)FgtMVtdg@k- zTocDsQWDbNa>be1*}k^aB)LQoEJ9E+qgzqaVu_n_Sno_s+HaOgrk_SFvy;d2n|z?h3EU4AS9rz6sRA>(+4pIM}*|c z?)w;1+tjYLbMD>HXjuQ|R(pkQ+N@Ix)~TxO7^&H;RhzZyc579Hl2c!+ z_#AWNp#$XS7fmM^i-rgZkyLMXPH2c`|8zpQ-u)V)QeIiigyWpkq^}h7uSEJ2yK|9mmI_H^ zD(t3}2E(F=#6cgF2KHqLW0a7vK!AGM!P~ z+#XMPO4Jk*pFg`mNBQgG&a_oAs+L9XOxwy(Mo%wlUk^EqH3rU65YbqvH6j@YpJRZr z($t1}@9Y6gUmr_3Fx#l@!(Sd3XV4yFEj58lOar(K{A;OPD-RFAbp!NSfZL99&Xffkqp$bjrYz;NP#Fhc*MrkE2BJ6* zi^FN#pzbJGtwSnE*SFT^tCsoo|6+ona?d@PtSJm63s|EIgp4ma!neU#6i zyHDVXMtyyLp`0=~P4Ic7$l_114^O(9!MjhKSDzB4ce0=oV56Cl;5oLQYmaGWn#72l zJA13`ea{3O>lHkcC{^?L3OT0%!j1AIVjm9o^{+HKOGhL62|Df!n36fysFdy#=Y>9- z1(Nrn1bzx1QxfX)Lm)|_G54Hb(LmSrrRqbCLfJV!QLBM=#|kcq9`A@^7ziL7jf;ta z(n)ec1w0;3H7#DgJyUgckVk_4_5!lZv{5#izvy22&zGlB)-vHVmLur=Rk(i@or-;A zz^+KnBEDJ?&ZFC+!k5A=qlRmG=`4q4D3%<027XDnaWGGc5-qCh$ZBKMtye>(mUFbg zHFsWQ8uPo2=!P4;Gpkm)&VO{EbFnfm-6zf|%9NYPGILaJ`ZbcrSnAW{#Eo99m_aY5zxTbLLsve zu{?!J4OXI5wPh>C$_>6I<-YYQ_~$M2_8-mmD{T~y-gAvea{Jiu7W=Pv*xN1Je|x>B zdj}i)?>;6>qU(9B4mSagb|rhJ*tUk|BoK@mi8J z?8(g9+tZIIVX^YYvtBy+nTc!wVHd_6hTSsi&w0~u10Pc+F{?O1P9+&vYT8YuVr4^$ z&!6G3_X7=4ct3`02pwfMi>{4}!&vfZ!e}ftA%dY7bf0v#y(3}B3O7LG0AmSz55QrZ zmw!X;b;x~I+U5UQ#s52ew(s9l=p6x%|od4WQDatoV64AZd9ik+eq@P(XI77KM zS?{z6`|48Nq#4#P?IJ;64aM$3+c4sGI&|j3qS7QF63-+j630_R9+Tp9%5)CdJ6*wn z9JHxZoCO_sQXub&Bwj?i68Snchi(NBE6h|zKm9xUDW`J!g2fU)$=MKi8XpOr%mur> z-fsSUB!1*9O-fJg`h_2>Dz_MR2Av7vK_^23fMoG#IqZZ5N5w$3`~~D`121)O)tNuR zAXO9e+GH)5$FDx2pJw?fv*Lne2@BbH`V&t3&?(2DOqh^*Qc7*?oStEbzGS>#c?l2^ z=}&&H%=@gY$N|NHE-~Nv4%`w{7i=VNOZ9}~2>GqZ?!q(Oet{z)MWr(mkh#$sMp*9g#2PFMZ8@x3`ZeSP%u`-{`#RZlkE)cf_Q#10B~_|Er1yQCQr_P50?Al1p zc_+3!n3_iAS*gTsw!te^FGH%vd$xfp*Du4Gj}mQzR&7y+Hy>Tv2Cv$p2(N}W>lv#Y zf?LN~bwRohmbEIwxjS!F0N#nYDzNU#T@`S5XRivt75QrxdF&{y%l=KGaJ~myXSRd2 z+baSwdBks68rSJj<*pXS6sc+_L{9opn-5ZKY7%0qCkl0tns?u;(F${WoO7M(Hx2#N zWh|%$U9$mLVuqnN%GQmZ~2t>4{!g@bR9erlSe!7 z$o19#%D}rgJAQZi;?>35qc^XfcU1d+y^j0GV;vZ%+`_&x1Gc*z#mjN^PP0ewq1DK! zOzoVR@%P={LA$*$kuv!aWtotPVcGdEvb%HU-z>`7Fr}(6pDwZFd{yigA-B7vp0hI0 zsOaVtr@c9$U+-M6u44S`NTp1p74}(Su_LoslisoWGT3@z6jDCRiq4QyCwHD_rx020 zWp%d`>Xwx17U(UuR#w@=%qZ5pak|3JvnfmOKbz{sUDTSCRjA#oshNgC%Q8N$*|e#_ zb+H9eB!J1ttLS`_gj;9D6!8-Dx0+G6vsE$2cdp%I{HWvFmTbRW9Ae$oAw`i31um}M zGTtKIwd0=K$txHV)!y^%G9@HMrp!{Mb&lEZIA(oQ)>oR(f3N&?-3Hnc|FOG&P>uh1 zdbq#2|M^}@ZG4Aac-l{V8%*-s9{E1V0FPySwNkonvw2yl)({I)IAS)3!8JL3>V@ex zcPZv~ER6-dAR`cThB6Gs+0iRYNcgNln%|-z#}bZV&7WK9&DM{ z|H57~yS}TS+w#Ak?d@0f|K9E<{^wpwW*o;%&W?cWUAkWRGRAEksn)Pys0#PKUVPJa z!RY7XgnUdfj{>Y7_GOgJuZq;Y)b3DKj^XB7-oWti0At?{zZi#6KYOJMD7RaC<|Gb| z!em0;rV;G+=KCu+8Vz6+lA!W_C{NW6s&6#ZT$@A)5Uue9L@f*>& z)fCl(Ym$S){kv`A&`8gwshj<}lK!t*|3h+x>kR*IkN-cYe*fq2aCejcb1%ipF^PkP zGM{@9Isc{3GOQyzz+~D!02^U(azKfgjsD=*o&P5JUt{=xYyAJ-)2jSG+uMD% zk^lQB%dPk8%Mw^tcGPao!X=h+;i%{Es_B$t8f*MJU%<0Z@da^ z8UUXhpVk2G?C%iSE zLcR{tt>feCfZZWFz7E{oaxXGR?2nOpQ9GJDbJbcBFKR*Ek+0U5c2Ns&omm%I2lth9 zQOo9=^oUJ*#3nsrlOFLk(j!*ZFw3VWH0&?KeAx6HK7Zf@g^E}BAoCOEC*eV)C(NU@ zRCYp5vsIE47B~NDxd|;;-+yYtCQIQfm&Wto^@abp#{VD8$A2Ci>}}3}@1v}K{<~_H zz@Om6catLUP*VizroH~?>z46zzxOh)|0N3P@-&W-uW1Eni~ry6&Bg!q_BQ(eUP`TI zKVd?OfZZ*G3ibiyX^EqL$OkApbJc9OffV5?)S5fl&c8D88*3A{?`gjba@(kVv_HO|L(Wumf!+4?kX3Qo^d3>aU4zyUuj*1l?t0W z?i~ENEt;h%;buVGN`Do%P`mPyaGCMWTf9QP@eA)%>ev5uX8*Ov{~zqmegF0F*(Uz~ zUdmnX_g^5;Sv#YN^UuovL%oI0IqKBHJUNC6yHQAoA$H)i)gr5WrLygK6kLQyV#2QT z?}Cc|e5CvN^X@KX^6x1NjOzHvwfE5`|GnPRiu~{QdK>%yUWzxS@=weBo07}_>8qoc zZ(ey(@D=)4EdTradrzz1f8EDoW?QGcAQwQ*_Ybcb4+9Tg%m4R({;zW14?$_ z2jb(9u}cl@N5;k>dX7`DHnN@rAfgBx1RKd~!cn)>-v#Fw>fH*3Z#GmaA>r(b1}K2U zpzx-#L_Q)u!ie$Anu*7{jR)rw1Z`nUxH1-(8M4KQgo5Sc`0@3b?)`tB9lwPU5}9%o zp5tt7!THbs`SR3p27`hA`uATzE!;q3AxRi2LJdRABYOsefo}9VmYfBtuLj{b=MyUI zxBZ8VNg+8&)G+56U`*vC9eO^C@}M63)3x`l+Mc$daB=|l>)8-s60&K8u>=;Y4rZU! zHGTBE-BPy1cHT7AlvAwo{+}2|GnDL3(KaN+za(RXh{R-!+)(F)qfWUIRHASkxA+m? zUGLyO;F`(_e2QIyqN%z zGZ{0ZDG(*2w;gBp>0#r|r`((bY2JNbdiLS*R_X6;5UAM;LMo&%pFbs>L@1Sj>hJCQ zB!(g8urYu@XX0c7G@Uulf5DR{=QMfpqz^w+AwiAZj#ya67-+(%t_*{}i)qswq}i)( zfFVlMuZsjPu<1PQZJSy3c_MR+*p<=(cA|^a&5j}j5FwaPiNs*W1BNBcM1{IRfinz4 zzbF#f^gCi<$gcH3wF;aOJf9KuxIlfYvokK3bt@-Gc|Pe^W*^RRZc1BICmA59D>t=I zU!u7TR58TztqRQzMhnZ0bK@>wR;hc}Ruve|4GhW~se=Whnt8tb5Kz$j#o zvIW4~=sCkP+tY0XHpBCE{(0ji`hY!o6@} zZi;%?w$caYiyA3BUzio~^~s&)y?B=1f~PThGZeMe(cT9P_V;^-w?(ml=-kCi%@d#B zS)U8})}gkmvIwG@q~O=xU~a|3EPT5Yls#CUD!C>i)1iEQB0OpxwU zGCbR6JnM08-g9IZwOV%Gwc^UDrX8~pjWM{$@l-TghMqc>L*c0K%N@1F1iewF;6+yX zc62rTIUhMy4{fICi!M_ZKGQt9f#m6(*s})Fg`8TCXv@4qp$E2v6fBA8LRMXX=t7BK zlV+;<^kMVNYiBEA0jTP3cCEppg zPTqCjV8r+|PnXD3B)TDus1zE10}^5u;M(P=HdT92#`8SovEE9I17M?3p6>;TY6OoB zsO`$=)~lCV)366Op#Ov+ju^)(l2q6{Er~%9;~N<4?)BEkp9}K42N`Al$*zHoB*w~0 zN|28jkIdHHLVE7r5zPg7mNV1_w#Q#57#HwTl?1R<)?+xs^R5cC8P96|@W8C^G^SA+ zfzM*0Zx_14EH@cNt^vx#{knKo^OBqKv^AU1)Gp^b@vP6IFKtZB$$Q>r0fVkv-rFlVygyx|94*Sc&*bPK$os;y{sBA~d2hKeVDM1nz2zc;Mm+CL-pltFF5LQl zi9%nR9H-F`Ij|Av#`(R7^{v)K&-rWU8o=JbnZ{}*H*S4?Tw}I!74$du<7yImYu&hS z?Z+)ndTsHK?$M81m>}Io&wKRa>NBQmcWS?>A6LAEpk<4Gkso)~prO|0$5pO_X-0K< zKh9`X?Z&NJj@sS0CHy!IYP&kRwd$pIHx5?u<7ODI&5vunrfZN>rp>Nvpyzt*y7o)J za(}bguB*dvhUXgB_jWgKRlClj{#Eg;bK_RD>oN+j-L7kMIgFsG(guUT;UX13BRoDGeu~auOP*k;ds<|0iS0Y~x zy%vR3%^|u}Fx3qVu6o{X&%1;6sX=s6Ow}BsOGQ)NG{;EY4$)Sdy?|$fz21Q58e%|n zEGZeXt5T?FL=v5}K0(0v3a*rsGYm10%1}Pc`$`C39?QLaxVQ&DFbI`L7=aK=@dL zQ-}_Vf!YHzI{ZYUB*dK+;<^8q17P~(jLAIe;kk*9%l_O+1#&NoS$R&9BoAQG5TG$G z+)Q^CxGd1d4QH;bjpri1&(c`Vf+y4f%P0&yI0^!4k|M$>P-%l~1dv(ijT&B14zTeY z=O~oOW1<$XaJmBo{(bc3XX9JzFmV&FMQt>+b)ECYZN{oLoZMz%1aLi3AS!X-I@bBt z^Wlz4&UzhL3&HCDW~oLh6<7+Ue_w#{;c?-QZJRxyO;P$A0)dfQ?+r$=k7m~1e{a|t z?vkHjM1X`LyB08IshU)U=#-@#{8UI5fr;7HL36YjDGy%9z<6Lp6!m8`%z$nC9rec) zqEtvVK_T*CLFudGLB_oQzD_! zDCJQIAkyfb>NUCpeIsZV%b#ECVFzs}s&8`8sXTK#Yabq$ze^G@Uv!lsamOhQu$%AK zN!Q-<(A~B-J{Xj!Ff*AagxHr=HM| nYd2f9bcUjJ#=LJ$^-bB7P1%%hU;cjp00960B}eC=0O9}utS^%_ literal 0 HcmV?d00001 diff --git a/pkg/action/testdata/charts/chart-with-compressed-dependencies/requirements.lock b/pkg/action/testdata/charts/chart-with-compressed-dependencies/requirements.lock new file mode 100755 index 000000000..cb3439862 --- /dev/null +++ b/pkg/action/testdata/charts/chart-with-compressed-dependencies/requirements.lock @@ -0,0 +1,6 @@ +dependencies: +- name: mariadb + repository: https://kubernetes-charts.storage.googleapis.com/ + version: 4.3.1 +digest: sha256:82a0e5374376169d2ecf7d452c18a2ed93507f5d17c3393a1457f9ffad7e9b26 +generated: 2018-08-02T22:07:51.905271776Z diff --git a/pkg/action/testdata/charts/chart-with-compressed-dependencies/requirements.yaml b/pkg/action/testdata/charts/chart-with-compressed-dependencies/requirements.yaml new file mode 100755 index 000000000..a894b8b3b --- /dev/null +++ b/pkg/action/testdata/charts/chart-with-compressed-dependencies/requirements.yaml @@ -0,0 +1,7 @@ +dependencies: +- name: mariadb + version: 4.x.x + repository: https://kubernetes-charts.storage.googleapis.com/ + condition: mariadb.enabled + tags: + - wordpress-database diff --git a/pkg/action/testdata/charts/chart-with-compressed-dependencies/templates/NOTES.txt b/pkg/action/testdata/charts/chart-with-compressed-dependencies/templates/NOTES.txt new file mode 100755 index 000000000..3b94f9157 --- /dev/null +++ b/pkg/action/testdata/charts/chart-with-compressed-dependencies/templates/NOTES.txt @@ -0,0 +1 @@ +Placeholder diff --git a/pkg/action/testdata/charts/chart-with-compressed-dependencies/values.yaml b/pkg/action/testdata/charts/chart-with-compressed-dependencies/values.yaml new file mode 100755 index 000000000..3cb66dafd --- /dev/null +++ b/pkg/action/testdata/charts/chart-with-compressed-dependencies/values.yaml @@ -0,0 +1,254 @@ +## Bitnami WordPress image version +## ref: https://hub.docker.com/r/bitnami/wordpress/tags/ +## +image: + registry: docker.io + repository: bitnami/wordpress + tag: 4.9.8-debian-9 + ## Specify a imagePullPolicy + ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images + ## + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistrKeySecretName + +## User of the application +## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables +## +wordpressUsername: user + +## Application password +## Defaults to a random 10-character alphanumeric string if not set +## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables +## +# wordpressPassword: + +## Admin email +## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables +## +wordpressEmail: user@example.com + +## First name +## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables +## +wordpressFirstName: FirstName + +## Last name +## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables +## +wordpressLastName: LastName + +## Blog name +## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables +## +wordpressBlogName: User's Blog! + +## Table prefix +## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables +## +wordpressTablePrefix: wp_ + +## Set to `yes` to allow the container to be started with blank passwords +## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables +allowEmptyPassword: yes + +## SMTP mail delivery configuration +## ref: https://github.com/bitnami/bitnami-docker-wordpress/#smtp-configuration +## +# smtpHost: +# smtpPort: +# smtpUser: +# smtpPassword: +# smtpUsername: +# smtpProtocol: + +replicaCount: 1 + +externalDatabase: +## All of these values are only used when mariadb.enabled is set to false + ## Database host + host: localhost + + ## non-root Username for Wordpress Database + user: bn_wordpress + + ## Database password + password: "" + + ## Database name + database: bitnami_wordpress + + ## Database port number + port: 3306 + +## +## MariaDB chart configuration +## +mariadb: + ## Whether to deploy a mariadb server to satisfy the applications database requirements. To use an external database set this to false and configure the externalDatabase parameters + enabled: true + ## Disable MariaDB replication + replication: + enabled: false + + ## Create a database and a database user + ## ref: https://github.com/bitnami/bitnami-docker-mariadb/blob/master/README.md#creating-a-database-user-on-first-run + ## + db: + name: bitnami_wordpress + user: bn_wordpress + ## If the password is not specified, mariadb will generates a random password + ## + # password: + + ## MariaDB admin password + ## ref: https://github.com/bitnami/bitnami-docker-mariadb/blob/master/README.md#setting-the-root-password-on-first-run + ## + # rootUser: + # password: + + ## Enable persistence using Persistent Volume Claims + ## ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ + ## + master: + persistence: + enabled: true + ## mariadb data Persistent Volume Storage Class + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + # storageClass: "-" + accessMode: ReadWriteOnce + size: 8Gi + +## Kubernetes configuration +## For minikube, set this to NodePort, elsewhere use LoadBalancer or ClusterIP +## +serviceType: LoadBalancer +## +## serviceType: NodePort +## nodePorts: +## http: +## https: +nodePorts: + http: "" + https: "" +## Enable client source IP preservation +## ref http://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip +## +serviceExternalTrafficPolicy: Cluster + +## Allow health checks to be pointed at the https port +healthcheckHttps: false + +## Configure extra options for liveness and readiness probes +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes) +livenessProbe: + initialDelaySeconds: 120 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 +readinessProbe: + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 + +## Configure the ingress resource that allows you to access the +## Wordpress installation. Set up the URL +## ref: http://kubernetes.io/docs/user-guide/ingress/ +## +ingress: + ## Set to true to enable ingress record generation + enabled: false + + ## The list of hostnames to be covered with this ingress record. + ## Most likely this will be just one host, but in the event more hosts are needed, this is an array + hosts: + - name: wordpress.local + + ## Set this to true in order to enable TLS on the ingress record + ## A side effect of this will be that the backend wordpress service will be connected at port 443 + tls: false + + ## If TLS is set to true, you must declare what secret will store the key/certificate for TLS + tlsSecret: wordpress.local-tls + + ## Ingress annotations done as key:value pairs + ## If you're using kube-lego, you will want to add: + ## kubernetes.io/tls-acme: true + ## + ## For a full list of possible ingress annotations, please see + ## ref: https://github.com/kubernetes/ingress-nginx/blob/master/docs/annotations.md + ## + ## If tls is set to true, annotation ingress.kubernetes.io/secure-backends: "true" will automatically be set + annotations: + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: true + + secrets: + ## If you're providing your own certificates, please use this to add the certificates as secrets + ## key and certificate should start with -----BEGIN CERTIFICATE----- or + ## -----BEGIN RSA PRIVATE KEY----- + ## + ## name should line up with a tlsSecret set further up + ## If you're using kube-lego, this is unneeded, as it will create the secret for you if it is not set + ## + ## It is also possible to create and manage the certificates outside of this helm chart + ## Please see README.md for more information + # - name: wordpress.local-tls + # key: + # certificate: + +## Enable persistence using Persistent Volume Claims +## ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ +## +persistence: + enabled: true + ## wordpress data Persistent Volume Storage Class + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + # storageClass: "-" + ## + ## If you want to reuse an existing claim, you can pass the name of the PVC using + ## the existingClaim variable + # existingClaim: your-claim + accessMode: ReadWriteOnce + size: 10Gi + +## Configure resource requests and limits +## ref: http://kubernetes.io/docs/user-guide/compute-resources/ +## +resources: + requests: + memory: 512Mi + cpu: 300m + +## Node labels for pod assignment +## Ref: https://kubernetes.io/docs/user-guide/node-selection/ +## +nodeSelector: {} + +## Tolerations for pod assignment +## Ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ +## +tolerations: [] + +## Affinity for pod assignment +## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity +## +affinity: {} diff --git a/pkg/action/testdata/charts/chart-with-uncompressed-dependencies-2.1.8.tgz b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies-2.1.8.tgz new file mode 100644 index 0000000000000000000000000000000000000000..ad9e681795cf96f3c632c8d6ba6c406be3b96b45 GIT binary patch literal 10953 zcmV;)DmK+0iwG0|00000|0w_~VMtOiV@ORlOnEsqVl!4SWK%V1T2nbTPgYhoO;>Dc zVQyr3R8em|NM&qo0PKD1a@$6i;P)F(afbS4ZDj=Trc1&ZPkoB5B-;3*k(9W;^}56+ z&?I{Vppou|WX5vUzQ(@ZKFL@91yc{^5T2@bKWU zd-S(Xue0Cl{tY_!8g0#za)H?2Iu9PJI=MecK?(U4GfrqcfSay^k|g_c-}}z{)(J58 z8A*hC^cIgGNk<{!QxZ=g36U667QvV@7^Nf(h=@@{)GNd}7QD>`@~?S2Yt|;wB>&4*uiaj+ z^P$(@_mX(xT;rL1%?FMPSv|)EKa&5KFgmUpP`<|_#|4xmA@Px%spCR2O`HgkSRfK( zrnb>6fojgdIt0 zzi`atR3*GciChJD4dJ+h;~T^{(+D%-0~bsxMKXq%3Q1(2XC^zjk~=fN4)ipFh{RfT zeP$eH96h`w=_gzb{4WZGOIaHdhR5W?XNE!zb*4rdz-@A+#=p=4b~wX)sF)`V>8;}P zOs2@kBMed{OR*)*5*URjzRoz1e+I}EASY2GW*O_i408?itIIQxgbe`>$qi;Ru=4Q1 za;d$;Bayh({+u0<&wrv^49xE{%Cg^bU9;yItLN{O7_;vg71XEUz;PH>;_Nw1V=;iP zuOvcVe6LOYISO$Mz6QG2 zw^J;pn%D!JgjAX_rUr1#ZuBdT1mWXZRT1(GY+(4elrSu*nR{?aC4!{6U{^M;sTOBS zc(y{yJUiE*Y34G6(Do>#qjrS2z^r|Ka{S`ei5CSs$}p0~8FKAVt}^3j?2e`0 za9J8#BStS=#lO2M+Nrg)DIf3bz^S%nEImqIQI>!*?+6a|vbDb@VF(i(ViR#5kN&gN%81sM+p25WYVE=8l=xE!G(&O-W^y%s zNEp2#(uX7Qq|{>?8%W@(q|HE^w7Wo=9V|}6a1UZSH=rV1Acc8>Y_kXclv1^9g&uPV zF!KPOPLdwb7>?gw$eExxKr8?yF0#IU{P|>0R)GHjZxS3|2;^VyW)y6r$LdE)0X`VJfNX^h~M%DcFgRFK3A~4GT4m1}VL_BREPy>fc-` zXwW1N{~t@5^1u~J zjiA!dxEdOlB!wk8F+E%|G#(S*I_GBf9Ag&ITbN=LiYfS0>|b+ZswR}g(o{u4(Y->1 zLZG8-tI9uV^cck_)n2A~@dv>W&_t_tY5GVjGnST*)PxM9fT+I`Mn`zw`d!;=&!5|Y z+-wk657OQ#G*(Y`9b5TK{w-IZ#6%DjzQ7@pEtqlvAHM&2MlY|JTxJqalySEZjW+#zU^j%65SLm@{;Snk z0cu|wBF56t=31?Fb~(im5-y}mAWe3)e`E;Xr#F~cA5BTda`$G7=@sPyLUN76nXaXb zoDqiqPG!Hb_73)7lnT2`g>R(Xh$z$Vv;z`j97x5fN8;J8nDHyr#9d>dJ2S zc2SVw3N<7K$^va}8ysD}ypS5IyvTlTe8Dkr5@5jNG4_S_l?t;`bRZj#kbjNiAcw^Y z_^hf=;~4u!b|{g%zu#A*h>#c7!Rl-YS?*^^`1cg4sZGBC`=LbBtsI}L9W_0W)RUTK zukozyVc4+UB0}qBkFCqIpnz_GzcZRX zVb-RT6zzt1LN)LT##4iw zdys@!J4Lpqh4yg{g=IE3o{;!M$ud@KSs1{Jf(nGv1qk`v>g8=LoL+g_9Q#rR8(NYI zRyJ&Dtf5rU2nph=o!*gh^3;F@Pz*Yuf=)Y{udOFFrTUr`#7hBL*vl<0ud?*i`QMDD z4CrkP1!~K&E)6BigeVB~{!gKzq$M+k9^x9$w6|KIT|T9080dW>ErDEFzCZc#^ff#` zIlnyp;q>|O<%xO<)Oe7EI_DS1aCUzB>+$6Y{Cx6H^(OOJc9a~kgM=i;Qbem^P|hl9 zEypQSu6&wQ@i9m4R&u4Wm1T$nF>I=h8HEhPy^=j8hmf(XWSvGV3Q(S^=P2YfCwM_^ zUlL~#iq(Nf6~{CcN{U(WHN|11_bznLXBme;=JzRxlvF11n6k)fj-5qvSBVbo@=4)f zeiq=+8`SH&e-C#4c6OYKvsbgdkxT9s-MsZ(yv@3In{@3q<<>2A>CFB>ohz4-_*kn6 zqahg1wzvov5-MMkgw`cK(%T-{QI|bNI{WpxCMn&g-t5xt6-9yF3Rd8hUJaBqbJZx$ za_>@mmfenVEpvl0GZGp8Eyc>9iKSSHh**=BeHtaHI%YG`0*aKgzw|k)9z#q12uJE{ z^`P5(Mbr~NNu{0K(Z@2<&x8<-aHxHcga&{(Clj?rE^C}OI2#4%q;u$U9AaNe1_hdY zbfKP5Hh_tlnGe#5tcl+JB_^)n% z%m0s2ya^G`4!n8$`sDlq#w64m1Np|dqhC4FFj6rc()bHfrQ>0SqR`RD#BToh{*l7l z^4Mi1{?B~C`;F14|BovAzk77Ff3)TQ$0$2_*r(%M+RarESi4sd^{@AozhZVEXksjB z6mIQ>KS5cE|4V@;A@#4<8>5N;`v+D1-|HOqw*3DXrMS)m$L{2r0EbG1_Pjf+f?fsp z%odI}p>z^rln|5F0dSLNlk~l=6Oak!VgP)KdIyJtZ+obN5Bf*@{iDO~;dem~`{Scv z|Dfl0zePP9eAhqd9E}fx?vdZ`f7eId{ez?NcjGY%j_`M*-l1coWr6|pI^A#G&bMx- zciHO=I!A+puJ>K%pm)?gIy(GcTUk|K*608Ajk_lP?;Xtf|DB_){(qFx#Qz2U{osAr zfI#qR91xvNR*53j`5tEHvXHcuMd)pLc=Kf?{@07zwqb$mrh_){f4A4~RrtT(JKEp! z|6`OT`Jdg6*Y*1QS&t8fz^;ubON!B1o|4QGIE5D80x%_ickn8hlko)uq|O;*ld zwJb!UiT&M>yJ|(T3(<`K7CLT@K^OFcgwY6#DNgyOGFI)N_ex65A+BkY!`fE!39j&m zgffAdQ!y34G-v!Z%6;Vjy6M2n<$rhoXn(&d{}1*LxAOlnN+JK(iaV9kxq8;GrHY5- z{O0zi%J?;!XPfi;SIGG_agipVtPqiJ117~Rk=BAxk@0y_IRx5#RG^$9%UGnPNgU3t=*vgj9AHWxmduQjCxhRMwXV zms6wy$Bb96jjSgs1T0M^3(tnEKCnN*Sq^QghX3kfmdCX!DFfoXKnSc*|olgr;+-PNcj0| zN=XMRq)*-_d2-XKlbcPP++5mZL(*iHE?l25S-Su_D7KJBQ0!?wyP&R%W1al;iUy@@ zjXZuf+vc55(yZ-}liy1{<$0Xup~)xDqHbT!F8=GK4chlpE@%YN%=6>1igKwDd1^mG zr+tjtIM#n>mar6Y>WOxiqnRI%+dC#E#--!(H4bhzhTifXlgR+?Tm|eqz47s0&*PdT zu2k15s5iXPle9^hSB4F^@9djYjK96o zqC33uiOzO03EdJ@8XQ9oDoKNFV5qf_TV)ty^(>86UsKc6#H$;Y$Ya*w z<0l7TF1@r~TjYsoD*KcCo(WECXZy-h6X+TPrx8|}pQ_c8eAQylW*4F8hwAMvdqu!p zZs~k>X;-gZO(v;)KA(5mR|u(QxYl8yei8>?#T*KEp^DjSRiI z*;%Fn6HNwi)A72QOj&#kP^Gasy_BDus>E=gp#cLii3g5hWAAX6D%O10SEUMTsJ$Rf{!1;JI4wj?{)U{ z(kQmi3C8TbqbIF^!HV=uo6mr*K3Q?^k%fWJR2uAn)_T4@1SSO#z@{5Pj@g2eP$ z>!T;tq-SGfN(XMAT5zY#%ENtd-2nSEz`KrfN$s5~AgI2DB0K(Y0JcV>M2xz+GQjp08UTrvI;|IFzouCz2(hKn?o8-|HM!^#8%( z(Ki3%QOd`U?Qh_QL<4=D4GFQBCHPq+(c(|>0KRD}0&jogoP0>6+^OEKp@_qCOg+~e z)8vvwvz(j1RrbE8gNT&|w;UyFzPZ7SkpRP;^dw>*4*TjSi7wLdn0$bin|CI8&ebBN z{f(2~C2Piz3_pY4QWUE5LqJKQAorZNSof~#3ps|Ig|u^gB(;HZ#|kcqzL%KbFwnP= zDHjt5>MExx;qY{%@9BN<`a;%KK^_VAcbA|gmqvCre^;Xno~@1}+cIG!7Gr4rD|i2u zw<`9L?siRd7U5Nba30(S5xx>^78|Y}rL`KEAzyLow)++Pje~hqlyFhiBkRpkwO$XD zTFOxjw|MX((dgeMq8ski&aBjOt$%Al>uPOS+TS?mSforxmg%E%6JMvU)LuyJ#>~km z-TKw94aH}aq$+y$b+LhUxNVba3;RBABhX~NqI`GKnEglW-lmE8QTwwIO3Ja}P5FO^ol5?1 zzuW6|w)Wp+luG{3HHm|P*=K)+5+}j}Nhf-c1r=7j#fovD{9s8HuFe_a4w(DJ%<)>1 zEZCFD+ndvmD50_R#_cGr?94O7i>+^5M@Y62P3~hWxLCPN$Op)$eo;x92~PQi}YIl7zQ!x+5%*<@5`)vKLrv zjMrNY%)UNXFOm$K=XRc8f1qx`RLQXI{UbctD1#a(Ho z>KE#dwoY-7W4~hQ=}nzNre4hh>io@{%d4~Fi;K5! z&R-OkO~o_)z-Wcw^zr%6cy^A*Rr07+uXP8rm3G&XbZgJdvx*fm2u7Yt1*=&%JFj0Z zPR^^-F|;?=No!#*UlC$zaZ;kedGKExUmkydd~wo7@1@`%M;HRtg=yu zrBKnj<*tb()dhdZ4JA9eiK2Sf;?ve1v|L~QUslwR>RfjdeU*hQ{JmB@wcZ!{u8q{3 zcVf$vscBSRl}hYpQ-7uE<({hXo=shq>zDhQj}mR_t=gj8-+XjwQ-9SK#r|q&v!1ZZ zA-HvvRTrfD)sj|aIuECe7FrD4ZX`)X8_S zx}9PtI*<4jP2)N_D&5t>oFZB642kIgYV$#gO?5&{^+chrr^UPP)o6veJ*H4^&swtmpkBuP<7xI8^MN!2cH1pU%W?HivnTMr(a5MQ z?VOqMUAME}Y%ffONPfhkjL1Zj?EDb+?wt8I%d$3fsr#0uE{j#pXN7JVaNQN3>0nRPQ+6?1&&+C9a;skpWk+wT^qSa)?uk>x^;msf9? zZxQa=dC%?S6%2{$=-GCeWRfCL+Ei(kWA+D*S>Kk;mCc<0-uTOI18s=^==S%k@gIl% z-uC|IM=7=O9Y*mqpZG2?$?tpQ`&9;btm3Pc(sdV`m&Na9*T;esj+pgfa7|91e4&TU zT#ES}OKm|f$p{3Mq0A;&Xg5PQU1hR$o=Z}8RZ1P3Y9%$sONV9~ppk|%u`kVLUu}7q z{5K!!Znz4%DgXPZ*RSS(9d)+xKaWzZaU4^T9|4)WbiMLrjJqmQZGpm&6&_t*d{Moi z<@1{a{g&b^3b1O>Xj~_+-)40p*T1W zlPP+gM$qldk5_Os7IY(fv3G2?jniZw}94b&SV##Jg-mng~m$>e<2w6 zTB7>y7V$yh{@o^aScuO>OPBv#Oa4Dl|A*)XH--N<$N%qFzyEX8?{4#d9;FyQMscu| z=QA%tWx#GKJ)rJhhD~G#=uF$Mz(!b}98f~$LVxh{uKyab8w5ttJb+4l<+IA3A*Kmn&KY{+Cid|k-{8rR$0 zfk(|9s6+ZTbzqx1uuUEKxZdmsm-CI+!Oen> z&)%HZblmFqJBN)xEg%+2i;Zo+A`+?Roo!Oa(xi;dXJBZf;?I$QQ8$+d#K_lmcfSbv zx}NSGA79tm1ES;Wx_emeg>}UK9H|$zvw1L8Z6xucwyOuy)#lPJYWv${)`cD5v63!o z$$Xn0u}zQIrblekBR)rZ#M%;O_4I@V`^zvNHa!Q=pEyCG;uU_C`3duj@KvNI%!9R3 zc0x_Fb&?a7H~(z82@Oy`erm!tOW`w@jhz235C3nB|KFdB|2*vW54Y#Pk5Se?|6Mmr z;4g6EyG;@JT2ln-mc9Py>z?s*fAsP&`7baRE=%JWvn3j!DgM9TsmA{t9(1?z|4~Y< zWIv-pja0AazDgpvqKIlV?E&0DNN=-qi@C2Gwx&3YJU(qtDZOqDK=op_=rU<1z1!!N z6W+|AR4v8~MfjGoYinAXji6Q#q*DB#usuS84?5ny*Rgf%jz_^rZH*A077tiju3!A~ zuDFeb1-SS_;mHp8G?{@MNqr!O(vU-7zEEt&Ou#*dcFI{>?Ub}bGQuCQ--foTuE1=U zeChH&pIh2=Q*9~^sC`yF6VF7m*#?q`t6*#HWIOxH#HUZ~nwQy!BtCr_%)J(9QgCG- zc8XruEHC0o{!eY43;BN~lz5w^vB#&`j`$ybYi23C1Q2y^v)Hr|L-KG2RzbCCVqGBmm8l#E-JDr0H|M&Ngw)X#{6mLSrUzYf{{tctIPc7__fMxnBz(}e3+_=i*_8^K z#@Sb2BgzFNG)Y60e`Z^yykN$G=ZaA%>Le@%%6iFuWz#B*6he zd+-DD@rcrE)!UDhPD1QCPQlu+BL{$Zv2WmXEN&6Qs-^laIK!dZt&s5AsS*JRqcmGorp#J1@kF)p;BtyVSTY#>4*r`bGyJiSn(|IdpzuVIXl zNEzmyAJsK@;4n)_DGNV`xtIRNwZYzS}?(piLK0W_8a zX~fw|+j z#gF)Qz5Ra!De2&QED#)@opLa{){YBjzv}gr3xgT(G*R2&hLY1bz$~0em`FTkhzpkb za#aj^74|K}>Z_~jd-Qf$vhO78@#UXpJ6eBQco@xG^I$kwxVZWQ@sQW8U2aKd_8akf z0eD7aRL`b>6pL>&Py6X%?ail5p9Cq|eOG$+{^?HX?_JeDlpENxu1J051s_Am?so%ne}-6=LL2n!taF$)Yt# z?W=ZxLoBd*T|{_+Oy_ZL+T_*ek<4&JZ=@733thynJ&NFfIHx{A0tYz_Xp+zi6{-dW z%FqP;yoh8y?wE!ly;T!cA}~jIwj%Oz0sBT~TPnzh6;n)Dw&*u{AI@>^N?TKBwj-!3 zcePJnV0{^=B#6~p6`E^^mX~*Dt$P2Ff4DR4wuX-_lVVJgR{NY<+y;M z7RVlT{>oF*M_5ukT)kzk*UXGJ*8Jzz`Wj7d0zrvb$rVk!UyL0u1m8gYyRNNB)!D{vN1 zxR)->T~Q93R_effStAMObFC3i&mOey#k2GVIJMDhK~z>p^B6Gf_dAF81+f6=+{H_a z7e2eQMw+)ui^|4BWrHsU^opT4pz2B;sd$htH3nwGvWhRUlhMu5zf->J~hDJ z!4I;A@eHK%VCWS=c9TwBtKk@hd^t@u6a}YAXl(B14&-!{*ra2K@vX+Apx`r%!~#0C zjP#IV;cQxQJL25BXTUCNwd%Sn!Ifc6Ic8%L{!^JDFoUMcgAgjCfT7zYZUMdqhUamx%N)gVQF!y0~#FJRN5P4POcOe*_ z*@xoSJT88?846tlXCYu@ScEf&1vD_ApeY8J%#Q)XkN39e8sIEOOAnXAd7*294y>*n z1BMTTatS{3$od9+9;=$5gYXx@`R*K};4LEpzlnYP{+=fNf)4@A_g0Hf@_U2(aRRz|zqDWBh_YFhE|H~3v_ldR!4>?vONeQJH_k`3soKLboR=w!)mCC003DCBd@o33 zBY3L2+O5oPvvR314SRS8>Q5NqF=e<4B;h7cOW>dg@f{4iz0T(7b4h;p&|>B%dJA+c za4fB)1hX+^k>0vniqGx?!CV4oIYVt|MtnQPaRDw_i33eVJ%l-&4;7$`;jHEl54CV6gyU-1`xyhJk5}=IS&kJWYFL^PXrsg6rmCLzFIP3H1OB)k&{GPR0LZF-G z_vQ@wD#KTytmk;S?gty>qh*=*mXE#$eqWl_KZLJ_-y1Fr7=A7M-f$7YLO36d-^=$G zF5UWmfw{UgIZmSyWAOZ#!llU^JAqet}PmL^Cy;qwvwxcZFg+MU`j>ci1?hZe6?1fc{zGtaIbmwCgN}H*VK8xpAxbaW%`oVSeAV z8<#=6F+Z;P0=1R*Z61N&o87ptgWsFoxUYlXo87oC&F_^=|7CW_g-{V@z)65zA2A72 zgvAu69PH&^^Q|^?0$;wdZH164=_pyC)(N_TYTVVGGdFl+997-$E5%aX!BAq=+^ObT zw62A|8hkAashR_HrC_Q%7~XWeZpV9o^r-=KSxnU&pesdF-7Suhx*wp8HhTfi1@`&^ zI5!XjqGCzWh~AV!MI)4`r1dEV9N)l=baF-^&Z07;5A&`P!k5KzuO2S$!4H%{B-Jd5 zrS{Ti_K6N_R`>T?Fg7rnA%cMVX@p~8I0GU~kPQcvhu+BP z1?2!6&vA}Jfmw{C@(O2rfZ(6UuU=~3T7`-0cr9Y0p^fXDDYu+dO*py9!U*7YD!Zt} zfvZ^Od#{H(E;;LUXe|V*|C6Q)sFdSUF#WqiAMc+Q{@69y1IiR7zhgi!mik^p6#H1O z+Pm*HS;Ir}Gc*gJFr>E}W;B(HDgm9*l!2deK_k#H+bU>|EC$Mh(-c;O84W#Q z(|%9>F@q@OLM~89ePuF}SegTc!1pl?WhXZdpPeQ0t=@*S&<bvui!fq%}s>O%5uRXKrWh{nPSyNdoHgwiG1p zBqafEXZv-+HTOKUcg>9t8YIH?N@fWm<|W~V^#db0gP8=|x9_v88WKl@)@= zr+Xl!Bv1xMT53&gC9;&PMg?T)Tv9%b3FA7w&`K **Tip**: List all releases using `helm list` + +## Uninstalling the Chart + +To uninstall/delete the `my-release` deployment: + +```bash +$ helm delete my-release +``` + +The command removes all the Kubernetes components associated with the chart and deletes the release. + +## Configuration + +The following table lists the configurable parameters of the MariaDB chart and their default values. + +| Parameter | Description | Default | +|-------------------------------------------|-----------------------------------------------------|-------------------------------------------------------------------| +| `image.registry` | MariaDB image registry | `docker.io` | +| `image.repository` | MariaDB Image name | `bitnami/mariadb` | +| `image.tag` | MariaDB Image tag | `{VERSION}` | +| `image.pullPolicy` | MariaDB image pull policy | `Always` if `imageTag` is `latest`, else `IfNotPresent` | +| `image.pullSecrets` | Specify image pull secrets | `nil` (does not add image pull secrets to deployed pods) | +| `service.type` | Kubernetes service type | `ClusterIP` | +| `service.port` | MySQL service port | `3306` | +| `rootUser.password` | Password for the `root` user | _random 10 character alphanumeric string_ | +| `rootUser.forcePassword` | Force users to specify a password | `false` | +| `db.user` | Username of new user to create | `nil` | +| `db.password` | Password for the new user | _random 10 character alphanumeric string if `db.user` is defined_ | +| `db.name` | Name for new database to create | `my_database` | +| `replication.enabled` | MariaDB replication enabled | `true` | +| `replication.user` | MariaDB replication user | `replicator` | +| `replication.password` | MariaDB replication user password | _random 10 character alphanumeric string_ | +| `master.antiAffinity` | Master pod anti-affinity policy | `soft` | +| `master.persistence.enabled` | Enable persistence using a `PersistentVolumeClaim` | `true` | +| `master.persistence.annotations` | Persistent Volume Claim annotations | `{}` | +| `master.persistence.storageClass` | Persistent Volume Storage Class | `` | +| `master.persistence.accessModes` | Persistent Volume Access Modes | `[ReadWriteOnce]` | +| `master.persistence.size` | Persistent Volume Size | `8Gi` | +| `master.config` | Config file for the MariaDB Master server | `_default values in the values.yaml file_` | +| `master.resources` | CPU/Memory resource requests/limits for master node | `{}` | +| `master.livenessProbe.enabled` | Turn on and off liveness probe (master) | `true` | +| `master.livenessProbe.initialDelaySeconds`| Delay before liveness probe is initiated (master) | `120` | +| `master.livenessProbe.periodSeconds` | How often to perform the probe (master) | `10` | +| `master.livenessProbe.timeoutSeconds` | When the probe times out (master) | `1` | +| `master.livenessProbe.successThreshold` | Minimum consecutive successes for the probe (master)| `1` | +| `master.livenessProbe.failureThreshold` | Minimum consecutive failures for the probe (master) | `3` | +| `master.readinessProbe.enabled` | Turn on and off readiness probe (master) | `true` | +| `master.readinessProbe.initialDelaySeconds`| Delay before readiness probe is initiated (master) | `15` | +| `master.readinessProbe.periodSeconds` | How often to perform the probe (master) | `10` | +| `master.readinessProbe.timeoutSeconds` | When the probe times out (master) | `1` | +| `master.readinessProbe.successThreshold` | Minimum consecutive successes for the probe (master)| `1` | +| `master.readinessProbe.failureThreshold` | Minimum consecutive failures for the probe (master) | `3` | +| `slave.replicas` | Desired number of slave replicas | `1` | +| `slave.antiAffinity` | Slave pod anti-affinity policy | `soft` | +| `slave.persistence.enabled` | Enable persistence using a `PersistentVolumeClaim` | `true` | +| `slave.persistence.annotations` | Persistent Volume Claim annotations | `{}` | +| `slave.persistence.storageClass` | Persistent Volume Storage Class | `` | +| `slave.persistence.accessModes` | Persistent Volume Access Modes | `[ReadWriteOnce]` | +| `slave.persistence.size` | Persistent Volume Size | `8Gi` | +| `slave.config` | Config file for the MariaDB Slave replicas | `_default values in the values.yaml file_` | +| `slave.resources` | CPU/Memory resource requests/limits for slave node | `{}` | +| `slave.livenessProbe.enabled` | Turn on and off liveness probe (slave) | `true` | +| `slave.livenessProbe.initialDelaySeconds` | Delay before liveness probe is initiated (slave) | `120` | +| `slave.livenessProbe.periodSeconds` | How often to perform the probe (slave) | `10` | +| `slave.livenessProbe.timeoutSeconds` | When the probe times out (slave) | `1` | +| `slave.livenessProbe.successThreshold` | Minimum consecutive successes for the probe (slave) | `1` | +| `slave.livenessProbe.failureThreshold` | Minimum consecutive failures for the probe (slave) | `3` | +| `slave.readinessProbe.enabled` | Turn on and off readiness probe (slave) | `true` | +| `slave.readinessProbe.initialDelaySeconds`| Delay before readiness probe is initiated (slave) | `15` | +| `slave.readinessProbe.periodSeconds` | How often to perform the probe (slave) | `10` | +| `slave.readinessProbe.timeoutSeconds` | When the probe times out (slave) | `1` | +| `slave.readinessProbe.successThreshold` | Minimum consecutive successes for the probe (slave) | `1` | +| `slave.readinessProbe.failureThreshold` | Minimum consecutive failures for the probe (slave) | `3` | +| `metrics.enabled` | Start a side-car prometheus exporter | `false` | +| `metrics.image.registry` | Exporter image registry | `docker.io` | +`metrics.image.repository` | Exporter image name | `prom/mysqld-exporter` | +| `metrics.image.tag` | Exporter image tag | `v0.10.0` | +| `metrics.image.pullPolicy` | Exporter image pull policy | `IfNotPresent` | +| `metrics.resources` | Exporter resource requests/limit | `nil` | + +The above parameters map to the env variables defined in [bitnami/mariadb](http://github.com/bitnami/bitnami-docker-mariadb). For more information please refer to the [bitnami/mariadb](http://github.com/bitnami/bitnami-docker-mariadb) image documentation. + +Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example, + +```bash +$ helm install --name my-release \ + --set root.password=secretpassword,user.database=app_database \ + stable/mariadb +``` + +The above command sets the MariaDB `root` account password to `secretpassword`. Additionally it creates a database named `my_database`. + +Alternatively, a YAML file that specifies the values for the parameters can be provided while installing the chart. For example, + +```bash +$ helm install --name my-release -f values.yaml stable/mariadb +``` + +> **Tip**: You can use the default [values.yaml](values.yaml) + +## Initialize a fresh instance + +The [Bitnami MariaDB](https://github.com/bitnami/bitnami-docker-mariadb) image allows you to use your custom scripts to initialize a fresh instance. In order to execute the scripts, they must be located inside the chart folder `files/docker-entrypoint-initdb.d` so they can be consumed as a ConfigMap. + +The allowed extensions are `.sh`, `.sql` and `.sql.gz`. + +## Persistence + +The [Bitnami MariaDB](https://github.com/bitnami/bitnami-docker-mariadb) image stores the MariaDB data and configurations at the `/bitnami/mariadb` path of the container. + +The chart mounts a [Persistent Volume](kubernetes.io/docs/user-guide/persistent-volumes/) volume at this location. The volume is created using dynamic volume provisioning, by default. An existing PersistentVolumeClaim can be defined. diff --git a/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/files/docker-entrypoint-initdb.d/README.md b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/files/docker-entrypoint-initdb.d/README.md new file mode 100755 index 000000000..aaddde303 --- /dev/null +++ b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/files/docker-entrypoint-initdb.d/README.md @@ -0,0 +1,3 @@ +You can copy here your custom .sh, .sql or .sql.gz file so they are executed during the first boot of the image. + +More info in the [bitnami-docker-mariadb](https://github.com/bitnami/bitnami-docker-mariadb#initializing-a-new-instance) repository. \ No newline at end of file diff --git a/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/NOTES.txt b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/NOTES.txt new file mode 100755 index 000000000..4ba3b668a --- /dev/null +++ b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/NOTES.txt @@ -0,0 +1,35 @@ + +Please be patient while the chart is being deployed + +Tip: + + Watch the deployment status using the command: kubectl get pods -w --namespace {{ .Release.Namespace }} -l release={{ .Release.Name }} + +Services: + + echo Master: {{ template "mariadb.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local:{{ .Values.service.port }} +{{- if .Values.replication.enabled }} + echo Slave: {{ template "slave.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local:{{ .Values.service.port }} +{{- end }} + +Administrator credentials: + + Username: root + Password : $(kubectl get secret --namespace {{ .Release.Namespace }} {{ template "mariadb.fullname" . }} -o jsonpath="{.data.mariadb-root-password}" | base64 --decode) + +To connect to your database + + 1. Run a pod that you can use as a client: + + kubectl run {{ template "mariadb.fullname" . }}-client --rm --tty -i --image {{ template "mariadb.image" . }} --namespace {{ .Release.Namespace }} --command -- bash + + 2. To connect to master service (read/write): + + mysql -h {{ template "mariadb.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local -uroot -p {{ .Values.db.name }} + +{{- if .Values.replication.enabled }} + + 3. To connect to slave service (read-only): + + mysql -h {{ template "slave.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local -uroot -p {{ .Values.db.name }} +{{- end }} diff --git a/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/_helpers.tpl b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/_helpers.tpl new file mode 100755 index 000000000..5afe380ff --- /dev/null +++ b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/_helpers.tpl @@ -0,0 +1,53 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "mariadb.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "mariadb.fullname" -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- define "master.fullname" -}} +{{- if .Values.replication.enabled -}} +{{- printf "%s-%s" .Release.Name "mariadb-master" | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name "mariadb" | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} + + +{{- define "slave.fullname" -}} +{{- printf "%s-%s" .Release.Name "mariadb-slave" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- define "mariadb.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Return the proper image name +*/}} +{{- define "mariadb.image" -}} +{{- $registryName := .Values.image.registry -}} +{{- $repositoryName := .Values.image.repository -}} +{{- $tag := .Values.image.tag | toString -}} +{{- printf "%s/%s:%s" $registryName $repositoryName $tag -}} +{{- end -}} + +{{/* +Return the proper image name +*/}} +{{- define "metrics.image" -}} +{{- $registryName := .Values.metrics.image.registry -}} +{{- $repositoryName := .Values.metrics.image.repository -}} +{{- $tag := .Values.metrics.image.tag | toString -}} +{{- printf "%s/%s:%s" $registryName $repositoryName $tag -}} +{{- end -}} diff --git a/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/initialization-configmap.yaml b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/initialization-configmap.yaml new file mode 100755 index 000000000..7bb969627 --- /dev/null +++ b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/initialization-configmap.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "master.fullname" . }}-init-scripts + labels: + app: {{ template "mariadb.name" . }} + component: "master" + chart: {{ template "mariadb.chart" . }} + release: {{ .Release.Name | quote }} + heritage: {{ .Release.Service | quote }} +data: +{{ (.Files.Glob "files/docker-entrypoint-initdb.d/*").AsConfig | indent 2 }} diff --git a/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/master-configmap.yaml b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/master-configmap.yaml new file mode 100755 index 000000000..880a10198 --- /dev/null +++ b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/master-configmap.yaml @@ -0,0 +1,15 @@ +{{- if .Values.master.config }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "master.fullname" . }} + labels: + app: {{ template "mariadb.name" . }} + component: "master" + chart: {{ template "mariadb.chart" . }} + release: {{ .Release.Name | quote }} + heritage: {{ .Release.Service | quote }} +data: + my.cnf: |- +{{ .Values.master.config | indent 4 }} +{{- end -}} diff --git a/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/master-statefulset.yaml b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/master-statefulset.yaml new file mode 100755 index 000000000..0d74f01ff --- /dev/null +++ b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/master-statefulset.yaml @@ -0,0 +1,187 @@ +apiVersion: apps/v1beta1 +kind: StatefulSet +metadata: + name: {{ template "master.fullname" . }} + labels: + app: "{{ template "mariadb.name" . }}" + chart: {{ template "mariadb.chart" . }} + component: "master" + release: {{ .Release.Name | quote }} + heritage: {{ .Release.Service | quote }} +spec: + serviceName: "{{ template "master.fullname" . }}" + replicas: 1 + updateStrategy: + type: RollingUpdate + template: + metadata: + labels: + app: "{{ template "mariadb.name" . }}" + component: "master" + release: "{{ .Release.Name }}" + chart: {{ template "mariadb.chart" . }} + spec: + securityContext: + runAsUser: 1001 + fsGroup: 1001 + {{- if eq .Values.master.antiAffinity "hard" }} + affinity: + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - topologyKey: "kubernetes.io/hostname" + labelSelector: + matchLabels: + app: "{{ template "mariadb.name" . }}" + release: "{{ .Release.Name }}" + {{- else if eq .Values.master.antiAffinity "soft" }} + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 1 + podAffinityTerm: + topologyKey: kubernetes.io/hostname + labelSelector: + matchLabels: + app: "{{ template "mariadb.name" . }}" + release: "{{ .Release.Name }}" + {{- end }} + {{- if .Values.image.pullSecrets }} + imagePullSecrets: + {{- range .Values.image.pullSecrets }} + - name: {{ . }} + {{- end}} + {{- end }} + containers: + - name: "mariadb" + image: {{ template "mariadb.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy | quote }} + env: + - name: MARIADB_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "mariadb.fullname" . }} + key: mariadb-root-password + {{- if .Values.db.user }} + - name: MARIADB_USER + value: "{{ .Values.db.user }}" + - name: MARIADB_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "mariadb.fullname" . }} + key: mariadb-password + {{- end }} + - name: MARIADB_DATABASE + value: "{{ .Values.db.name }}" + {{- if .Values.replication.enabled }} + - name: MARIADB_REPLICATION_MODE + value: "master" + - name: MARIADB_REPLICATION_USER + value: "{{ .Values.replication.user }}" + - name: MARIADB_REPLICATION_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "mariadb.fullname" . }} + key: mariadb-replication-password + {{- end }} + ports: + - name: mysql + containerPort: 3306 + {{- if .Values.master.livenessProbe.enabled }} + livenessProbe: + exec: + command: ["sh", "-c", "exec mysqladmin status -uroot -p$MARIADB_ROOT_PASSWORD"] + initialDelaySeconds: {{ .Values.master.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.master.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.master.livenessProbe.timeoutSeconds }} + successThreshold: {{ .Values.master.livenessProbe.successThreshold }} + failureThreshold: {{ .Values.master.livenessProbe.failureThreshold }} + {{- end }} + {{- if .Values.master.readinessProbe.enabled }} + readinessProbe: + exec: + command: ["sh", "-c", "exec mysqladmin status -uroot -p$MARIADB_ROOT_PASSWORD"] + initialDelaySeconds: {{ .Values.master.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.master.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.master.readinessProbe.timeoutSeconds }} + successThreshold: {{ .Values.master.readinessProbe.successThreshold }} + failureThreshold: {{ .Values.master.readinessProbe.failureThreshold }} + {{- end }} + resources: +{{ toYaml .Values.master.resources | indent 10 }} + volumeMounts: + - name: data + mountPath: /bitnami/mariadb + - name: custom-init-scripts + mountPath: /docker-entrypoint-initdb.d +{{- if .Values.master.config }} + - name: config + mountPath: /opt/bitnami/mariadb/conf/my.cnf + subPath: my.cnf +{{- end }} +{{- if .Values.metrics.enabled }} + - name: metrics + image: {{ template "metrics.image" . }} + imagePullPolicy: {{ .Values.metrics.image.pullPolicy | quote }} + env: + - name: MARIADB_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "mariadb.fullname" . }} + key: mariadb-root-password + command: [ 'sh', '-c', 'DATA_SOURCE_NAME="root:$MARIADB_ROOT_PASSWORD@(localhost:3306)/" /bin/mysqld_exporter' ] + ports: + - name: metrics + containerPort: 9104 + livenessProbe: + httpGet: + path: /metrics + port: metrics + initialDelaySeconds: 15 + timeoutSeconds: 5 + readinessProbe: + httpGet: + path: /metrics + port: metrics + initialDelaySeconds: 5 + timeoutSeconds: 1 + resources: +{{ toYaml .Values.metrics.resources | indent 10 }} +{{- end }} + volumes: + {{- if .Values.master.config }} + - name: config + configMap: + name: {{ template "master.fullname" . }} + {{- end }} + - name: custom-init-scripts + configMap: + name: {{ template "master.fullname" . }}-init-scripts +{{- if .Values.master.persistence.enabled }} + volumeClaimTemplates: + - metadata: + name: data + labels: + app: "{{ template "mariadb.name" . }}" + chart: {{ template "mariadb.chart" . }} + component: "master" + release: {{ .Release.Name | quote }} + heritage: {{ .Release.Service | quote }} + spec: + accessModes: + {{- range .Values.master.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.master.persistence.size | quote }} + {{- if .Values.master.persistence.storageClass }} + {{- if (eq "-" .Values.master.persistence.storageClass) }} + storageClassName: "" + {{- else }} + storageClassName: {{ .Values.master.persistence.storageClass | quote }} + {{- end }} + {{- end }} +{{- else }} + - name: "data" + emptyDir: {} +{{- end }} diff --git a/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/master-svc.yaml b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/master-svc.yaml new file mode 100755 index 000000000..460ec328e --- /dev/null +++ b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/master-svc.yaml @@ -0,0 +1,29 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "mariadb.fullname" . }} + labels: + app: "{{ template "mariadb.name" . }}" + component: "master" + chart: {{ template "mariadb.chart" . }} + release: {{ .Release.Name | quote }} + heritage: {{ .Release.Service | quote }} +{{- if .Values.metrics.enabled }} + annotations: +{{ toYaml .Values.metrics.annotations | indent 4 }} +{{- end }} +spec: + type: {{ .Values.service.type }} + ports: + - name: mysql + port: {{ .Values.service.port }} + targetPort: mysql +{{- if .Values.metrics.enabled }} + - name: metrics + port: 9104 + targetPort: metrics +{{- end }} + selector: + app: "{{ template "mariadb.name" . }}" + component: "master" + release: "{{ .Release.Name }}" diff --git a/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/secrets.yaml b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/secrets.yaml new file mode 100755 index 000000000..17999d609 --- /dev/null +++ b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/secrets.yaml @@ -0,0 +1,38 @@ +{{- if (not .Values.rootUser.existingSecret) -}} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "mariadb.fullname" . }} + labels: + app: "{{ template "mariadb.name" . }}" + chart: {{ template "mariadb.chart" . }} + release: {{ .Release.Name | quote }} + heritage: {{ .Release.Service | quote }} +type: Opaque +data: + {{- if .Values.rootUser.password }} + mariadb-root-password: "{{ .Values.rootUser.password | b64enc }}" + {{- else if (not .Values.rootUser.forcePassword) }} + mariadb-root-password: "{{ randAlphaNum 10 | b64enc }}" + {{ else }} + mariadb-root-password: {{ required "A MariaDB Root Password is required!" .Values.rootUser.password }} + {{- end }} + {{- if .Values.db.user }} + {{- if .Values.db.password }} + mariadb-password: "{{ .Values.db.password | b64enc }}" + {{- else if (not .Values.db.forcePassword) }} + mariadb-password: "{{ randAlphaNum 10 | b64enc }}" + {{- else }} + mariadb-password: {{ required "A MariaDB Database Password is required!" .Values.db.password }} + {{- end }} + {{- end }} + {{- if .Values.replication.enabled }} + {{- if .Values.replication.password }} + mariadb-replication-password: "{{ .Values.replication.password | b64enc }}" + {{- else if (not .Values.replication.forcePassword) }} + mariadb-replication-password: "{{ randAlphaNum 10 | b64enc }}" + {{- else }} + mariadb-replication-password: {{ required "A MariaDB Replication Password is required!" .Values.replication.password }} + {{- end }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/slave-configmap.yaml b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/slave-configmap.yaml new file mode 100755 index 000000000..056cf5c07 --- /dev/null +++ b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/slave-configmap.yaml @@ -0,0 +1,15 @@ +{{- if and .Values.replication.enabled .Values.slave.config }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "slave.fullname" . }} + labels: + app: {{ template "mariadb.name" . }} + component: "slave" + chart: {{ template "mariadb.chart" . }} + release: {{ .Release.Name | quote }} + heritage: {{ .Release.Service | quote }} +data: + my.cnf: |- +{{ .Values.slave.config | indent 4 }} +{{- end }} diff --git a/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/slave-statefulset.yaml b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/slave-statefulset.yaml new file mode 100755 index 000000000..aa67d4a70 --- /dev/null +++ b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/slave-statefulset.yaml @@ -0,0 +1,193 @@ +{{- if .Values.replication.enabled }} +apiVersion: apps/v1beta1 +kind: StatefulSet +metadata: + name: {{ template "slave.fullname" . }} + labels: + app: "{{ template "mariadb.name" . }}" + chart: {{ template "mariadb.chart" . }} + component: "slave" + release: {{ .Release.Name | quote }} + heritage: {{ .Release.Service | quote }} +spec: + serviceName: "{{ template "slave.fullname" . }}" + replicas: {{ .Values.slave.replicas }} + updateStrategy: + type: RollingUpdate + template: + metadata: + labels: + app: "{{ template "mariadb.name" . }}" + component: "slave" + release: "{{ .Release.Name }}" + chart: {{ template "mariadb.chart" . }} + spec: + securityContext: + runAsUser: 1001 + fsGroup: 1001 + {{- if eq .Values.slave.antiAffinity "hard" }} + affinity: + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - topologyKey: "kubernetes.io/hostname" + labelSelector: + matchLabels: + app: "{{ template "mariadb.name" . }}" + release: "{{ .Release.Name }}" + {{- else if eq .Values.slave.antiAffinity "soft" }} + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 1 + podAffinityTerm: + topologyKey: kubernetes.io/hostname + labelSelector: + matchLabels: + app: "{{ template "mariadb.name" . }}" + release: "{{ .Release.Name }}" + {{- end }} + {{- if .Values.image.pullSecrets }} + imagePullSecrets: + {{- range .Values.image.pullSecrets }} + - name: {{ . }} + {{- end}} + {{- end }} + containers: + - name: "mariadb" + image: {{ template "mariadb.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy | quote }} + env: + - name: MARIADB_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "mariadb.fullname" . }} + key: mariadb-root-password + {{- if .Values.db.user }} + - name: MARIADB_USER + value: "{{ .Values.db.user }}" + - name: MARIADB_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "mariadb.fullname" . }} + key: mariadb-password + {{- end }} + - name: MARIADB_DATABASE + value: "{{ .Values.db.name }}" + - name: MARIADB_REPLICATION_MODE + value: "slave" + - name: MARIADB_MASTER_HOST + value: {{ template "mariadb.fullname" . }} + - name: MARIADB_MASTER_PORT + value: "3306" + - name: MARIADB_MASTER_USER + value: "root" + - name: MARIADB_MASTER_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "mariadb.fullname" . }} + key: mariadb-root-password + - name: MARIADB_REPLICATION_USER + value: "{{ .Values.replication.user }}" + - name: MARIADB_REPLICATION_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "mariadb.fullname" . }} + key: mariadb-replication-password + ports: + - name: mysql + containerPort: 3306 + {{- if .Values.slave.livenessProbe.enabled }} + livenessProbe: + exec: + command: ["sh", "-c", "exec mysqladmin status -uroot -p$MARIADB_ROOT_PASSWORD"] + initialDelaySeconds: {{ .Values.slave.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.slave.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.slave.livenessProbe.timeoutSeconds }} + successThreshold: {{ .Values.slave.livenessProbe.successThreshold }} + failureThreshold: {{ .Values.slave.livenessProbe.failureThreshold }} + {{- end }} + {{- if .Values.slave.readinessProbe.enabled }} + readinessProbe: + exec: + command: ["sh", "-c", "exec mysqladmin status -uroot -p$MARIADB_ROOT_PASSWORD"] + initialDelaySeconds: {{ .Values.slave.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.slave.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.slave.readinessProbe.timeoutSeconds }} + successThreshold: {{ .Values.slave.readinessProbe.successThreshold }} + failureThreshold: {{ .Values.slave.readinessProbe.failureThreshold }} + {{- end }} + resources: +{{ toYaml .Values.slave.resources | indent 10 }} + volumeMounts: + - name: data + mountPath: /bitnami/mariadb +{{- if .Values.slave.config }} + - name: config + mountPath: /opt/bitnami/mariadb/conf/my.cnf + subPath: my.cnf +{{- end }} +{{- if .Values.metrics.enabled }} + - name: metrics + image: {{ template "metrics.image" . }} + imagePullPolicy: {{ .Values.metrics.image.pullPolicy | quote }} + env: + - name: MARIADB_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "mariadb.fullname" . }} + key: mariadb-root-password + command: [ 'sh', '-c', 'DATA_SOURCE_NAME="root:$MARIADB_ROOT_PASSWORD@(localhost:3306)/" /bin/mysqld_exporter' ] + ports: + - name: metrics + containerPort: 9104 + livenessProbe: + httpGet: + path: /metrics + port: metrics + initialDelaySeconds: 15 + timeoutSeconds: 5 + readinessProbe: + httpGet: + path: /metrics + port: metrics + initialDelaySeconds: 5 + timeoutSeconds: 1 + resources: +{{ toYaml .Values.metrics.resources | indent 10 }} +{{- end }} + volumes: + {{- if .Values.slave.config }} + - name: config + configMap: + name: {{ template "slave.fullname" . }} + {{- end }} +{{- if .Values.slave.persistence.enabled }} + volumeClaimTemplates: + - metadata: + name: data + labels: + app: "{{ template "mariadb.name" . }}" + chart: {{ template "mariadb.chart" . }} + component: "slave" + release: {{ .Release.Name | quote }} + heritage: {{ .Release.Service | quote }} + spec: + accessModes: + {{- range .Values.slave.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.slave.persistence.size | quote }} + {{- if .Values.slave.persistence.storageClass }} + {{- if (eq "-" .Values.slave.persistence.storageClass) }} + storageClassName: "" + {{- else }} + storageClassName: {{ .Values.slave.persistence.storageClass | quote }} + {{- end }} + {{- end }} +{{- else }} + - name: "data" + emptyDir: {} +{{- end }} +{{- end }} diff --git a/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/slave-svc.yaml b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/slave-svc.yaml new file mode 100755 index 000000000..fa551371f --- /dev/null +++ b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/slave-svc.yaml @@ -0,0 +1,31 @@ +{{- if .Values.replication.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "slave.fullname" . }} + labels: + app: "{{ template "mariadb.name" . }}" + chart: {{ template "mariadb.chart" . }} + component: "slave" + release: {{ .Release.Name | quote }} + heritage: {{ .Release.Service | quote }} +{{- if .Values.metrics.enabled }} + annotations: +{{ toYaml .Values.metrics.annotations | indent 4 }} +{{- end }} +spec: + type: {{ .Values.service.type }} + ports: + - name: mysql + port: {{ .Values.service.port }} + targetPort: mysql +{{- if .Values.metrics.enabled }} + - name: metrics + port: 9104 + targetPort: metrics +{{- end }} + selector: + app: "{{ template "mariadb.name" . }}" + component: "slave" + release: "{{ .Release.Name }}" +{{- end }} \ No newline at end of file diff --git a/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/test-runner.yaml b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/test-runner.yaml new file mode 100755 index 000000000..99a85d4aa --- /dev/null +++ b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/test-runner.yaml @@ -0,0 +1,44 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ template "mariadb.fullname" . }}-test-{{ randAlphaNum 5 | lower }}" + annotations: + "helm.sh/hook": test-success +spec: + initContainers: + - name: "test-framework" + image: "dduportal/bats:0.4.0" + command: + - "bash" + - "-c" + - | + set -ex + # copy bats to tools dir + cp -R /usr/local/libexec/ /tools/bats/ + volumeMounts: + - mountPath: /tools + name: tools + containers: + - name: mariadb-test + image: "{{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy | quote }} + command: ["/tools/bats/bats", "-t", "/tests/run.sh"] + env: + - name: MARIADB_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "mariadb.fullname" . }} + key: mariadb-root-password + volumeMounts: + - mountPath: /tests + name: tests + readOnly: true + - mountPath: /tools + name: tools + volumes: + - name: tests + configMap: + name: {{ template "mariadb.fullname" . }}-tests + - name: tools + emptyDir: {} + restartPolicy: Never diff --git a/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/tests.yaml b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/tests.yaml new file mode 100755 index 000000000..957f3fd1e --- /dev/null +++ b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/templates/tests.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "mariadb.fullname" . }}-tests +data: + run.sh: |- + @test "Testing MariaDB is accessible" { + mysql -h {{ template "mariadb.fullname" . }} -uroot -p$MARIADB_ROOT_PASSWORD -e 'show databases;' + } diff --git a/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/values.yaml b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/values.yaml new file mode 100755 index 000000000..ce2414e9f --- /dev/null +++ b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/charts/mariadb/values.yaml @@ -0,0 +1,233 @@ +## Bitnami MariaDB image +## ref: https://hub.docker.com/r/bitnami/mariadb/tags/ +## +image: + registry: docker.io + repository: bitnami/mariadb + tag: 10.1.34-debian-9 + ## Specify a imagePullPolicy + ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images + ## + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistrKeySecretName + +service: + ## Kubernetes service type + type: ClusterIP + port: 3306 + +rootUser: + ## MariaDB admin password + ## ref: https://github.com/bitnami/bitnami-docker-mariadb#setting-the-root-password-on-first-run + ## + password: + ## Use existing secret (ignores root, db and replication passwords) + # existingSecret: + ## + ## Option to force users to specify a password. That is required for 'helm upgrade' to work properly. + ## If it is not force, a random password will be generated. + forcePassword: false + +db: + ## MariaDB username and password + ## ref: https://github.com/bitnami/bitnami-docker-mariadb#creating-a-database-user-on-first-run + ## + user: + password: + ## Password is ignored if existingSecret is specified. + ## Database to create + ## ref: https://github.com/bitnami/bitnami-docker-mariadb#creating-a-database-on-first-run + ## + name: my_database + ## Option to force users to specify a password. That is required for 'helm upgrade' to work properly. + ## If it is not force, a random password will be generated. + forcePassword: false + +replication: + ## Enable replication. This enables the creation of replicas of MariaDB. If false, only a + ## master deployment would be created + enabled: true + ## + ## MariaDB replication user + ## ref: https://github.com/bitnami/bitnami-docker-mariadb#setting-up-a-replication-cluster + ## + user: replicator + ## MariaDB replication user password + ## ref: https://github.com/bitnami/bitnami-docker-mariadb#setting-up-a-replication-cluster + ## + password: + ## Password is ignored if existingSecret is specified. + ## + ## Option to force users to specify a password. That is required for 'helm upgrade' to work properly. + ## If it is not force, a random password will be generated. + forcePassword: false + +master: + antiAffinity: soft + ## Enable persistence using Persistent Volume Claims + ## ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ + ## + persistence: + ## If true, use a Persistent Volume Claim, If false, use emptyDir + ## + enabled: true + ## Persistent Volume Storage Class + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + # storageClass: "-" + ## Persistent Volume Claim annotations + ## + annotations: + ## Persistent Volume Access Mode + ## + accessModes: + - ReadWriteOnce + ## Persistent Volume size + ## + size: 8Gi + ## + + ## Configure MySQL with a custom my.cnf file + ## ref: https://mysql.com/kb/en/mysql/configuring-mysql-with-mycnf/#example-of-configuration-file + ## + config: |- + [mysqld] + skip-name-resolve + explicit_defaults_for_timestamp + basedir=/opt/bitnami/mariadb + port=3306 + socket=/opt/bitnami/mariadb/tmp/mysql.sock + tmpdir=/opt/bitnami/mariadb/tmp + max_allowed_packet=16M + bind-address=0.0.0.0 + pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid + log-error=/opt/bitnami/mariadb/logs/mysqld.log + character-set-server=UTF8 + collation-server=utf8_general_ci + + [client] + port=3306 + socket=/opt/bitnami/mariadb/tmp/mysql.sock + default-character-set=UTF8 + + [manager] + port=3306 + socket=/opt/bitnami/mariadb/tmp/mysql.sock + pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid + + ## Configure master resource requests and limits + ## ref: http://kubernetes.io/docs/user-guide/compute-resources/ + ## + resources: {} + livenessProbe: + enabled: true + ## + ## Initializing the database could take some time + initialDelaySeconds: 120 + ## + ## Default Kubernetes values + periodSeconds: 10 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 3 + readinessProbe: + enabled: true + initialDelaySeconds: 15 + ## + ## Default Kubernetes values + periodSeconds: 10 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 3 + +slave: + replicas: 1 + antiAffinity: soft + persistence: + ## If true, use a Persistent Volume Claim, If false, use emptyDir + ## + enabled: true + # storageClass: "-" + annotations: + accessModes: + - ReadWriteOnce + ## Persistent Volume size + ## + size: 8Gi + ## + + ## Configure MySQL slave with a custom my.cnf file + ## ref: https://mysql.com/kb/en/mysql/configuring-mysql-with-mycnf/#example-of-configuration-file + ## + config: |- + [mysqld] + skip-name-resolve + explicit_defaults_for_timestamp + basedir=/opt/bitnami/mariadb + port=3306 + socket=/opt/bitnami/mariadb/tmp/mysql.sock + tmpdir=/opt/bitnami/mariadb/tmp + max_allowed_packet=16M + bind-address=0.0.0.0 + pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid + log-error=/opt/bitnami/mariadb/logs/mysqld.log + character-set-server=UTF8 + collation-server=utf8_general_ci + + [client] + port=3306 + socket=/opt/bitnami/mariadb/tmp/mysql.sock + default-character-set=UTF8 + + [manager] + port=3306 + socket=/opt/bitnami/mariadb/tmp/mysql.sock + pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid + + ## + ## Configure slave resource requests and limits + ## ref: http://kubernetes.io/docs/user-guide/compute-resources/ + ## + resources: {} + livenessProbe: + enabled: true + ## + ## Initializing the database could take some time + initialDelaySeconds: 120 + ## + ## Default Kubernetes values + periodSeconds: 10 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 3 + readinessProbe: + enabled: true + initialDelaySeconds: 15 + ## + ## Default Kubernetes values + periodSeconds: 10 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 3 + +metrics: + enabled: false + image: + registry: docker.io + repository: prom/mysqld-exporter + tag: v0.10.0 + pullPolicy: IfNotPresent + resources: {} + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: "9104" diff --git a/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/requirements.lock b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/requirements.lock new file mode 100755 index 000000000..cb3439862 --- /dev/null +++ b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/requirements.lock @@ -0,0 +1,6 @@ +dependencies: +- name: mariadb + repository: https://kubernetes-charts.storage.googleapis.com/ + version: 4.3.1 +digest: sha256:82a0e5374376169d2ecf7d452c18a2ed93507f5d17c3393a1457f9ffad7e9b26 +generated: 2018-08-02T22:07:51.905271776Z diff --git a/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/requirements.yaml b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/requirements.yaml new file mode 100755 index 000000000..a894b8b3b --- /dev/null +++ b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/requirements.yaml @@ -0,0 +1,7 @@ +dependencies: +- name: mariadb + version: 4.x.x + repository: https://kubernetes-charts.storage.googleapis.com/ + condition: mariadb.enabled + tags: + - wordpress-database diff --git a/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/templates/NOTES.txt b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/templates/NOTES.txt new file mode 100755 index 000000000..75ed9b64f --- /dev/null +++ b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/templates/NOTES.txt @@ -0,0 +1 @@ +Placeholder. diff --git a/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/values.yaml b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/values.yaml new file mode 100755 index 000000000..3cb66dafd --- /dev/null +++ b/pkg/action/testdata/charts/chart-with-uncompressed-dependencies/values.yaml @@ -0,0 +1,254 @@ +## Bitnami WordPress image version +## ref: https://hub.docker.com/r/bitnami/wordpress/tags/ +## +image: + registry: docker.io + repository: bitnami/wordpress + tag: 4.9.8-debian-9 + ## Specify a imagePullPolicy + ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images + ## + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistrKeySecretName + +## User of the application +## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables +## +wordpressUsername: user + +## Application password +## Defaults to a random 10-character alphanumeric string if not set +## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables +## +# wordpressPassword: + +## Admin email +## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables +## +wordpressEmail: user@example.com + +## First name +## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables +## +wordpressFirstName: FirstName + +## Last name +## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables +## +wordpressLastName: LastName + +## Blog name +## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables +## +wordpressBlogName: User's Blog! + +## Table prefix +## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables +## +wordpressTablePrefix: wp_ + +## Set to `yes` to allow the container to be started with blank passwords +## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables +allowEmptyPassword: yes + +## SMTP mail delivery configuration +## ref: https://github.com/bitnami/bitnami-docker-wordpress/#smtp-configuration +## +# smtpHost: +# smtpPort: +# smtpUser: +# smtpPassword: +# smtpUsername: +# smtpProtocol: + +replicaCount: 1 + +externalDatabase: +## All of these values are only used when mariadb.enabled is set to false + ## Database host + host: localhost + + ## non-root Username for Wordpress Database + user: bn_wordpress + + ## Database password + password: "" + + ## Database name + database: bitnami_wordpress + + ## Database port number + port: 3306 + +## +## MariaDB chart configuration +## +mariadb: + ## Whether to deploy a mariadb server to satisfy the applications database requirements. To use an external database set this to false and configure the externalDatabase parameters + enabled: true + ## Disable MariaDB replication + replication: + enabled: false + + ## Create a database and a database user + ## ref: https://github.com/bitnami/bitnami-docker-mariadb/blob/master/README.md#creating-a-database-user-on-first-run + ## + db: + name: bitnami_wordpress + user: bn_wordpress + ## If the password is not specified, mariadb will generates a random password + ## + # password: + + ## MariaDB admin password + ## ref: https://github.com/bitnami/bitnami-docker-mariadb/blob/master/README.md#setting-the-root-password-on-first-run + ## + # rootUser: + # password: + + ## Enable persistence using Persistent Volume Claims + ## ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ + ## + master: + persistence: + enabled: true + ## mariadb data Persistent Volume Storage Class + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + # storageClass: "-" + accessMode: ReadWriteOnce + size: 8Gi + +## Kubernetes configuration +## For minikube, set this to NodePort, elsewhere use LoadBalancer or ClusterIP +## +serviceType: LoadBalancer +## +## serviceType: NodePort +## nodePorts: +## http: +## https: +nodePorts: + http: "" + https: "" +## Enable client source IP preservation +## ref http://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip +## +serviceExternalTrafficPolicy: Cluster + +## Allow health checks to be pointed at the https port +healthcheckHttps: false + +## Configure extra options for liveness and readiness probes +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes) +livenessProbe: + initialDelaySeconds: 120 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 +readinessProbe: + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 + +## Configure the ingress resource that allows you to access the +## Wordpress installation. Set up the URL +## ref: http://kubernetes.io/docs/user-guide/ingress/ +## +ingress: + ## Set to true to enable ingress record generation + enabled: false + + ## The list of hostnames to be covered with this ingress record. + ## Most likely this will be just one host, but in the event more hosts are needed, this is an array + hosts: + - name: wordpress.local + + ## Set this to true in order to enable TLS on the ingress record + ## A side effect of this will be that the backend wordpress service will be connected at port 443 + tls: false + + ## If TLS is set to true, you must declare what secret will store the key/certificate for TLS + tlsSecret: wordpress.local-tls + + ## Ingress annotations done as key:value pairs + ## If you're using kube-lego, you will want to add: + ## kubernetes.io/tls-acme: true + ## + ## For a full list of possible ingress annotations, please see + ## ref: https://github.com/kubernetes/ingress-nginx/blob/master/docs/annotations.md + ## + ## If tls is set to true, annotation ingress.kubernetes.io/secure-backends: "true" will automatically be set + annotations: + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: true + + secrets: + ## If you're providing your own certificates, please use this to add the certificates as secrets + ## key and certificate should start with -----BEGIN CERTIFICATE----- or + ## -----BEGIN RSA PRIVATE KEY----- + ## + ## name should line up with a tlsSecret set further up + ## If you're using kube-lego, this is unneeded, as it will create the secret for you if it is not set + ## + ## It is also possible to create and manage the certificates outside of this helm chart + ## Please see README.md for more information + # - name: wordpress.local-tls + # key: + # certificate: + +## Enable persistence using Persistent Volume Claims +## ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ +## +persistence: + enabled: true + ## wordpress data Persistent Volume Storage Class + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + # storageClass: "-" + ## + ## If you want to reuse an existing claim, you can pass the name of the PVC using + ## the existingClaim variable + # existingClaim: your-claim + accessMode: ReadWriteOnce + size: 10Gi + +## Configure resource requests and limits +## ref: http://kubernetes.io/docs/user-guide/compute-resources/ +## +resources: + requests: + memory: 512Mi + cpu: 300m + +## Node labels for pod assignment +## Ref: https://kubernetes.io/docs/user-guide/node-selection/ +## +nodeSelector: {} + +## Tolerations for pod assignment +## Ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ +## +tolerations: [] + +## Affinity for pod assignment +## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity +## +affinity: {} diff --git a/pkg/action/testdata/output/compressed-deps-tgz.txt b/pkg/action/testdata/output/compressed-deps-tgz.txt new file mode 100644 index 000000000..6cc526b70 --- /dev/null +++ b/pkg/action/testdata/output/compressed-deps-tgz.txt @@ -0,0 +1,3 @@ +NAME VERSION REPOSITORY STATUS +mariadb 4.x.x https://kubernetes-charts.storage.googleapis.com/ unpacked + diff --git a/pkg/action/testdata/output/compressed-deps.txt b/pkg/action/testdata/output/compressed-deps.txt new file mode 100644 index 000000000..ff2b0ab75 --- /dev/null +++ b/pkg/action/testdata/output/compressed-deps.txt @@ -0,0 +1,3 @@ +NAME VERSION REPOSITORY STATUS +mariadb 4.x.x https://kubernetes-charts.storage.googleapis.com/ ok + diff --git a/pkg/action/testdata/output/missing-deps.txt b/pkg/action/testdata/output/missing-deps.txt new file mode 100644 index 000000000..8d742883a --- /dev/null +++ b/pkg/action/testdata/output/missing-deps.txt @@ -0,0 +1,3 @@ +NAME VERSION REPOSITORY STATUS +mariadb 4.x.x https://kubernetes-charts.storage.googleapis.com/ missing + diff --git a/pkg/action/testdata/output/uncompressed-deps-tgz.txt b/pkg/action/testdata/output/uncompressed-deps-tgz.txt new file mode 100644 index 000000000..6cc526b70 --- /dev/null +++ b/pkg/action/testdata/output/uncompressed-deps-tgz.txt @@ -0,0 +1,3 @@ +NAME VERSION REPOSITORY STATUS +mariadb 4.x.x https://kubernetes-charts.storage.googleapis.com/ unpacked + diff --git a/pkg/action/testdata/output/uncompressed-deps.txt b/pkg/action/testdata/output/uncompressed-deps.txt new file mode 100644 index 000000000..6cc526b70 --- /dev/null +++ b/pkg/action/testdata/output/uncompressed-deps.txt @@ -0,0 +1,3 @@ +NAME VERSION REPOSITORY STATUS +mariadb 4.x.x https://kubernetes-charts.storage.googleapis.com/ unpacked + From f89d985cb8248be7db0850c018505836d303ff1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Lindh=C3=A9?= Date: Fri, 10 Apr 2020 17:39:34 +0200 Subject: [PATCH 038/281] Implement support for multiple args for repo remove (#7791) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Implement support for multiple args for repo remove Signed-off-by: Andreas Lindhé --- cmd/helm/repo_list.go | 29 ++++++++++- cmd/helm/repo_remove.go | 35 +++++++------- cmd/helm/repo_remove_test.go | 94 ++++++++++++++++++++++++++++++------ cmd/helm/search_repo.go | 2 +- 4 files changed, 124 insertions(+), 36 deletions(-) diff --git a/cmd/helm/repo_list.go b/cmd/helm/repo_list.go index 25316bafc..51b4f0d58 100644 --- a/cmd/helm/repo_list.go +++ b/cmd/helm/repo_list.go @@ -97,13 +97,38 @@ func (r *repoListWriter) encodeByFormat(out io.Writer, format output.Format) err return nil } +// Returns all repos from repos, except those with names matching ignoredRepoNames +// Inspired by https://stackoverflow.com/a/28701031/893211 +func filterRepos(repos []*repo.Entry, ignoredRepoNames []string) []*repo.Entry { + // if ignoredRepoNames is nil, just return repo + if ignoredRepoNames == nil { + return repos + } + + filteredRepos := make([]*repo.Entry, 0) + + ignored := make(map[string]bool, len(ignoredRepoNames)) + for _, repoName := range ignoredRepoNames { + ignored[repoName] = true + } + + for _, repo := range repos { + if _, removed := ignored[repo.Name]; !removed { + filteredRepos = append(filteredRepos, repo) + } + } + + return filteredRepos +} + // Provide dynamic auto-completion for repo names -func compListRepos(prefix string) []string { +func compListRepos(prefix string, ignoredRepoNames []string) []string { var rNames []string f, err := repo.LoadFile(settings.RepositoryConfig) if err == nil && len(f.Repositories) > 0 { - for _, repo := range f.Repositories { + filteredRepos := filterRepos(f.Repositories, ignoredRepoNames) + for _, repo := range filteredRepos { if strings.HasPrefix(repo.Name, prefix) { rNames = append(rNames, repo.Name) } diff --git a/cmd/helm/repo_remove.go b/cmd/helm/repo_remove.go index e8c0ec027..5dad4e5e0 100644 --- a/cmd/helm/repo_remove.go +++ b/cmd/helm/repo_remove.go @@ -32,7 +32,7 @@ import ( ) type repoRemoveOptions struct { - name string + names []string repoFile string repoCache string } @@ -41,24 +41,21 @@ func newRepoRemoveCmd(out io.Writer) *cobra.Command { o := &repoRemoveOptions{} cmd := &cobra.Command{ - Use: "remove [NAME]", + Use: "remove [REPO1 [REPO2 ...]]", Aliases: []string{"rm"}, - Short: "remove a chart repository", - Args: require.ExactArgs(1), + Short: "remove one or more chart repositories", + Args: require.MinimumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { o.repoFile = settings.RepositoryConfig o.repoCache = settings.RepositoryCache - o.name = args[0] + o.names = args return o.run(out) }, } // Function providing dynamic auto-completion completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) != 0 { - return nil, completion.BashCompDirectiveNoFileComp - } - return compListRepos(toComplete), completion.BashCompDirectiveNoFileComp + return compListRepos(toComplete, args), completion.BashCompDirectiveNoFileComp }) return cmd @@ -70,18 +67,20 @@ func (o *repoRemoveOptions) run(out io.Writer) error { return errors.New("no repositories configured") } - if !r.Remove(o.name) { - return errors.Errorf("no repo named %q found", o.name) - } - if err := r.WriteFile(o.repoFile, 0644); err != nil { - return err - } + for _, name := range o.names { + if !r.Remove(name) { + return errors.Errorf("no repo named %q found", name) + } + if err := r.WriteFile(o.repoFile, 0644); err != nil { + return err + } - if err := removeRepoCache(o.repoCache, o.name); err != nil { - return err + if err := removeRepoCache(o.repoCache, name); err != nil { + return err + } + fmt.Fprintf(out, "%q has been removed from your repositories\n", name) } - fmt.Fprintf(out, "%q has been removed from your repositories\n", o.name) return nil } diff --git a/cmd/helm/repo_remove_test.go b/cmd/helm/repo_remove_test.go index 85c76bb92..f7d50140e 100644 --- a/cmd/helm/repo_remove_test.go +++ b/cmd/helm/repo_remove_test.go @@ -44,7 +44,7 @@ func TestRepoRemove(t *testing.T) { b := bytes.NewBuffer(nil) rmOpts := repoRemoveOptions{ - name: testRepoName, + names: []string{testRepoName}, repoFile: repoFile, repoCache: rootDir, } @@ -62,14 +62,9 @@ func TestRepoRemove(t *testing.T) { t.Error(err) } - idx := filepath.Join(rootDir, helmpath.CacheIndexFile(testRepoName)) - mf, _ := os.Create(idx) - mf.Close() - - idx2 := filepath.Join(rootDir, helmpath.CacheChartsFile(testRepoName)) - mf, _ = os.Create(idx2) - mf.Close() + cacheIndexFile, cacheChartsFile := createCacheFiles(rootDir, testRepoName) + // Reset the buffer before running repo remove b.Reset() if err := rmOpts.run(b); err != nil { @@ -79,13 +74,7 @@ func TestRepoRemove(t *testing.T) { t.Errorf("Unexpected output: %s", b.String()) } - if _, err := os.Stat(idx); err == nil { - t.Errorf("Error cache index file was not removed for repository %s", testRepoName) - } - - if _, err := os.Stat(idx2); err == nil { - t.Errorf("Error cache chart file was not removed for repository %s", testRepoName) - } + testCacheFiles(t, cacheIndexFile, cacheChartsFile, testRepoName) f, err := repo.LoadFile(repoFile) if err != nil { @@ -95,4 +84,79 @@ func TestRepoRemove(t *testing.T) { if f.Has(testRepoName) { t.Errorf("%s was not successfully removed from repositories list", testRepoName) } + + // Test removal of multiple repos in one go + var testRepoNames = []string{"foo", "bar", "baz"} + cacheFiles := make(map[string][]string, len(testRepoNames)) + + // Add test repos + for _, repoName := range testRepoNames { + o := &repoAddOptions{ + name: repoName, + url: ts.URL(), + repoFile: repoFile, + } + + if err := o.run(os.Stderr); err != nil { + t.Error(err) + } + + cacheIndex, cacheChart := createCacheFiles(rootDir, repoName) + cacheFiles[repoName] = []string{cacheIndex, cacheChart} + + } + + // Create repo remove command + multiRmOpts := repoRemoveOptions{ + names: testRepoNames, + repoFile: repoFile, + repoCache: rootDir, + } + + // Reset the buffer before running repo remove + b.Reset() + + // Run repo remove command + if err := multiRmOpts.run(b); err != nil { + t.Errorf("Error removing list of repos from repositories: %q", testRepoNames) + } + + // Check that stuff were removed + if !strings.Contains(b.String(), "has been removed") { + t.Errorf("Unexpected output: %s", b.String()) + } + + for _, repoName := range testRepoNames { + f, err := repo.LoadFile(repoFile) + if err != nil { + t.Error(err) + } + if f.Has(repoName) { + t.Errorf("%s was not successfully removed from repositories list", repoName) + } + cacheIndex := cacheFiles[repoName][0] + cacheChart := cacheFiles[repoName][1] + testCacheFiles(t, cacheIndex, cacheChart, repoName) + } +} + +func createCacheFiles(rootDir string, repoName string) (cacheIndexFile string, cacheChartsFile string) { + cacheIndexFile = filepath.Join(rootDir, helmpath.CacheIndexFile(repoName)) + mf, _ := os.Create(cacheIndexFile) + mf.Close() + + cacheChartsFile = filepath.Join(rootDir, helmpath.CacheChartsFile(repoName)) + mf, _ = os.Create(cacheChartsFile) + mf.Close() + + return cacheIndexFile, cacheChartsFile +} + +func testCacheFiles(t *testing.T, cacheIndexFile string, cacheChartsFile string, repoName string) { + if _, err := os.Stat(cacheIndexFile); err == nil { + t.Errorf("Error cache index file was not removed for repository %s", repoName) + } + if _, err := os.Stat(cacheChartsFile); err == nil { + t.Errorf("Error cache chart file was not removed for repository %s", repoName) + } } diff --git a/cmd/helm/search_repo.go b/cmd/helm/search_repo.go index 8a8ac379d..c7f0da974 100644 --- a/cmd/helm/search_repo.go +++ b/cmd/helm/search_repo.go @@ -298,7 +298,7 @@ func compListCharts(toComplete string, includeFiles bool) ([]string, completion. var completions []string // First check completions for repos - repos := compListRepos("") + repos := compListRepos("", nil) for _, repo := range repos { repoWithSlash := fmt.Sprintf("%s/", repo) if strings.HasPrefix(toComplete, repoWithSlash) { From 0d9727d6abb466f4628db16a3ee81cddd4f31d22 Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Fri, 10 Apr 2020 12:15:27 -0400 Subject: [PATCH 039/281] Adding template docs to the version command Signed-off-by: Matt Farina --- cmd/helm/version.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cmd/helm/version.go b/cmd/helm/version.go index 3067f7289..b3f831e07 100644 --- a/cmd/helm/version.go +++ b/cmd/helm/version.go @@ -39,6 +39,14 @@ version.BuildInfo{Version:"v2.0.0", GitCommit:"ff52399e51bb880526e9cd0ed8386f643 - GitCommit is the SHA for the commit that this version was built from. - GitTreeState is "clean" if there are no local code changes when this binary was built, and "dirty" if the binary was built from locally modified code. + +When using the --template flag the following properties are available to use in +the template: + +- .Version contains the semantic version of Helm +- .GitCommit is the git commit +- .GitTreeState is the state of the git tree when Helm was built +- .GoVersion contains the version of Go that Helm was compiled with ` type versionOptions struct { From c2da4fd53dc57189a46797a87b6b30dcd8d44879 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Mon, 13 Apr 2020 08:40:38 -0700 Subject: [PATCH 040/281] ref(*): kubernetes v1.18 (#7831) Upgrade Kubernetes libraries to v0.18.0 Add new lazy load KubernetesClientSet to avoid missing kubeconfig error In kubernetes v1.18 kubeconfig validation was added. Minikube and Kind both remove kubeconfig when stopping clusters. This causes and error when running any helm commands because we initialize the client before executing the command. Signed-off-by: Adam Reese --- cmd/helm/root.go | 3 +- go.mod | 16 +- go.sum | 41 ++++ .../deployment/util/deploymentutil.go | 3 +- pkg/action/action.go | 12 +- pkg/action/lazyclient.go | 182 ++++++++++++++++++ pkg/action/release_testing.go | 3 +- pkg/action/upgrade.go | 5 +- pkg/engine/lookup_func.go | 5 +- pkg/kube/client.go | 22 ++- pkg/kube/wait.go | 15 +- pkg/storage/driver/cfgmaps.go | 13 +- pkg/storage/driver/mock_test.go | 21 +- pkg/storage/driver/secrets.go | 13 +- 14 files changed, 298 insertions(+), 56 deletions(-) create mode 100644 pkg/action/lazyclient.go diff --git a/cmd/helm/root.go b/cmd/helm/root.go index 3ebea3bae..3e3dfa012 100644 --- a/cmd/helm/root.go +++ b/cmd/helm/root.go @@ -17,6 +17,7 @@ limitations under the License. package main // import "helm.sh/helm/v3/cmd/helm" import ( + "context" "fmt" "io" "strings" @@ -92,7 +93,7 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string completion.CompDebugln(fmt.Sprintf("About to call kube client for namespaces with timeout of: %d", to)) nsNames := []string{} - if namespaces, err := client.CoreV1().Namespaces().List(metav1.ListOptions{TimeoutSeconds: &to}); err == nil { + if namespaces, err := client.CoreV1().Namespaces().List(context.Background(), metav1.ListOptions{TimeoutSeconds: &to}); err == nil { for _, ns := range namespaces.Items { if strings.HasPrefix(ns.Name, toComplete) { nsNames = append(nsNames, ns.Name) diff --git a/go.mod b/go.mod index 7ba7a5542..3413112cd 100644 --- a/go.mod +++ b/go.mod @@ -28,15 +28,15 @@ require ( github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.4.0 github.com/xeipuuv/gojsonschema v1.1.0 - golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d - k8s.io/api v0.17.3 - k8s.io/apiextensions-apiserver v0.17.3 - k8s.io/apimachinery v0.17.3 - k8s.io/cli-runtime v0.17.3 - k8s.io/client-go v0.17.3 + golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 + k8s.io/api v0.18.0 + k8s.io/apiextensions-apiserver v0.18.0 + k8s.io/apimachinery v0.18.0 + k8s.io/cli-runtime v0.18.0 + k8s.io/client-go v0.18.0 k8s.io/klog v1.0.0 - k8s.io/kubectl v0.17.3 - sigs.k8s.io/yaml v1.1.0 + k8s.io/kubectl v0.18.0 + sigs.k8s.io/yaml v1.2.0 ) replace ( diff --git a/go.sum b/go.sum index 4d8cc83a2..3c08dba4b 100644 --- a/go.sum +++ b/go.sum @@ -144,6 +144,7 @@ github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:Htrtb github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e h1:p1yVGRW3nmb85p1Sh1ZJSDm4A4iKLS5QNbvUHMgGu/M= github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= @@ -252,6 +253,8 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -260,6 +263,8 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d h1:7XGaL1e6bYS1yIonGp9761ExpPPV1ui0SAC59Yube9k= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.1.0 h1:rVsPeBmXbYv4If/cumu1AzZPwV58q433hvONV1UEZoI= +github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/391+sT5o= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -365,6 +370,7 @@ github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -404,6 +410,8 @@ github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1: github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= @@ -496,6 +504,8 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d h1:9FCpayM9Egr1baVnV1SX0H87m+XB0B8S0hAMi99X/3U= golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo= +golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -556,6 +566,8 @@ golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3 h1:7TYNF4UdlohbFwpNH04CoPMp1cHUZgO1Ebq5r2hIjfo= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7 h1:HmbHVPwrPEKPGLAcHSrMe6+hqSUlvZU0rab6x5EXfGU= +golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -606,6 +618,7 @@ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= @@ -639,41 +652,69 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/api v0.17.3 h1:XAm3PZp3wnEdzekNkcmj/9Y1zdmQYJ1I4GKSBBZ8aG0= k8s.io/api v0.17.3/go.mod h1:YZ0OTkuw7ipbe305fMpIdf3GLXZKRigjtZaV5gzC2J0= +k8s.io/api v0.18.0 h1:lwYk8Vt7rsVTwjRU6pzEsa9YNhThbmbocQlKvNBB4EQ= +k8s.io/api v0.18.0/go.mod h1:q2HRQkfDzHMBZL9l/y9rH63PkQl4vae0xRT+8prbrK8= k8s.io/apiextensions-apiserver v0.17.3 h1:WDZWkPcbgvchEdDd7ysL21GGPx3UKZQLDZXEkevT6n4= k8s.io/apiextensions-apiserver v0.17.3/go.mod h1:CJbCyMfkKftAd/X/V6OTHYhVn7zXnDdnkUjS1h0GTeY= +k8s.io/apiextensions-apiserver v0.18.0 h1:HN4/P8vpGZFvB5SOMuPPH2Wt9Y/ryX+KRvIyAkchu1Q= +k8s.io/apiextensions-apiserver v0.18.0/go.mod h1:18Cwn1Xws4xnWQNC00FLq1E350b9lUF+aOdIWDOZxgo= k8s.io/apimachinery v0.17.3 h1:f+uZV6rm4/tHE7xXgLyToprg6xWairaClGVkm2t8omg= k8s.io/apimachinery v0.17.3/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g= +k8s.io/apimachinery v0.18.0 h1:fuPfYpk3cs1Okp/515pAf0dNhL66+8zk8RLbSX+EgAE= +k8s.io/apimachinery v0.18.0/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= k8s.io/apiserver v0.17.3/go.mod h1:iJtsPpu1ZpEnHaNawpSV0nYTGBhhX2dUlnn7/QS7QiY= +k8s.io/apiserver v0.18.0/go.mod h1:3S2O6FeBBd6XTo0njUrLxiqk8GNy6wWOftjhJcXYnjw= k8s.io/cli-runtime v0.17.3 h1:0ZlDdJgJBKsu77trRUynNiWsRuAvAVPBNaQfnt/1qtc= k8s.io/cli-runtime v0.17.3/go.mod h1:X7idckYphH4SZflgNpOOViSxetiMj6xI0viMAjM81TA= +k8s.io/cli-runtime v0.18.0 h1:jG8XpSqQ5TrV0N+EZ3PFz6+gqlCk71dkggWCCq9Mq34= +k8s.io/cli-runtime v0.18.0/go.mod h1:1eXfmBsIJosjn9LjEBUd2WVPoPAY9XGTqTFcPMIBsUQ= k8s.io/client-go v0.17.3 h1:deUna1Ksx05XeESH6XGCyONNFfiQmDdqeqUvicvP6nU= k8s.io/client-go v0.17.3/go.mod h1:cLXlTMtWHkuK4tD360KpWz2gG2KtdWEr/OT02i3emRQ= +k8s.io/client-go v0.18.0 h1:yqKw4cTUQraZK3fcVCMeSa+lqKwcjZ5wtcOIPnxQno4= +k8s.io/client-go v0.18.0/go.mod h1:uQSYDYs4WhVZ9i6AIoEZuwUggLVEF64HOD37boKAtF8= k8s.io/code-generator v0.17.3/go.mod h1:l8BLVwASXQZTo2xamW5mQNFCe1XPiAesVq7Y1t7PiQQ= +k8s.io/code-generator v0.18.0/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc= k8s.io/component-base v0.17.3 h1:hQzTSshY14aLSR6WGIYvmw+w+u6V4d+iDR2iDGMrlUg= k8s.io/component-base v0.17.3/go.mod h1:GeQf4BrgelWm64PXkIXiPh/XS0hnO42d9gx9BtbZRp8= +k8s.io/component-base v0.18.0 h1:I+lP0fNfsEdTDpHaL61bCAqTZLoiWjEEP304Mo5ZQgE= +k8s.io/component-base v0.18.0/go.mod h1:u3BCg0z1uskkzrnAKFzulmYaEpZF7XC9Pf/uFyb1v2c= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLyy7rfbeuf1PYyBf973pgyU= k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= +k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c h1:/KUFqjjqAcY4Us6luF5RDNZ16KJtb49HfR3ZHB9qYXM= +k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= k8s.io/kubectl v0.17.3 h1:9HHYj07kuFkM+sMJMOyQX29CKWq4lvKAG1UIPxNPMQ4= k8s.io/kubectl v0.17.3/go.mod h1:NUn4IBY7f7yCMwSop2HCXlw/MVYP4HJBiUmOR3n9w28= +k8s.io/kubectl v0.18.0 h1:hu52Ndq/d099YW+3sS3VARxFz61Wheiq8K9S7oa82Dk= +k8s.io/kubectl v0.18.0/go.mod h1:LOkWx9Z5DXMEg5KtOjHhRiC1fqJPLyCr3KtQgEolCkU= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/metrics v0.17.3/go.mod h1:HEJGy1fhHOjHggW9rMDBJBD3YuGroH3Y1pnIRw9FFaI= +k8s.io/metrics v0.18.0/go.mod h1:8aYTW18koXqjLVKL7Ds05RPMX9ipJZI3mywYvBOxXd4= k8s.io/utils v0.0.0-20191114184206-e782cd3c129f h1:GiPwtSzdP43eI1hpPCbROQCCIgCuiMMNF8YUVLF3vJo= k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89 h1:d4vVOjXm687F1iLSP2q3lyPPuyvTUt3aVoBpi2DqRsU= +k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0= sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0= sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18= +sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= +sigs.k8s.io/structured-merge-diff/v3 v3.0.0 h1:dOmIZBMfhcHS09XZkMyUgkq5trg3/jRyJYFZUiaOp8E= +sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI= diff --git a/internal/third_party/k8s.io/kubernetes/deployment/util/deploymentutil.go b/internal/third_party/k8s.io/kubernetes/deployment/util/deploymentutil.go index da93a6910..103db35c4 100644 --- a/internal/third_party/k8s.io/kubernetes/deployment/util/deploymentutil.go +++ b/internal/third_party/k8s.io/kubernetes/deployment/util/deploymentutil.go @@ -17,6 +17,7 @@ limitations under the License. package util import ( + "context" "sort" apps "k8s.io/api/apps/v1" @@ -116,7 +117,7 @@ func GetNewReplicaSet(deployment *apps.Deployment, c appsclient.AppsV1Interface) // RsListFromClient returns an rsListFunc that wraps the given client. func RsListFromClient(c appsclient.AppsV1Interface) RsListFunc { return func(namespace string, options metav1.ListOptions) ([]*apps.ReplicaSet, error) { - rsList, err := c.ReplicaSets(namespace).List(options) + rsList, err := c.ReplicaSets(namespace).List(context.Background(), options) if err != nil { return nil, err } diff --git a/pkg/action/action.go b/pkg/action/action.go index e4db942c8..05a133abf 100644 --- a/pkg/action/action.go +++ b/pkg/action/action.go @@ -221,23 +221,23 @@ func (c *Configuration) recordRelease(r *release.Release) { } // Init initializes the action configuration -func (c *Configuration) Init(getter genericclioptions.RESTClientGetter, namespace string, helmDriver string, log DebugLog) error { +func (c *Configuration) Init(getter genericclioptions.RESTClientGetter, namespace, helmDriver string, log DebugLog) error { kc := kube.New(getter) kc.Log = log - clientset, err := kc.Factory.KubernetesClientSet() - if err != nil { - return err + lazyClient := &lazyClient{ + namespace: namespace, + clientFn: kc.Factory.KubernetesClientSet, } var store *storage.Storage switch helmDriver { case "secret", "secrets", "": - d := driver.NewSecrets(clientset.CoreV1().Secrets(namespace)) + d := driver.NewSecrets(newSecretClient(lazyClient)) d.Log = log store = storage.Init(d) case "configmap", "configmaps": - d := driver.NewConfigMaps(clientset.CoreV1().ConfigMaps(namespace)) + d := driver.NewConfigMaps(newConfigMapClient(lazyClient)) d.Log = log store = storage.Init(d) case "memory": diff --git a/pkg/action/lazyclient.go b/pkg/action/lazyclient.go new file mode 100644 index 000000000..0bd57ff5b --- /dev/null +++ b/pkg/action/lazyclient.go @@ -0,0 +1,182 @@ +/* +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 action + +import ( + "context" + "sync" + + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/kubernetes" + corev1 "k8s.io/client-go/kubernetes/typed/core/v1" +) + +// lazyClient is a workaround to deal with Kubernetes having an unstable client API. +// In Kubernetes v1.18 the defaults where removed which broke creating a +// client without an explicit configuration. ಠ_ಠ +type lazyClient struct { + // client caches an initialized kubernetes client + initClient sync.Once + client kubernetes.Interface + clientErr error + + // clientFn loads a kubernetes client + clientFn func() (*kubernetes.Clientset, error) + + // namespace passed to each client request + namespace string +} + +func (s *lazyClient) init() error { + s.initClient.Do(func() { + s.client, s.clientErr = s.clientFn() + }) + return s.clientErr +} + +// secretClient implements a corev1.SecretsInterface +type secretClient struct{ *lazyClient } + +var _ corev1.SecretInterface = (*secretClient)(nil) + +func newSecretClient(lc *lazyClient) *secretClient { + return &secretClient{lazyClient: lc} +} + +func (s *secretClient) Create(ctx context.Context, secret *v1.Secret, opts metav1.CreateOptions) (*v1.Secret, error) { + if err := s.init(); err != nil { + return nil, err + } + return s.client.CoreV1().Secrets(s.namespace).Create(ctx, secret, opts) +} + +func (s *secretClient) Update(ctx context.Context, secret *v1.Secret, opts metav1.UpdateOptions) (*v1.Secret, error) { + if err := s.init(); err != nil { + return nil, err + } + return s.client.CoreV1().Secrets(s.namespace).Update(ctx, secret, opts) +} + +func (s *secretClient) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { + if err := s.init(); err != nil { + return err + } + return s.client.CoreV1().Secrets(s.namespace).Delete(ctx, name, opts) +} + +func (s *secretClient) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error { + if err := s.init(); err != nil { + return err + } + return s.client.CoreV1().Secrets(s.namespace).DeleteCollection(ctx, opts, listOpts) +} + +func (s *secretClient) Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.Secret, error) { + if err := s.init(); err != nil { + return nil, err + } + return s.client.CoreV1().Secrets(s.namespace).Get(ctx, name, opts) +} + +func (s *secretClient) List(ctx context.Context, opts metav1.ListOptions) (*v1.SecretList, error) { + if err := s.init(); err != nil { + return nil, err + } + return s.client.CoreV1().Secrets(s.namespace).List(ctx, opts) +} + +func (s *secretClient) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) { + if err := s.init(); err != nil { + return nil, err + } + return s.client.CoreV1().Secrets(s.namespace).Watch(ctx, opts) +} + +func (s *secretClient) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (*v1.Secret, error) { + if err := s.init(); err != nil { + return nil, err + } + return s.client.CoreV1().Secrets(s.namespace).Patch(ctx, name, pt, data, opts, subresources...) +} + +// configMapClient implements a corev1.ConfigMapInterface +type configMapClient struct{ *lazyClient } + +var _ corev1.ConfigMapInterface = (*configMapClient)(nil) + +func newConfigMapClient(lc *lazyClient) *configMapClient { + return &configMapClient{lazyClient: lc} +} + +func (c *configMapClient) Create(ctx context.Context, configMap *v1.ConfigMap, opts metav1.CreateOptions) (*v1.ConfigMap, error) { + if err := c.init(); err != nil { + return nil, err + } + return c.client.CoreV1().ConfigMaps(c.namespace).Create(ctx, configMap, opts) +} + +func (c *configMapClient) Update(ctx context.Context, configMap *v1.ConfigMap, opts metav1.UpdateOptions) (*v1.ConfigMap, error) { + if err := c.init(); err != nil { + return nil, err + } + return c.client.CoreV1().ConfigMaps(c.namespace).Update(ctx, configMap, opts) +} + +func (c *configMapClient) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { + if err := c.init(); err != nil { + return err + } + return c.client.CoreV1().ConfigMaps(c.namespace).Delete(ctx, name, opts) +} + +func (c *configMapClient) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error { + if err := c.init(); err != nil { + return err + } + return c.client.CoreV1().ConfigMaps(c.namespace).DeleteCollection(ctx, opts, listOpts) +} + +func (c *configMapClient) Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.ConfigMap, error) { + if err := c.init(); err != nil { + return nil, err + } + return c.client.CoreV1().ConfigMaps(c.namespace).Get(ctx, name, opts) +} + +func (c *configMapClient) List(ctx context.Context, opts metav1.ListOptions) (*v1.ConfigMapList, error) { + if err := c.init(); err != nil { + return nil, err + } + return c.client.CoreV1().ConfigMaps(c.namespace).List(ctx, opts) +} + +func (c *configMapClient) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) { + if err := c.init(); err != nil { + return nil, err + } + return c.client.CoreV1().ConfigMaps(c.namespace).Watch(ctx, opts) +} + +func (c *configMapClient) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (*v1.ConfigMap, error) { + if err := c.init(); err != nil { + return nil, err + } + return c.client.CoreV1().ConfigMaps(c.namespace).Patch(ctx, name, pt, data, opts, subresources...) +} diff --git a/pkg/action/release_testing.go b/pkg/action/release_testing.go index b7a1da757..795c3c747 100644 --- a/pkg/action/release_testing.go +++ b/pkg/action/release_testing.go @@ -17,6 +17,7 @@ limitations under the License. package action import ( + "context" "fmt" "io" "time" @@ -81,7 +82,7 @@ func (r *ReleaseTesting) GetPodLogs(out io.Writer, rel *release.Release) error { for _, e := range h.Events { if e == release.HookTest { req := client.CoreV1().Pods(r.Namespace).GetLogs(h.Name, &v1.PodLogOptions{}) - logReader, err := req.Stream() + logReader, err := req.Stream(context.Background()) if err != nil { return errors.Wrapf(err, "unable to get pod logs for %s", h.Name) } diff --git a/pkg/action/upgrade.go b/pkg/action/upgrade.go index c8e71c6d4..b73e2ac8b 100644 --- a/pkg/action/upgrade.go +++ b/pkg/action/upgrade.go @@ -18,6 +18,7 @@ package action import ( "bytes" + "context" "fmt" "strings" "time" @@ -428,7 +429,7 @@ func recreate(cfg *Configuration, resources kube.ResourceList) error { return errors.Wrapf(err, "unable to recreate pods for object %s/%s because an error occurred", res.Namespace, res.Name) } - pods, err := client.CoreV1().Pods(res.Namespace).List(metav1.ListOptions{ + pods, err := client.CoreV1().Pods(res.Namespace).List(context.Background(), metav1.ListOptions{ LabelSelector: selector.String(), }) if err != nil { @@ -438,7 +439,7 @@ func recreate(cfg *Configuration, resources kube.ResourceList) error { // Restart pods for _, pod := range pods.Items { // Delete each pod for get them restarted with changed spec. - if err := client.CoreV1().Pods(pod.Namespace).Delete(pod.Name, metav1.NewPreconditionDeleteOptions(string(pod.UID))); err != nil { + if err := client.CoreV1().Pods(pod.Namespace).Delete(context.Background(), pod.Name, *metav1.NewPreconditionDeleteOptions(string(pod.UID))); err != nil { return errors.Wrapf(err, "unable to recreate pods for object %s/%s because an error occurred", res.Namespace, res.Name) } } diff --git a/pkg/engine/lookup_func.go b/pkg/engine/lookup_func.go index 5dde29443..2527954fa 100644 --- a/pkg/engine/lookup_func.go +++ b/pkg/engine/lookup_func.go @@ -17,6 +17,7 @@ limitations under the License. package engine import ( + "context" "log" "strings" @@ -47,7 +48,7 @@ func NewLookupFunction(config *rest.Config) lookupFunc { } if name != "" { // this will return a single object - obj, err := client.Get(name, metav1.GetOptions{}) + obj, err := client.Get(context.Background(), name, metav1.GetOptions{}) if err != nil { if apierrors.IsNotFound(err) { // Just return an empty interface when the object was not found. @@ -59,7 +60,7 @@ func NewLookupFunction(config *rest.Config) lookupFunc { return obj.UnstructuredContent(), nil } //this will return a list - obj, err := client.List(metav1.ListOptions{}) + obj, err := client.List(context.Background(), metav1.ListOptions{}) if err != nil { if apierrors.IsNotFound(err) { // Just return an empty interface when the object was not found. diff --git a/pkg/kube/client.go b/pkg/kube/client.go index 2cb7e45cf..05b26b12a 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -88,10 +88,17 @@ var nopLogger = func(_ string, _ ...interface{}) {} // IsReachable tests connectivity to the cluster func (c *Client) IsReachable() error { - client, _ := c.Factory.KubernetesClientSet() - _, err := client.ServerVersion() + client, err := c.Factory.KubernetesClientSet() + if err == genericclioptions.ErrEmptyConfig { + // re-replace kubernetes ErrEmptyConfig error with a friendy error + // moar workarounds for Kubernetes API breaking. + return errors.New("Kubernetes cluster unreachable") + } if err != nil { - return fmt.Errorf("Kubernetes cluster unreachable: %s", err.Error()) + return errors.Wrap(err, "Kubernetes cluster unreachable") + } + if _, err := client.ServerVersion(); err != nil { + return errors.Wrap(err, "Kubernetes cluster unreachable") } return nil } @@ -344,7 +351,7 @@ func batchPerform(infos ResourceList, fn func(*resource.Info) error, errs chan<- } func createResource(info *resource.Info) error { - obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object, nil) + obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object) if err != nil { return err } @@ -568,9 +575,12 @@ func scrubValidationError(err error) error { // WaitAndGetCompletedPodPhase waits up to a timeout until a pod enters a completed phase // and returns said phase (PodSucceeded or PodFailed qualify). func (c *Client) WaitAndGetCompletedPodPhase(name string, timeout time.Duration) (v1.PodPhase, error) { - client, _ := c.Factory.KubernetesClientSet() + client, err := c.Factory.KubernetesClientSet() + if err != nil { + return v1.PodUnknown, err + } to := int64(timeout) - watcher, err := client.CoreV1().Pods(c.namespace()).Watch(metav1.ListOptions{ + watcher, err := client.CoreV1().Pods(c.namespace()).Watch(context.Background(), metav1.ListOptions{ FieldSelector: fmt.Sprintf("metadata.name=%s", name), TimeoutSeconds: &to, }) diff --git a/pkg/kube/wait.go b/pkg/kube/wait.go index 0254a60bb..90a9f8b38 100644 --- a/pkg/kube/wait.go +++ b/pkg/kube/wait.go @@ -17,6 +17,7 @@ limitations under the License. package kube // import "helm.sh/helm/v3/pkg/kube" import ( + "context" "fmt" "time" @@ -62,12 +63,12 @@ func (w *waiter) waitForResources(created ResourceList) error { ) switch value := AsVersioned(v).(type) { case *corev1.Pod: - pod, err := w.c.CoreV1().Pods(v.Namespace).Get(v.Name, metav1.GetOptions{}) + pod, err := w.c.CoreV1().Pods(v.Namespace).Get(context.Background(), v.Name, metav1.GetOptions{}) if err != nil || !w.isPodReady(pod) { return false, err } case *appsv1.Deployment, *appsv1beta1.Deployment, *appsv1beta2.Deployment, *extensionsv1beta1.Deployment: - currentDeployment, err := w.c.AppsV1().Deployments(v.Namespace).Get(v.Name, metav1.GetOptions{}) + currentDeployment, err := w.c.AppsV1().Deployments(v.Namespace).Get(context.Background(), v.Name, metav1.GetOptions{}) if err != nil { return false, err } @@ -84,7 +85,7 @@ func (w *waiter) waitForResources(created ResourceList) error { return false, nil } case *corev1.PersistentVolumeClaim: - claim, err := w.c.CoreV1().PersistentVolumeClaims(v.Namespace).Get(v.Name, metav1.GetOptions{}) + claim, err := w.c.CoreV1().PersistentVolumeClaims(v.Namespace).Get(context.Background(), v.Name, metav1.GetOptions{}) if err != nil { return false, err } @@ -92,7 +93,7 @@ func (w *waiter) waitForResources(created ResourceList) error { return false, nil } case *corev1.Service: - svc, err := w.c.CoreV1().Services(v.Namespace).Get(v.Name, metav1.GetOptions{}) + svc, err := w.c.CoreV1().Services(v.Namespace).Get(context.Background(), v.Name, metav1.GetOptions{}) if err != nil { return false, err } @@ -100,7 +101,7 @@ func (w *waiter) waitForResources(created ResourceList) error { return false, nil } case *extensionsv1beta1.DaemonSet, *appsv1.DaemonSet, *appsv1beta2.DaemonSet: - ds, err := w.c.AppsV1().DaemonSets(v.Namespace).Get(v.Name, metav1.GetOptions{}) + ds, err := w.c.AppsV1().DaemonSets(v.Namespace).Get(context.Background(), v.Name, metav1.GetOptions{}) if err != nil { return false, err } @@ -130,7 +131,7 @@ func (w *waiter) waitForResources(created ResourceList) error { return false, nil } case *appsv1.StatefulSet, *appsv1beta1.StatefulSet, *appsv1beta2.StatefulSet: - sts, err := w.c.AppsV1().StatefulSets(v.Namespace).Get(v.Name, metav1.GetOptions{}) + sts, err := w.c.AppsV1().StatefulSets(v.Namespace).Get(context.Background(), v.Name, metav1.GetOptions{}) if err != nil { return false, err } @@ -337,7 +338,7 @@ func (w *waiter) statefulSetReady(sts *appsv1.StatefulSet) bool { } func getPods(client kubernetes.Interface, namespace, selector string) ([]corev1.Pod, error) { - list, err := client.CoreV1().Pods(namespace).List(metav1.ListOptions{ + list, err := client.CoreV1().Pods(namespace).List(context.Background(), metav1.ListOptions{ LabelSelector: selector, }) return list.Items, err diff --git a/pkg/storage/driver/cfgmaps.go b/pkg/storage/driver/cfgmaps.go index f9d4da8c3..71e635975 100644 --- a/pkg/storage/driver/cfgmaps.go +++ b/pkg/storage/driver/cfgmaps.go @@ -17,6 +17,7 @@ limitations under the License. package driver // import "helm.sh/helm/v3/pkg/storage/driver" import ( + "context" "strconv" "strings" "time" @@ -62,7 +63,7 @@ func (cfgmaps *ConfigMaps) Name() string { // or error if not found. func (cfgmaps *ConfigMaps) Get(key string) (*rspb.Release, error) { // fetch the configmap holding the release named by key - obj, err := cfgmaps.impl.Get(key, metav1.GetOptions{}) + obj, err := cfgmaps.impl.Get(context.Background(), key, metav1.GetOptions{}) if err != nil { if apierrors.IsNotFound(err) { return nil, ErrReleaseNotFound @@ -88,7 +89,7 @@ func (cfgmaps *ConfigMaps) List(filter func(*rspb.Release) bool) ([]*rspb.Releas lsel := kblabels.Set{"owner": "helm"}.AsSelector() opts := metav1.ListOptions{LabelSelector: lsel.String()} - list, err := cfgmaps.impl.List(opts) + list, err := cfgmaps.impl.List(context.Background(), opts) if err != nil { cfgmaps.Log("list: failed to list: %s", err) return nil, err @@ -124,7 +125,7 @@ func (cfgmaps *ConfigMaps) Query(labels map[string]string) ([]*rspb.Release, err opts := metav1.ListOptions{LabelSelector: ls.AsSelector().String()} - list, err := cfgmaps.impl.List(opts) + list, err := cfgmaps.impl.List(context.Background(), opts) if err != nil { cfgmaps.Log("query: failed to query with labels: %s", err) return nil, err @@ -162,7 +163,7 @@ func (cfgmaps *ConfigMaps) Create(key string, rls *rspb.Release) error { return err } // push the configmap object out into the kubiverse - if _, err := cfgmaps.impl.Create(obj); err != nil { + if _, err := cfgmaps.impl.Create(context.Background(), obj, metav1.CreateOptions{}); err != nil { if apierrors.IsAlreadyExists(err) { return ErrReleaseExists } @@ -189,7 +190,7 @@ func (cfgmaps *ConfigMaps) Update(key string, rls *rspb.Release) error { return err } // push the configmap object out into the kubiverse - _, err = cfgmaps.impl.Update(obj) + _, err = cfgmaps.impl.Update(context.Background(), obj, metav1.UpdateOptions{}) if err != nil { cfgmaps.Log("update: failed to update: %s", err) return err @@ -204,7 +205,7 @@ func (cfgmaps *ConfigMaps) Delete(key string) (rls *rspb.Release, err error) { return nil, err } // delete the release - if err = cfgmaps.impl.Delete(key, &metav1.DeleteOptions{}); err != nil { + if err = cfgmaps.impl.Delete(context.Background(), key, metav1.DeleteOptions{}); err != nil { return rls, err } return rls, nil diff --git a/pkg/storage/driver/mock_test.go b/pkg/storage/driver/mock_test.go index 3cb3773c2..22e6454a1 100644 --- a/pkg/storage/driver/mock_test.go +++ b/pkg/storage/driver/mock_test.go @@ -17,6 +17,7 @@ limitations under the License. package driver // import "helm.sh/helm/v3/pkg/storage/driver" import ( + "context" "fmt" "testing" @@ -102,7 +103,7 @@ func (mock *MockConfigMapsInterface) Init(t *testing.T, releases ...*rspb.Releas } // Get returns the ConfigMap by name. -func (mock *MockConfigMapsInterface) Get(name string, options metav1.GetOptions) (*v1.ConfigMap, error) { +func (mock *MockConfigMapsInterface) Get(_ context.Context, name string, _ metav1.GetOptions) (*v1.ConfigMap, error) { object, ok := mock.objects[name] if !ok { return nil, apierrors.NewNotFound(v1.Resource("tests"), name) @@ -111,7 +112,7 @@ func (mock *MockConfigMapsInterface) Get(name string, options metav1.GetOptions) } // List returns the a of ConfigMaps. -func (mock *MockConfigMapsInterface) List(opts metav1.ListOptions) (*v1.ConfigMapList, error) { +func (mock *MockConfigMapsInterface) List(_ context.Context, _ metav1.ListOptions) (*v1.ConfigMapList, error) { var list v1.ConfigMapList for _, cfgmap := range mock.objects { list.Items = append(list.Items, *cfgmap) @@ -120,7 +121,7 @@ func (mock *MockConfigMapsInterface) List(opts metav1.ListOptions) (*v1.ConfigMa } // Create creates a new ConfigMap. -func (mock *MockConfigMapsInterface) Create(cfgmap *v1.ConfigMap) (*v1.ConfigMap, error) { +func (mock *MockConfigMapsInterface) Create(_ context.Context, cfgmap *v1.ConfigMap, _ metav1.CreateOptions) (*v1.ConfigMap, error) { name := cfgmap.ObjectMeta.Name if object, ok := mock.objects[name]; ok { return object, apierrors.NewAlreadyExists(v1.Resource("tests"), name) @@ -130,7 +131,7 @@ func (mock *MockConfigMapsInterface) Create(cfgmap *v1.ConfigMap) (*v1.ConfigMap } // Update updates a ConfigMap. -func (mock *MockConfigMapsInterface) Update(cfgmap *v1.ConfigMap) (*v1.ConfigMap, error) { +func (mock *MockConfigMapsInterface) Update(_ context.Context, cfgmap *v1.ConfigMap, _ metav1.UpdateOptions) (*v1.ConfigMap, error) { name := cfgmap.ObjectMeta.Name if _, ok := mock.objects[name]; !ok { return nil, apierrors.NewNotFound(v1.Resource("tests"), name) @@ -140,7 +141,7 @@ func (mock *MockConfigMapsInterface) Update(cfgmap *v1.ConfigMap) (*v1.ConfigMap } // Delete deletes a ConfigMap by name. -func (mock *MockConfigMapsInterface) Delete(name string, opts *metav1.DeleteOptions) error { +func (mock *MockConfigMapsInterface) Delete(_ context.Context, name string, _ metav1.DeleteOptions) error { if _, ok := mock.objects[name]; !ok { return apierrors.NewNotFound(v1.Resource("tests"), name) } @@ -180,7 +181,7 @@ func (mock *MockSecretsInterface) Init(t *testing.T, releases ...*rspb.Release) } // Get returns the Secret by name. -func (mock *MockSecretsInterface) Get(name string, options metav1.GetOptions) (*v1.Secret, error) { +func (mock *MockSecretsInterface) Get(_ context.Context, name string, _ metav1.GetOptions) (*v1.Secret, error) { object, ok := mock.objects[name] if !ok { return nil, apierrors.NewNotFound(v1.Resource("tests"), name) @@ -189,7 +190,7 @@ func (mock *MockSecretsInterface) Get(name string, options metav1.GetOptions) (* } // List returns the a of Secret. -func (mock *MockSecretsInterface) List(opts metav1.ListOptions) (*v1.SecretList, error) { +func (mock *MockSecretsInterface) List(_ context.Context, _ metav1.ListOptions) (*v1.SecretList, error) { var list v1.SecretList for _, secret := range mock.objects { list.Items = append(list.Items, *secret) @@ -198,7 +199,7 @@ func (mock *MockSecretsInterface) List(opts metav1.ListOptions) (*v1.SecretList, } // Create creates a new Secret. -func (mock *MockSecretsInterface) Create(secret *v1.Secret) (*v1.Secret, error) { +func (mock *MockSecretsInterface) Create(_ context.Context, secret *v1.Secret, _ metav1.CreateOptions) (*v1.Secret, error) { name := secret.ObjectMeta.Name if object, ok := mock.objects[name]; ok { return object, apierrors.NewAlreadyExists(v1.Resource("tests"), name) @@ -208,7 +209,7 @@ func (mock *MockSecretsInterface) Create(secret *v1.Secret) (*v1.Secret, error) } // Update updates a Secret. -func (mock *MockSecretsInterface) Update(secret *v1.Secret) (*v1.Secret, error) { +func (mock *MockSecretsInterface) Update(_ context.Context, secret *v1.Secret, _ metav1.UpdateOptions) (*v1.Secret, error) { name := secret.ObjectMeta.Name if _, ok := mock.objects[name]; !ok { return nil, apierrors.NewNotFound(v1.Resource("tests"), name) @@ -218,7 +219,7 @@ func (mock *MockSecretsInterface) Update(secret *v1.Secret) (*v1.Secret, error) } // Delete deletes a Secret by name. -func (mock *MockSecretsInterface) Delete(name string, opts *metav1.DeleteOptions) error { +func (mock *MockSecretsInterface) Delete(_ context.Context, name string, _ metav1.DeleteOptions) error { if _, ok := mock.objects[name]; !ok { return apierrors.NewNotFound(v1.Resource("tests"), name) } diff --git a/pkg/storage/driver/secrets.go b/pkg/storage/driver/secrets.go index 1f1931aed..44280f70f 100644 --- a/pkg/storage/driver/secrets.go +++ b/pkg/storage/driver/secrets.go @@ -17,6 +17,7 @@ limitations under the License. package driver // import "helm.sh/helm/v3/pkg/storage/driver" import ( + "context" "strconv" "strings" "time" @@ -62,7 +63,7 @@ func (secrets *Secrets) Name() string { // or error if not found. func (secrets *Secrets) Get(key string) (*rspb.Release, error) { // fetch the secret holding the release named by key - obj, err := secrets.impl.Get(key, metav1.GetOptions{}) + obj, err := secrets.impl.Get(context.Background(), key, metav1.GetOptions{}) if err != nil { if apierrors.IsNotFound(err) { return nil, ErrReleaseNotFound @@ -81,7 +82,7 @@ func (secrets *Secrets) List(filter func(*rspb.Release) bool) ([]*rspb.Release, lsel := kblabels.Set{"owner": "helm"}.AsSelector() opts := metav1.ListOptions{LabelSelector: lsel.String()} - list, err := secrets.impl.List(opts) + list, err := secrets.impl.List(context.Background(), opts) if err != nil { return nil, errors.Wrap(err, "list: failed to list") } @@ -116,7 +117,7 @@ func (secrets *Secrets) Query(labels map[string]string) ([]*rspb.Release, error) opts := metav1.ListOptions{LabelSelector: ls.AsSelector().String()} - list, err := secrets.impl.List(opts) + list, err := secrets.impl.List(context.Background(), opts) if err != nil { return nil, errors.Wrap(err, "query: failed to query with labels") } @@ -152,7 +153,7 @@ func (secrets *Secrets) Create(key string, rls *rspb.Release) error { return errors.Wrapf(err, "create: failed to encode release %q", rls.Name) } // push the secret object out into the kubiverse - if _, err := secrets.impl.Create(obj); err != nil { + if _, err := secrets.impl.Create(context.Background(), obj, metav1.CreateOptions{}); err != nil { if apierrors.IsAlreadyExists(err) { return ErrReleaseExists } @@ -177,7 +178,7 @@ func (secrets *Secrets) Update(key string, rls *rspb.Release) error { return errors.Wrapf(err, "update: failed to encode release %q", rls.Name) } // push the secret object out into the kubiverse - _, err = secrets.impl.Update(obj) + _, err = secrets.impl.Update(context.Background(), obj, metav1.UpdateOptions{}) return errors.Wrap(err, "update: failed to update") } @@ -188,7 +189,7 @@ func (secrets *Secrets) Delete(key string) (rls *rspb.Release, err error) { return nil, err } // delete the release - err = secrets.impl.Delete(key, &metav1.DeleteOptions{}) + err = secrets.impl.Delete(context.Background(), key, metav1.DeleteOptions{}) return rls, err } From 65f28c153a5eeb77584658ccca875b005a8ddff6 Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Mon, 13 Apr 2020 11:53:43 -0400 Subject: [PATCH 041/281] chore(comp): Remove unnecessary code After the introduction of lazy loading of the Kubernetes client as part of PR #7831, there is no longer a need to protect against an incomplete --kube-context value when doing completion. Signed-off-by: Marc Khouzam --- cmd/helm/helm.go | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index 257387547..7e1fcb6e1 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -32,7 +32,6 @@ import ( // Import to initialize client auth plugins. _ "k8s.io/client-go/plugin/pkg/client/auth" - "helm.sh/helm/v3/internal/completion" "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/cli" "helm.sh/helm/v3/pkg/gates" @@ -73,16 +72,6 @@ func main() { actionConfig := new(action.Configuration) cmd := newRootCmd(actionConfig, os.Stdout, os.Args[1:]) - if calledCmd, _, err := cmd.Find(os.Args[1:]); err == nil && calledCmd.Name() == completion.CompRequestCmd { - // If completion is being called, we have to check if the completion is for the "--kube-context" - // value; if it is, we cannot call the action.Init() method with an incomplete kube-context value - // or else it will fail immediately. So, we simply unset the invalid kube-context value. - if args := os.Args[1:]; len(args) > 2 && args[len(args)-2] == "--kube-context" { - // We are completing the kube-context value! Reset it as the current value is not valid. - settings.KubeContext = "" - } - } - helmDriver := os.Getenv("HELM_DRIVER") if err := actionConfig.Init(settings.RESTClientGetter(), settings.Namespace(), helmDriver, debug); err != nil { log.Fatal(err) From 469837b92cefa0c633397dea1e49606ba0105d56 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Mon, 13 Apr 2020 12:05:33 -0600 Subject: [PATCH 042/281] fixed capitalization in a few help messages. (#7898) Signed-off-by: Matt Butcher --- cmd/helm/completion.go | 2 +- cmd/helm/create.go | 2 +- cmd/helm/env.go | 2 +- cmd/helm/release_testing.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/helm/completion.go b/cmd/helm/completion.go index 1601cb448..c1f7790bc 100644 --- a/cmd/helm/completion.go +++ b/cmd/helm/completion.go @@ -52,7 +52,7 @@ func newCompletionCmd(out io.Writer) *cobra.Command { cmd := &cobra.Command{ Use: "completion SHELL", - Short: "Generate autocompletions script for the specified shell (bash or zsh)", + Short: "generate autocompletions script for the specified shell (bash or zsh)", Long: completionDesc, RunE: func(cmd *cobra.Command, args []string) error { return runCompletion(out, cmd, args) diff --git a/cmd/helm/create.go b/cmd/helm/create.go index e8ff757cf..5bdda05cb 100644 --- a/cmd/helm/create.go +++ b/cmd/helm/create.go @@ -71,7 +71,7 @@ func newCreateCmd(out io.Writer) *cobra.Command { }, } - cmd.Flags().StringVarP(&o.starter, "starter", "p", "", "The name or absolute path to Helm starter scaffold") + cmd.Flags().StringVarP(&o.starter, "starter", "p", "", "the name or absolute path to Helm starter scaffold") return cmd } diff --git a/cmd/helm/env.go b/cmd/helm/env.go index 2687272ba..efb6dfea9 100644 --- a/cmd/helm/env.go +++ b/cmd/helm/env.go @@ -40,7 +40,7 @@ func newEnvCmd(out io.Writer) *cobra.Command { cmd := &cobra.Command{ Use: "env", - Short: "Helm client environment information", + Short: "helm client environment information", Long: envHelp, Args: require.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { diff --git a/cmd/helm/release_testing.go b/cmd/helm/release_testing.go index e4690b9d4..036d96794 100644 --- a/cmd/helm/release_testing.go +++ b/cmd/helm/release_testing.go @@ -82,7 +82,7 @@ func newReleaseTestCmd(cfg *action.Configuration, out io.Writer) *cobra.Command f := cmd.Flags() f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)") - f.BoolVar(&outputLogs, "logs", false, "Dump the logs from test pods (this runs after all tests are complete, but before any cleanup)") + f.BoolVar(&outputLogs, "logs", false, "dump the logs from test pods (this runs after all tests are complete, but before any cleanup)") return cmd } From b9445616b522fe701620b18becdae3a783c054cb Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Mon, 13 Apr 2020 13:57:30 -0500 Subject: [PATCH 043/281] fix(storage): preserve last deployed revision (#7806) Signed-off-by: Eric Bailey --- pkg/storage/storage.go | 40 ++++++++++++++++++++++++----- pkg/storage/storage_test.go | 51 +++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 7 deletions(-) diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go index 89183355f..3e62ae9ee 100644 --- a/pkg/storage/storage.go +++ b/pkg/storage/storage.go @@ -169,21 +169,37 @@ func (s *Storage) removeLeastRecent(name string, max int) error { if len(h) <= max { return nil } - overage := len(h) - max // We want oldest to newest relutil.SortByRevision(h) + lastDeployed, err := s.Deployed(name) + if err != nil { + return err + } + + var toDelete []*rspb.Release + for _, rel := range h { + // once we have enough releases to delete to reach the max, stop + if len(h)-len(toDelete) == max { + break + } + if lastDeployed != nil { + if rel.Version != lastDeployed.Version { + toDelete = append(toDelete, rel) + } + } else { + toDelete = append(toDelete, rel) + } + } + // Delete as many as possible. In the case of API throughput limitations, // multiple invocations of this function will eventually delete them all. - toDelete := h[0:overage] errs := []error{} for _, rel := range toDelete { - key := makeKey(name, rel.Version) - _, innerErr := s.Delete(name, rel.Version) - if innerErr != nil { - s.Log("error pruning %s from release history: %s", key, innerErr) - errs = append(errs, innerErr) + err = s.deleteReleaseVersion(name, rel.Version) + if err != nil { + errs = append(errs, err) } } @@ -198,6 +214,16 @@ func (s *Storage) removeLeastRecent(name string, max int) error { } } +func (s *Storage) deleteReleaseVersion(name string, version int) error { + key := makeKey(name, version) + _, err := s.Delete(name, version) + if err != nil { + s.Log("error pruning %s from release history: %s", key, err) + return err + } + return nil +} + // Last fetches the last revision of the named release. func (s *Storage) Last(name string) (*rspb.Release, error) { s.Log("getting last revision of %q", name) diff --git a/pkg/storage/storage_test.go b/pkg/storage/storage_test.go index ee9c68b80..934a3842c 100644 --- a/pkg/storage/storage_test.go +++ b/pkg/storage/storage_test.go @@ -333,6 +333,57 @@ func TestStorageRemoveLeastRecent(t *testing.T) { } } +func TestStorageDontDeleteDeployed(t *testing.T) { + storage := Init(driver.NewMemory()) + storage.Log = t.Logf + storage.MaxHistory = 3 + + const name = "angry-bird" + + // setup storage with test releases + setup := func() { + // release records + rls0 := ReleaseTestData{Name: name, Version: 1, Status: rspb.StatusSuperseded}.ToRelease() + rls1 := ReleaseTestData{Name: name, Version: 2, Status: rspb.StatusDeployed}.ToRelease() + rls2 := ReleaseTestData{Name: name, Version: 3, Status: rspb.StatusFailed}.ToRelease() + rls3 := ReleaseTestData{Name: name, Version: 4, Status: rspb.StatusFailed}.ToRelease() + + // create the release records in the storage + assertErrNil(t.Fatal, storage.Create(rls0), "Storing release 'angry-bird' (v1)") + assertErrNil(t.Fatal, storage.Create(rls1), "Storing release 'angry-bird' (v2)") + assertErrNil(t.Fatal, storage.Create(rls2), "Storing release 'angry-bird' (v3)") + assertErrNil(t.Fatal, storage.Create(rls3), "Storing release 'angry-bird' (v4)") + } + setup() + + rls5 := ReleaseTestData{Name: name, Version: 5, Status: rspb.StatusFailed}.ToRelease() + assertErrNil(t.Fatal, storage.Create(rls5), "Storing release 'angry-bird' (v5)") + + // On inserting the 5th record, we expect a total of 3 releases, but we expect version 2 + // (the only deployed release), to still exist + hist, err := storage.History(name) + if err != nil { + t.Fatal(err) + } else if len(hist) != storage.MaxHistory { + for _, item := range hist { + t.Logf("%s %v", item.Name, item.Version) + } + t.Fatalf("expected %d items in history, got %d", storage.MaxHistory, len(hist)) + } + + expectedVersions := map[int]bool{ + 2: true, + 4: true, + 5: true, + } + + for _, item := range hist { + if !expectedVersions[item.Version] { + t.Errorf("Release version %d, found when not expected", item.Version) + } + } +} + func TestStorageLast(t *testing.T) { storage := Init(driver.NewMemory()) From a806326d18e008740a522440ac02856add556e33 Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Sat, 11 Apr 2020 13:07:42 -0400 Subject: [PATCH 044/281] feat(cmd/helm): Update Cobra to 1.0.0 release Signed-off-by: Marc Khouzam --- go.mod | 2 +- go.sum | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 3413112cd..72cc667cc 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/opencontainers/image-spec v1.0.1 github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.4.2 - github.com/spf13/cobra v0.0.5 + github.com/spf13/cobra v1.0.0 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.4.0 github.com/xeipuuv/gojsonschema v1.1.0 diff --git a/go.sum b/go.sum index 3c08dba4b..872673ab2 100644 --- a/go.sum +++ b/go.sum @@ -39,6 +39,7 @@ github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tT github.com/Microsoft/hcsshim v0.8.7 h1:ptnOoufxGSzauVTsdE+wMYnCWA301PdoN4xg5oRdZpg= github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= @@ -78,6 +79,7 @@ github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0Bsq github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= @@ -94,6 +96,7 @@ github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= @@ -103,8 +106,11 @@ github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= @@ -119,6 +125,7 @@ github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8l github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/docker/cli v0.0.0-20200130152716-5d0cf8839492 h1:FwssHbCDJD025h+BchanCwE1Q8fyMgqDr2mOQAWOLGw= github.com/docker/cli v0.0.0-20200130152716-5d0cf8839492/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= @@ -170,6 +177,7 @@ github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0 github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= @@ -233,6 +241,8 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekf github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903 h1:LbsanbbD6LieFkXbj9YNNBupiGHJgFeLpO0j0Fza1h8= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -278,8 +288,10 @@ github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= @@ -365,6 +377,7 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -403,6 +416,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= @@ -413,19 +427,27 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1: github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8= github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= @@ -434,6 +456,7 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= @@ -442,6 +465,8 @@ github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKv github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= +github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -450,6 +475,7 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= @@ -461,6 +487,8 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= @@ -481,6 +509,7 @@ github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMzt github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= @@ -490,6 +519,7 @@ go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -528,6 +558,7 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -616,6 +647,7 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98 google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= From 0c87ca544f6fc760065d590bed43f25040e276ea Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Mon, 13 Apr 2020 18:03:53 -0600 Subject: [PATCH 045/281] updated help text for install --atomic, which was completely inaccurate (#7912) Signed-off-by: Matt Butcher --- 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 719dc9014..40535e4e3 100644 --- a/cmd/helm/install.go +++ b/cmd/helm/install.go @@ -148,7 +148,7 @@ func addInstallFlags(f *pflag.FlagSet, client *action.Install, valueOpts *values f.BoolVar(&client.Devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored") f.BoolVar(&client.DependencyUpdate, "dependency-update", false, "run helm dependency update before installing the chart") f.BoolVar(&client.DisableOpenAPIValidation, "disable-openapi-validation", false, "if set, the installation process will not validate rendered templates against the Kubernetes OpenAPI Schema") - f.BoolVar(&client.Atomic, "atomic", false, "if set, installation process purges chart on fail. The --wait flag will be set automatically if --atomic is used") + f.BoolVar(&client.Atomic, "atomic", false, "if set, the installation process deletes the installation on failure. The --wait flag will be set automatically if --atomic is used") f.BoolVar(&client.SkipCRDs, "skip-crds", false, "if set, no CRDs will be installed. By default, CRDs are installed if not already present") f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent") addValueOptionsFlags(f, valueOpts) From 8c55de381869005e61cbc769bce1660404741987 Mon Sep 17 00:00:00 2001 From: Zhou Hao Date: Thu, 2 Apr 2020 13:49:20 +0800 Subject: [PATCH 046/281] add unit test for IsRoot Signed-off-by: Zhou Hao --- pkg/chart/chart_test.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/pkg/chart/chart_test.go b/pkg/chart/chart_test.go index 4f19bf87d..51dc8cb4f 100644 --- a/pkg/chart/chart_test.go +++ b/pkg/chart/chart_test.go @@ -96,3 +96,24 @@ func TestMetadata(t *testing.T) { is.Equal("1.0.0", chrt.AppVersion()) is.Equal(nil, chrt.Validate()) } + +func TestIsRoot(t *testing.T) { + chrt1 := Chart{ + parent: &Chart{ + Metadata: &Metadata{ + Name: "foo", + }, + }, + } + + chrt2 := Chart{ + Metadata: &Metadata{ + Name: "foo", + }, + } + + is := assert.New(t) + + is.Equal(false, chrt1.IsRoot()) + is.Equal(true, chrt2.IsRoot()) +} From a3d3fa396423b82c14fa3c3619eaf03e79ad8884 Mon Sep 17 00:00:00 2001 From: Zhou Hao Date: Thu, 2 Apr 2020 13:58:58 +0800 Subject: [PATCH 047/281] add unit test for ChartPath Signed-off-by: Zhou Hao --- pkg/chart/chart_test.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/pkg/chart/chart_test.go b/pkg/chart/chart_test.go index 51dc8cb4f..6f352adda 100644 --- a/pkg/chart/chart_test.go +++ b/pkg/chart/chart_test.go @@ -117,3 +117,24 @@ func TestIsRoot(t *testing.T) { is.Equal(false, chrt1.IsRoot()) is.Equal(true, chrt2.IsRoot()) } + +func TestChartPath(t *testing.T) { + chrt1 := Chart{ + parent: &Chart{ + Metadata: &Metadata{ + Name: "foo", + }, + }, + } + + chrt2 := Chart{ + Metadata: &Metadata{ + Name: "foo", + }, + } + + is := assert.New(t) + + is.Equal("foo.", chrt1.ChartPath()) + is.Equal("foo", chrt2.ChartPath()) +} From b439d34a43b886ee4f2272e922e03b05ee5e898d Mon Sep 17 00:00:00 2001 From: Zhou Hao Date: Thu, 2 Apr 2020 13:59:37 +0800 Subject: [PATCH 048/281] add unit test for ChartFullPath Signed-off-by: Zhou Hao --- pkg/chart/chart_test.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/pkg/chart/chart_test.go b/pkg/chart/chart_test.go index 6f352adda..1b8669ac8 100644 --- a/pkg/chart/chart_test.go +++ b/pkg/chart/chart_test.go @@ -138,3 +138,24 @@ func TestChartPath(t *testing.T) { is.Equal("foo.", chrt1.ChartPath()) is.Equal("foo", chrt2.ChartPath()) } + +func TestChartFullPath(t *testing.T) { + chrt1 := Chart{ + parent: &Chart{ + Metadata: &Metadata{ + Name: "foo", + }, + }, + } + + chrt2 := Chart{ + Metadata: &Metadata{ + Name: "foo", + }, + } + + is := assert.New(t) + + is.Equal("foo/charts/", chrt1.ChartFullPath()) + is.Equal("foo", chrt2.ChartFullPath()) +} From 20c7909756f03e5c7a266f9bef294cfd9cf64088 Mon Sep 17 00:00:00 2001 From: Zhou Hao Date: Thu, 2 Apr 2020 14:44:00 +0800 Subject: [PATCH 049/281] add unit test for metadata Validate Signed-off-by: Zhou Hao --- pkg/chart/metadata_test.go | 59 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 pkg/chart/metadata_test.go diff --git a/pkg/chart/metadata_test.go b/pkg/chart/metadata_test.go new file mode 100644 index 000000000..8b436000b --- /dev/null +++ b/pkg/chart/metadata_test.go @@ -0,0 +1,59 @@ +/* +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 chart + +import ( + "testing" +) + +func TestValidate(t *testing.T) { + tests := []struct { + md *Metadata + err error + }{ + { + nil, + ValidationError("chart.metadata is required"), + }, + { + &Metadata{Name: "test", Version: "1.0"}, + ValidationError("chart.metadata.apiVersion is required"), + }, + { + &Metadata{APIVersion: "v2", Version: "1.0"}, + ValidationError("chart.metadata.name is required"), + }, + { + &Metadata{Name: "test", APIVersion: "v2"}, + ValidationError("chart.metadata.version is required"), + }, + { + &Metadata{Name: "test", APIVersion: "v2", Version: "1.0", Type: "test"}, + ValidationError("chart.metadata.type must be application or library"), + }, + { + &Metadata{Name: "test", APIVersion: "v2", Version: "1.0", Type: "application"}, + nil, + }, + } + + for _, tt := range tests { + result := tt.md.Validate() + if result != tt.err { + t.Errorf("expected %s, got %s", tt.err, result) + } + } +} From ad3ba46de1fa5dcba2a90003bf8f368ab47c06df Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Tue, 14 Apr 2020 19:55:50 -0600 Subject: [PATCH 050/281] docs: Update inline docs on action/upgrade.go (#7834) * docs: Update inline docs on action/upgrade.go Signed-off-by: Matt Butcher * clarify atomic and cleanup-on-fail Signed-off-by: Matt Butcher * updated the post-render documentation on action.Upgrade Signed-off-by: Matt Butcher --- pkg/action/upgrade.go | 62 ++++++++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 16 deletions(-) diff --git a/pkg/action/upgrade.go b/pkg/action/upgrade.go index b73e2ac8b..9d05217e9 100644 --- a/pkg/action/upgrade.go +++ b/pkg/action/upgrade.go @@ -43,27 +43,57 @@ type Upgrade struct { ChartPathOptions - Install bool - Devel bool + // Install is a purely informative flag that indicates whether this upgrade was done in "install" mode. + // + // Applications may use this to determine whether this Upgrade operation was done as part of a + // pure upgrade (Upgrade.Install == false) or as part of an install-or-upgrade operation + // (Upgrade.Install == true). + // + // Setting this to `true` will NOT cause `Upgrade` to perform an install if the release does not exist. + // That process must be handled by creating an Install action directly. See cmd/upgrade.go for an + // example of how this flag is used. + Install bool + // Devel indicates that the operation is done in devel mode. + Devel bool + // Namespace is the namespace in which this operation should be performed. Namespace string - // SkipCRDs skip installing CRDs when install flag is enabled during upgrade - SkipCRDs bool - Timeout time.Duration - Wait bool + // SkipCRDs skips installing CRDs when install flag is enabled during upgrade + SkipCRDs bool + // Timeout is the timeout for this operation + Timeout time.Duration + // Wait determines whether the wait operation should be performed after the upgrade is requested. + Wait bool + // DisableHooks disables hook processing if set to true. DisableHooks bool - DryRun bool - Force bool - ResetValues bool - ReuseValues bool + // DryRun controls whether the operation is prepared, but not executed. + // If `true`, the upgrade is prepared but not performed. + DryRun bool + // Force will, if set to `true`, ignore certain warnings and perform the upgrade anyway. + // + // This should be used with caution. + Force bool + // ResetValues will reset the values to the chart's built-ins rather than merging with existing. + ResetValues bool + // ReuseValues will re-use the user's last supplied values. + ReuseValues bool // Recreate will (if true) recreate pods after a rollback. Recreate bool // MaxHistory limits the maximum number of revisions saved per release - MaxHistory int - Atomic bool - CleanupOnFail bool - SubNotes bool - Description string - PostRenderer postrender.PostRenderer + MaxHistory int + // Atomic, if true, will roll back on failure. + Atomic bool + // CleanupOnFail will, if true, cause the upgrade to delete newly-created resources on a failed update. + CleanupOnFail bool + // SubNotes determines whether sub-notes are rendered in the chart. + SubNotes bool + // Description is the description of this operation + Description string + // PostRender is an optional post-renderer + // + // If this is non-nil, then after templates are rendered, they will be sent to the + // post renderer before sending to the Kuberntes API server. + PostRenderer postrender.PostRenderer + // DisableOpenAPIValidation controls whether OpenAPI validation is enforced. DisableOpenAPIValidation bool } From 9e1f381bf2b0c3880cdf4bad3a0dbc21026b54c6 Mon Sep 17 00:00:00 2001 From: Lu Fengqi Date: Wed, 15 Apr 2020 10:07:02 +0800 Subject: [PATCH 051/281] Add unit test for Secrets/ConfigMaps (#7765) * test(pkg/storage/secrets): make MockSecretsInterface.List follow ListOptions Signed-off-by: Lu Fengqi * test(pkg/storage/secrets): add unit test for Secrets.Query Signed-off-by: Lu Fengqi * test(pkg/storage/cfgmaps): make MockConfigMapsInterface.List follow ListOptions Signed-off-by: Lu Fengqi * test(pkg/storage/cfgmaps): add unit test for ConfigMaps.Query Signed-off-by: Lu Fengqi --- pkg/storage/driver/cfgmaps_test.go | 24 ++++++++++++++++++++++++ pkg/storage/driver/mock_test.go | 21 +++++++++++++++++++-- pkg/storage/driver/secrets_test.go | 24 ++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 2 deletions(-) diff --git a/pkg/storage/driver/cfgmaps_test.go b/pkg/storage/driver/cfgmaps_test.go index e40247d3c..626c36cb9 100644 --- a/pkg/storage/driver/cfgmaps_test.go +++ b/pkg/storage/driver/cfgmaps_test.go @@ -130,6 +130,30 @@ func TestConfigMapList(t *testing.T) { } } +func TestConfigMapQuery(t *testing.T) { + cfgmaps := newTestFixtureCfgMaps(t, []*rspb.Release{ + releaseStub("key-1", 1, "default", rspb.StatusUninstalled), + releaseStub("key-2", 1, "default", rspb.StatusUninstalled), + releaseStub("key-3", 1, "default", rspb.StatusDeployed), + releaseStub("key-4", 1, "default", rspb.StatusDeployed), + releaseStub("key-5", 1, "default", rspb.StatusSuperseded), + releaseStub("key-6", 1, "default", rspb.StatusSuperseded), + }...) + + rls, err := cfgmaps.Query(map[string]string{"status": "deployed"}) + if err != nil { + t.Errorf("Failed to query: %s", err) + } + if len(rls) != 2 { + t.Errorf("Expected 2 results, got %d", len(rls)) + } + + _, err = cfgmaps.Query(map[string]string{"name": "notExist"}) + if err != ErrReleaseNotFound { + t.Errorf("Expected {%v}, got {%v}", ErrReleaseNotFound, err) + } +} + func TestConfigMapCreate(t *testing.T) { cfgmaps := newTestFixtureCfgMaps(t) diff --git a/pkg/storage/driver/mock_test.go b/pkg/storage/driver/mock_test.go index 22e6454a1..0ef498c4e 100644 --- a/pkg/storage/driver/mock_test.go +++ b/pkg/storage/driver/mock_test.go @@ -24,6 +24,7 @@ import ( v1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + kblabels "k8s.io/apimachinery/pkg/labels" corev1 "k8s.io/client-go/kubernetes/typed/core/v1" rspb "helm.sh/helm/v3/pkg/release" @@ -114,8 +115,16 @@ func (mock *MockConfigMapsInterface) Get(_ context.Context, name string, _ metav // List returns the a of ConfigMaps. func (mock *MockConfigMapsInterface) List(_ context.Context, _ metav1.ListOptions) (*v1.ConfigMapList, error) { var list v1.ConfigMapList + + labelSelector, err := kblabels.Parse(opts.LabelSelector) + if err != nil { + return nil, err + } + for _, cfgmap := range mock.objects { - list.Items = append(list.Items, *cfgmap) + if labelSelector.Matches(kblabels.Set(cfgmap.ObjectMeta.Labels)) { + list.Items = append(list.Items, *cfgmap) + } } return &list, nil } @@ -192,8 +201,16 @@ func (mock *MockSecretsInterface) Get(_ context.Context, name string, _ metav1.G // List returns the a of Secret. func (mock *MockSecretsInterface) List(_ context.Context, _ metav1.ListOptions) (*v1.SecretList, error) { var list v1.SecretList + + labelSelector, err := kblabels.Parse(opts.LabelSelector) + if err != nil { + return nil, err + } + for _, secret := range mock.objects { - list.Items = append(list.Items, *secret) + if labelSelector.Matches(kblabels.Set(secret.ObjectMeta.Labels)) { + list.Items = append(list.Items, *secret) + } } return &list, nil } diff --git a/pkg/storage/driver/secrets_test.go b/pkg/storage/driver/secrets_test.go index 5f0ecc8bb..d509c7b3a 100644 --- a/pkg/storage/driver/secrets_test.go +++ b/pkg/storage/driver/secrets_test.go @@ -130,6 +130,30 @@ func TestSecretList(t *testing.T) { } } +func TestSecretQuery(t *testing.T) { + secrets := newTestFixtureSecrets(t, []*rspb.Release{ + releaseStub("key-1", 1, "default", rspb.StatusUninstalled), + releaseStub("key-2", 1, "default", rspb.StatusUninstalled), + releaseStub("key-3", 1, "default", rspb.StatusDeployed), + releaseStub("key-4", 1, "default", rspb.StatusDeployed), + releaseStub("key-5", 1, "default", rspb.StatusSuperseded), + releaseStub("key-6", 1, "default", rspb.StatusSuperseded), + }...) + + rls, err := secrets.Query(map[string]string{"status": "deployed"}) + if err != nil { + t.Fatalf("Failed to query: %s", err) + } + if len(rls) != 2 { + t.Fatalf("Expected 2 results, actual %d", len(rls)) + } + + _, err = secrets.Query(map[string]string{"name": "notExist"}) + if err != ErrReleaseNotFound { + t.Errorf("Expected {%v}, got {%v}", ErrReleaseNotFound, err) + } +} + func TestSecretCreate(t *testing.T) { secrets := newTestFixtureSecrets(t) From e1d046bc43ad1ec53c4c0d38aa7723c82e2ff9d2 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Tue, 14 Apr 2020 21:34:08 -0700 Subject: [PATCH 052/281] fix(tests): fix broken unit tests in storage (#7928) --- pkg/storage/driver/mock_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/storage/driver/mock_test.go b/pkg/storage/driver/mock_test.go index 0ef498c4e..77ddca43d 100644 --- a/pkg/storage/driver/mock_test.go +++ b/pkg/storage/driver/mock_test.go @@ -113,7 +113,7 @@ func (mock *MockConfigMapsInterface) Get(_ context.Context, name string, _ metav } // List returns the a of ConfigMaps. -func (mock *MockConfigMapsInterface) List(_ context.Context, _ metav1.ListOptions) (*v1.ConfigMapList, error) { +func (mock *MockConfigMapsInterface) List(_ context.Context, opts metav1.ListOptions) (*v1.ConfigMapList, error) { var list v1.ConfigMapList labelSelector, err := kblabels.Parse(opts.LabelSelector) @@ -199,7 +199,7 @@ func (mock *MockSecretsInterface) Get(_ context.Context, name string, _ metav1.G } // List returns the a of Secret. -func (mock *MockSecretsInterface) List(_ context.Context, _ metav1.ListOptions) (*v1.SecretList, error) { +func (mock *MockSecretsInterface) List(_ context.Context, opts metav1.ListOptions) (*v1.SecretList, error) { var list v1.SecretList labelSelector, err := kblabels.Parse(opts.LabelSelector) From bdf6f48704ed9e09d7fa636f025a3e2d344d42d4 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Tue, 14 Apr 2020 22:27:53 -0700 Subject: [PATCH 053/281] fix(pkg/kube): continue deleting objects when one fails * Continue deleting objects when one fails to minimize the risk of an upgrade ending in an unrecoverable state * Exclude failed deleted object from the returned result set Signed-off-by: Adam Reese --- pkg/kube/client.go | 12 ++++-------- pkg/kube/client_test.go | 17 ++++++++++++++--- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/pkg/kube/client.go b/pkg/kube/client.go index 05b26b12a..8a4831ffb 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -223,6 +223,7 @@ func (c *Client) Update(original, target ResourceList, force bool) (*Result, err if err := info.Get(); err != nil { c.Log("Unable to get obj %q, err: %s", info.Name, err) + continue } annotations, err := metadataAccessor.Annotations(info.Object) if err != nil { @@ -232,16 +233,11 @@ func (c *Client) Update(original, target ResourceList, force bool) (*Result, err c.Log("Skipping delete of %q due to annotation [%s=%s]", info.Name, ResourcePolicyAnno, KeepPolicy) continue } - - res.Deleted = append(res.Deleted, info) if err := deleteResource(info); err != nil { - if apierrors.IsNotFound(err) { - c.Log("Attempted to delete %q, but the resource was missing", info.Name) - } else { - c.Log("Failed to delete %q, err: %s", info.Name, err) - return res, errors.Wrapf(err, "Failed to delete %q", info.Name) - } + c.Log("Failed to delete %q, err: %s", info.ObjectName(), err) + continue } + res.Deleted = append(res.Deleted, info) } return res, nil } diff --git a/pkg/kube/client_test.go b/pkg/kube/client_test.go index aa081423c..568afa094 100644 --- a/pkg/kube/client_test.go +++ b/pkg/kube/client_test.go @@ -164,9 +164,21 @@ func TestUpdate(t *testing.T) { t.Fatal(err) } - if _, err := c.Update(first, second, false); err != nil { + result, err := c.Update(first, second, false) + if err != nil { t.Fatal(err) } + + if len(result.Created) != 1 { + t.Errorf("expected 1 resource created, got %d", len(result.Created)) + } + if len(result.Updated) != 2 { + t.Errorf("expected 2 resource updated, got %d", len(result.Updated)) + } + if len(result.Deleted) != 1 { + t.Errorf("expected 1 resource deleted, got %d", len(result.Deleted)) + } + // TODO: Find a way to test methods that use Client Set // Test with a wait // if err := c.Update("test", objBody(codec, &listB), objBody(codec, &listC), false, 300, true); err != nil { @@ -190,8 +202,7 @@ func TestUpdate(t *testing.T) { "/namespaces/default/pods/squid:DELETE", } if len(expectedActions) != len(actions) { - t.Errorf("unexpected number of requests, expected %d, got %d", len(expectedActions), len(actions)) - return + t.Fatalf("unexpected number of requests, expected %d, got %d", len(expectedActions), len(actions)) } for k, v := range expectedActions { if actions[k] != v { From 0b1cba8474eeb704230fe86f70a7f0b5ac6bd440 Mon Sep 17 00:00:00 2001 From: Martin Hickey Date: Tue, 14 Apr 2020 22:30:41 +0000 Subject: [PATCH 054/281] Add an improved user error message for removed k8s apis The error message returned from Kubernetes when APIs are removed is not very informative. This PR adds additional information to the user. It covers the current release manifest APIs. Partial #7219 Signed-off-by: Martin Hickey --- pkg/action/upgrade.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pkg/action/upgrade.go b/pkg/action/upgrade.go index 9d05217e9..7565da5a9 100644 --- a/pkg/action/upgrade.go +++ b/pkg/action/upgrade.go @@ -233,6 +233,13 @@ func (u *Upgrade) prepareUpgrade(name string, chart *chart.Chart, vals map[strin func (u *Upgrade) performUpgrade(originalRelease, upgradedRelease *release.Release) (*release.Release, error) { current, err := u.cfg.KubeClient.Build(bytes.NewBufferString(originalRelease.Manifest), false) if err != nil { + // Checking for removed Kubernetes API error so can provide a more informative error message to the user + // Ref: https://github.com/helm/helm/issues/7219 + if strings.Contains(err.Error(), "unable to recognize \"\": no matches for kind") { + return upgradedRelease, errors.Wrap(err, "current release manifest contains removed kubernetes api(s) for this "+ + "kubernetes version and it is therefore unable to build the kubernetes "+ + "objects for performing the diff. error from kubernetes") + } return upgradedRelease, errors.Wrap(err, "unable to build kubernetes objects from current release manifest") } target, err := u.cfg.KubeClient.Build(bytes.NewBufferString(upgradedRelease.Manifest), !u.DisableOpenAPIValidation) From 549193dbcb04c7883fef59e844f981d32a92c887 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Wed, 15 Apr 2020 11:48:26 -0600 Subject: [PATCH 055/281] test: forward-port regression test from Helm 2 (#7927) Signed-off-by: Matt Butcher --- pkg/lint/lint_test.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/pkg/lint/lint_test.go b/pkg/lint/lint_test.go index b51939d76..2c110009d 100644 --- a/pkg/lint/lint_test.go +++ b/pkg/lint/lint_test.go @@ -17,9 +17,12 @@ limitations under the License. package lint import ( + "io/ioutil" + "os" "strings" "testing" + "helm.sh/helm/v3/pkg/chartutil" "helm.sh/helm/v3/pkg/lint/support" ) @@ -104,3 +107,30 @@ func TestGoodChart(t *testing.T) { t.Errorf("All failed but shouldn't have: %#v", m) } } + +// TestHelmCreateChart tests that a `helm create` always passes a `helm lint` test. +// +// See https://github.com/helm/helm/issues/7923 +func TestHelmCreateChart(t *testing.T) { + dir, err := ioutil.TempDir("", "-helm-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + createdChart, err := chartutil.Create("testhelmcreatepasseslint", dir) + if err != nil { + t.Error(err) + // Fatal is bad because of the defer. + return + } + + // Note: we test with strict=true here, even though others have + // strict = false. + m := All(createdChart, values, namespace, true).Messages + if ll := len(m); ll != 1 { + t.Errorf("All should have had exactly 1 error. Got %d", ll) + } else if msg := m[0].Err.Error(); !strings.Contains(msg, "icon is recommended") { + t.Errorf("Unexpected lint error: %s", msg) + } +} From 48e6ea0caec94682d80f20bbd66658392b27edd3 Mon Sep 17 00:00:00 2001 From: Riccardo Piccoli Date: Wed, 15 Apr 2020 19:50:18 +0200 Subject: [PATCH 056/281] add softonic to adopters (#7918) Signed-off-by: Riccardo Piccoli Co-authored-by: Riccardo Piccoli --- ADOPTERS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ADOPTERS.md b/ADOPTERS.md index 46b42b8a0..9d5365b72 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -10,6 +10,7 @@ - [Microsoft](https://microsoft.com) - [Qovery](https://www.qovery.com/) - [Samsung SDS](https://www.samsungsds.com/) +- [Softonic](https://hello.softonic.com/) - [Ville de Montreal](https://montreal.ca) _This file is part of the CNCF official documentation for projects._ From 4276acdf4b22c993ac6d1b1aeb1c4d2246ff7309 Mon Sep 17 00:00:00 2001 From: Scott Rigby Date: Wed, 15 Apr 2020 18:10:16 -0400 Subject: [PATCH 057/281] Make get script eaiser for helm versions to live side by side (helm3 etc) (#7752) * Make get script eaiser for helm versions to live side by side (helm3 etc) Signed-off-by: Scott Rigby * Change PROJECT_NAME to BINARY_NAME for purpose clarity Signed-off-by: Scott Rigby --- scripts/get-helm-3 | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/scripts/get-helm-3 b/scripts/get-helm-3 index a974d97b6..adadf5953 100755 --- a/scripts/get-helm-3 +++ b/scripts/get-helm-3 @@ -17,8 +17,7 @@ # 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" - +: ${BINARY_NAME:="helm"} : ${USE_SUDO:="true"} : ${HELM_INSTALL_DIR:="/usr/local/bin"} @@ -92,8 +91,8 @@ checkDesiredVersion() { # 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 --template="{{ .Version }}") + if [[ -f "${HELM_INSTALL_DIR}/${BINARY_NAME}" ]]; then + local version=$("${HELM_INSTALL_DIR}/${BINARY_NAME}" version --template="{{ .Version }}") if [[ "$version" == "$TAG" ]]; then echo "Helm ${version} is already ${DESIRED_VERSION:-latest}" return 0 @@ -131,7 +130,7 @@ downloadFile() { # installFile verifies the SHA256 for the file, then unpacks and # installs it. installFile() { - HELM_TMP="$HELM_TMP_ROOT/$PROJECT_NAME" + HELM_TMP="$HELM_TMP_ROOT/$BINARY_NAME" local sum=$(openssl sha1 -sha256 ${HELM_TMP_FILE} | awk '{print $2}') local expected_sum=$(cat ${HELM_SUM_FILE}) if [ "$sum" != "$expected_sum" ]; then @@ -141,10 +140,10 @@ installFile() { mkdir -p "$HELM_TMP" tar xf "$HELM_TMP_FILE" -C "$HELM_TMP" - HELM_TMP_BIN="$HELM_TMP/$OS-$ARCH/$PROJECT_NAME" - 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" + HELM_TMP_BIN="$HELM_TMP/$OS-$ARCH/helm" + echo "Preparing to install $BINARY_NAME into ${HELM_INSTALL_DIR}" + runAsRoot cp "$HELM_TMP_BIN" "$HELM_INSTALL_DIR/$BINARY_NAME" + echo "$BINARY_NAME installed into $HELM_INSTALL_DIR/$BINARY_NAME" } # fail_trap is executed if an error occurs. @@ -152,10 +151,10 @@ fail_trap() { result=$? if [ "$result" != "0" ]; then if [[ -n "$INPUT_ARGUMENTS" ]]; then - echo "Failed to install $PROJECT_NAME with the arguments provided: $INPUT_ARGUMENTS" + echo "Failed to install $BINARY_NAME with the arguments provided: $INPUT_ARGUMENTS" help else - echo "Failed to install $PROJECT_NAME" + echo "Failed to install $BINARY_NAME" fi echo -e "\tFor support, go to https://github.com/helm/helm." fi @@ -166,9 +165,9 @@ fail_trap() { # testVersion tests the installed client to make sure it is working. testVersion() { set +e - HELM="$(which $PROJECT_NAME)" + HELM="$(which $BINARY_NAME)" if [ "$?" = "1" ]; then - echo "$PROJECT_NAME not found. Is $HELM_INSTALL_DIR on your "'$PATH?' + echo "$BINARY_NAME not found. Is $HELM_INSTALL_DIR on your "'$PATH?' exit 1 fi set -e From fa5eb64f32c99a2771acf326c4b0e01481d23066 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Wed, 15 Apr 2020 16:17:57 -0600 Subject: [PATCH 058/281] fix: rebuild chart after dependency update on install (#7897) * fix: rebuild chart after dependency update on install Signed-off-by: Matt Butcher * add correct debug settings Signed-off-by: Matt Butcher --- cmd/helm/install.go | 5 +++++ cmd/helm/install_test.go | 6 ++++++ cmd/helm/testdata/output/chart-with-subchart-update.txt | 8 ++++++++ .../testcharts/chart-with-subchart-update/Chart.yaml | 8 ++++++++ .../charts/subchart-with-notes/Chart.yaml | 4 ++++ .../charts/subchart-with-notes/templates/NOTES.txt | 1 + .../chart-with-subchart-update/templates/NOTES.txt | 1 + 7 files changed, 33 insertions(+) create mode 100644 cmd/helm/testdata/output/chart-with-subchart-update.txt create mode 100644 cmd/helm/testdata/testcharts/chart-with-subchart-update/Chart.yaml create mode 100644 cmd/helm/testdata/testcharts/chart-with-subchart-update/charts/subchart-with-notes/Chart.yaml create mode 100644 cmd/helm/testdata/testcharts/chart-with-subchart-update/charts/subchart-with-notes/templates/NOTES.txt create mode 100644 cmd/helm/testdata/testcharts/chart-with-subchart-update/templates/NOTES.txt diff --git a/cmd/helm/install.go b/cmd/helm/install.go index 40535e4e3..21a41b9f9 100644 --- a/cmd/helm/install.go +++ b/cmd/helm/install.go @@ -210,10 +210,15 @@ func runInstall(args []string, client *action.Install, valueOpts *values.Options Getters: p, RepositoryConfig: settings.RepositoryConfig, RepositoryCache: settings.RepositoryCache, + Debug: settings.Debug, } if err := man.Update(); err != nil { return nil, err } + // Reload the chart with the updated Chart.lock file. + if chartRequested, err = loader.Load(cp); err != nil { + return nil, errors.Wrap(err, "failed reloading chart after repo update") + } } else { return nil, err } diff --git a/cmd/helm/install_test.go b/cmd/helm/install_test.go index 57972024f..e3013d713 100644 --- a/cmd/helm/install_test.go +++ b/cmd/helm/install_test.go @@ -111,6 +111,12 @@ func TestInstall(t *testing.T) { cmd: "install nodeps testdata/testcharts/chart-missing-deps", wantError: true, }, + // Install chart with update-dependency + { + name: "install chart with missing dependencies", + cmd: "install --dependency-update updeps testdata/testcharts/chart-with-subchart-update", + golden: "output/chart-with-subchart-update.txt", + }, // Install, chart with bad dependencies in Chart.yaml in /charts { name: "install chart with bad dependencies in Chart.yaml", diff --git a/cmd/helm/testdata/output/chart-with-subchart-update.txt b/cmd/helm/testdata/output/chart-with-subchart-update.txt new file mode 100644 index 000000000..a4135c782 --- /dev/null +++ b/cmd/helm/testdata/output/chart-with-subchart-update.txt @@ -0,0 +1,8 @@ +NAME: updeps +LAST DEPLOYED: Fri Sep 2 22:04:05 1977 +NAMESPACE: default +STATUS: deployed +REVISION: 1 +TEST SUITE: None +NOTES: +PARENT NOTES diff --git a/cmd/helm/testdata/testcharts/chart-with-subchart-update/Chart.yaml b/cmd/helm/testdata/testcharts/chart-with-subchart-update/Chart.yaml new file mode 100644 index 000000000..1bc230200 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-subchart-update/Chart.yaml @@ -0,0 +1,8 @@ +apiVersion: v2 +description: Chart with subchart that needs to be fetched +name: chart-with-subchart-update +version: 0.0.1 +dependencies: + - name: subchart-with-notes + version: 0.0.1 + repository: file://../chart-with-subchart-notes/charts diff --git a/cmd/helm/testdata/testcharts/chart-with-subchart-update/charts/subchart-with-notes/Chart.yaml b/cmd/helm/testdata/testcharts/chart-with-subchart-update/charts/subchart-with-notes/Chart.yaml new file mode 100644 index 000000000..f0fead9ee --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-subchart-update/charts/subchart-with-notes/Chart.yaml @@ -0,0 +1,4 @@ +apiVersion: v2 +description: Subchart with notes +name: subchart-with-notes +version: 0.0.1 diff --git a/cmd/helm/testdata/testcharts/chart-with-subchart-update/charts/subchart-with-notes/templates/NOTES.txt b/cmd/helm/testdata/testcharts/chart-with-subchart-update/charts/subchart-with-notes/templates/NOTES.txt new file mode 100644 index 000000000..1f61a294e --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-subchart-update/charts/subchart-with-notes/templates/NOTES.txt @@ -0,0 +1 @@ +SUBCHART NOTES diff --git a/cmd/helm/testdata/testcharts/chart-with-subchart-update/templates/NOTES.txt b/cmd/helm/testdata/testcharts/chart-with-subchart-update/templates/NOTES.txt new file mode 100644 index 000000000..9e166d370 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-subchart-update/templates/NOTES.txt @@ -0,0 +1 @@ +PARENT NOTES From a1685b737dd6846bdc35c281bf841b9cc43cb104 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Thu, 16 Apr 2020 10:11:07 +0800 Subject: [PATCH 059/281] Merge remote-tracking branch 'helm/master' Signed-off-by: Liu Ming --- pkg/action/rollback.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/action/rollback.go b/pkg/action/rollback.go index f717610c8..81812983f 100644 --- a/pkg/action/rollback.go +++ b/pkg/action/rollback.go @@ -210,10 +210,7 @@ func (r *Rollback) performRollback(currentRelease, targetRelease *release.Releas } } - targetRelease.Info.Status = release.StatusDeployed - deployed, err := r.cfg.Releases.DeployedAll(currentRelease.Name) - if err != nil && !strings.Contains(err.Error(), "has no deployed releases") { return nil, err } @@ -224,5 +221,7 @@ func (r *Rollback) performRollback(currentRelease, targetRelease *release.Releas r.cfg.recordRelease(rel) } + targetRelease.Info.Status = release.StatusDeployed + return targetRelease, nil } From df9cf87cbe1164d40845e664a4d193188c77aee9 Mon Sep 17 00:00:00 2001 From: ZouYu Date: Thu, 16 Apr 2020 14:12:40 +0800 Subject: [PATCH 060/281] add unit test for function FindPlugins Signed-off-by: ZouYu --- pkg/plugin/plugin_test.go | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/pkg/plugin/plugin_test.go b/pkg/plugin/plugin_test.go index c869e4c86..7bbc3b442 100644 --- a/pkg/plugin/plugin_test.go +++ b/pkg/plugin/plugin_test.go @@ -264,6 +264,43 @@ func TestLoadAll(t *testing.T) { } } +func TestFindPlugins(t *testing.T) { + cases := []struct { + name string + plugdirs string + expected int + }{ + { + name: "plugdirs is empty", + plugdirs: "", + expected: 0, + }, + { + name: "plugdirs isn't dir", + plugdirs: "./plugin_test.go", + expected: 0, + }, + { + name: "plugdirs doens't have plugin", + plugdirs: ".", + expected: 0, + }, + { + name: "normal", + plugdirs: "./testdata/plugdir", + expected: 3, + }, + } + for _, c := range cases { + t.Run(t.Name(), func(t *testing.T) { + plugin, _ := FindPlugins(c.plugdirs) + if len(plugin) != c.expected { + t.Errorf("expected: %v, got: %v", c.expected, len(plugin)) + } + }) + } +} + func TestSetupEnv(t *testing.T) { name := "pequod" base := filepath.Join("testdata/helmhome/helm/plugins", name) From 3b8521c1f0555f1c56799117ea638fd3e6663336 Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Thu, 16 Apr 2020 14:12:30 -0400 Subject: [PATCH 061/281] Updating sprig and semver to newer versions Note, there is an issue with a dependency of sprig changing behavior. A test has been added with a description to catch if a behavior breaking change of mergo is used. See https://github.com/imdario/mergo/issues/139 for the mergo issue and sprig for further details on handling this in the future. Closes #7533 Signed-off-by: Matt Farina --- go.mod | 8 ++--- go.sum | 16 ++++++++++ pkg/engine/funcs_test.go | 68 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 72cc667cc..3d6977a5c 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,8 @@ go 1.13 require ( github.com/BurntSushi/toml v0.3.1 - github.com/Masterminds/semver/v3 v3.0.3 - github.com/Masterminds/sprig/v3 v3.0.2 + github.com/Masterminds/semver/v3 v3.1.0 + github.com/Masterminds/sprig/v3 v3.1.0 github.com/Masterminds/vcs v1.13.1 github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496 github.com/containerd/containerd v1.3.2 @@ -26,9 +26,9 @@ require ( github.com/sirupsen/logrus v1.4.2 github.com/spf13/cobra v1.0.0 github.com/spf13/pflag v1.0.5 - github.com/stretchr/testify v1.4.0 + github.com/stretchr/testify v1.5.1 github.com/xeipuuv/gojsonschema v1.1.0 - golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 + golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 k8s.io/api v0.18.0 k8s.io/apiextensions-apiserver v0.18.0 k8s.io/apimachinery v0.18.0 diff --git a/go.sum b/go.sum index 872673ab2..c5ed4ef82 100644 --- a/go.sum +++ b/go.sum @@ -30,8 +30,12 @@ github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RP github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver/v3 v3.0.3 h1:znjIyLfpXEDQjOIEWh+ehwpTU14UzUPub3c3sm36u14= github.com/Masterminds/semver/v3 v3.0.3/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/Masterminds/semver/v3 v3.1.0 h1:Y2lUDsFKVRSYGojLJ1yLxSXdMmMYTYls0rCvoqmMUQk= +github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/sprig/v3 v3.0.2 h1:wz22D0CiSctrliXiI9ZO3HoNApweeRGftyDN+BQa3B8= github.com/Masterminds/sprig/v3 v3.0.2/go.mod h1:oesJ8kPONMONaZgtiHNzUShJbksypC5kWczhZAf6+aU= +github.com/Masterminds/sprig/v3 v3.1.0 h1:j7GpgZ7PdFqNsmncycTHsLmVPf5/3wJtlgW9TNDYD9Y= +github.com/Masterminds/sprig/v3 v3.1.0/go.mod h1:ONGMf7UfYGAbMXCZmQLy8x3lCDIPrEZE/rU8pmrbihA= github.com/Masterminds/vcs v1.13.1 h1:NL3G1X7/7xduQtA2sJLpVpfHTNBALVNSjob6KEjPXNQ= github.com/Masterminds/vcs v1.13.1/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5 h1:ygIc8M6trr62pF5DucadTWGdEB4mEyvzi0e2nbcmcyA= @@ -151,6 +155,7 @@ github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:Htrtb github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e h1:p1yVGRW3nmb85p1Sh1ZJSDm4A4iKLS5QNbvUHMgGu/M= github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= @@ -303,9 +308,13 @@ github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0= github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= +github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs= +github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI= github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= +github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= @@ -383,6 +392,7 @@ github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= @@ -461,6 +471,8 @@ github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= @@ -484,6 +496,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -536,6 +550,8 @@ golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d h1:9FCpayM9Egr1baVnV1SX0H golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo= golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 h1:bXoxMPcSLOq08zI3/c5dEBT6lE4eh+jOh886GHrn6V8= +golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= diff --git a/pkg/engine/funcs_test.go b/pkg/engine/funcs_test.go index a405c1c47..ddcf6624c 100644 --- a/pkg/engine/funcs_test.go +++ b/pkg/engine/funcs_test.go @@ -103,3 +103,71 @@ func TestFuncs(t *testing.T) { assert.Equal(t, tt.expect, b.String(), tt.tpl) } } + +// This test to check a function provided by sprig is due to a change in a +// dependency of sprig. mergo in v0.3.9 changed the way it merges and only does +// public fields (i.e. those starting with a capital letter). This test, from +// sprig, fails in the new version. This is a behavior change for mergo that +// impacts sprig and Helm users. This test will help us to not update to a +// version of mergo (even accidentally) that causes a breaking change. See +// sprig changelog and notes for more details. +// Note, Go modules assume semver is never broken. So, there is no way to tell +// the tooling to not update to a minor or patch version. `go get -u` could be +// used to accidentally update mergo. This test and message should catch the +// problem and explain why it's happening. +func TestMerge(t *testing.T) { + dict := map[string]interface{}{ + "src2": map[string]interface{}{ + "h": 10, + "i": "i", + "j": "j", + }, + "src1": map[string]interface{}{ + "a": 1, + "b": 2, + "d": map[string]interface{}{ + "e": "four", + }, + "g": []int{6, 7}, + "i": "aye", + "j": "jay", + "k": map[string]interface{}{ + "l": false, + }, + }, + "dst": map[string]interface{}{ + "a": "one", + "c": 3, + "d": map[string]interface{}{ + "f": 5, + }, + "g": []int{8, 9}, + "i": "eye", + "k": map[string]interface{}{ + "l": true, + }, + }, + } + tpl := `{{merge .dst .src1 .src2}}` + var b strings.Builder + err := template.Must(template.New("test").Funcs(funcMap()).Parse(tpl)).Execute(&b, dict) + assert.NoError(t, err) + + expected := map[string]interface{}{ + "a": "one", // key overridden + "b": 2, // merged from src1 + "c": 3, // merged from dst + "d": map[string]interface{}{ // deep merge + "e": "four", + "f": 5, + }, + "g": []int{8, 9}, // overridden - arrays are not merged + "h": 10, // merged from src2 + "i": "eye", // overridden twice + "j": "jay", // overridden and merged + "k": map[string]interface{}{ + "l": true, // overridden + }, + } + assert.Equal(t, expected, dict["dst"]) +} From a34f3115395474fbf3b8a167ef3473eb8b0952e9 Mon Sep 17 00:00:00 2001 From: uzxmx Date: Fri, 17 Apr 2020 03:53:39 +0800 Subject: [PATCH 062/281] Fix nested null value overrides (#7743) * Fix nested null value overrides Signed-off-by: Mingxiang Xue * Fix subchart value deletion Signed-off-by: Mingxiang Xue --- pkg/chartutil/coalesce.go | 24 +++++------ pkg/chartutil/coalesce_test.go | 40 +++++++++++++++++-- .../charts/pequod/charts/ahab/values.yaml | 4 ++ pkg/chartutil/testdata/moby/values.yaml | 2 + 4 files changed, 52 insertions(+), 18 deletions(-) diff --git a/pkg/chartutil/coalesce.go b/pkg/chartutil/coalesce.go index bbdd5f21c..a5ff66aed 100644 --- a/pkg/chartutil/coalesce.go +++ b/pkg/chartutil/coalesce.go @@ -47,10 +47,7 @@ func CoalesceValues(chrt *chart.Chart, vals map[string]interface{}) (Values, err if valsCopy == nil { valsCopy = make(map[string]interface{}) } - if _, err := coalesce(chrt, valsCopy); err != nil { - return valsCopy, err - } - return coalesceDeps(chrt, valsCopy) + return coalesce(chrt, valsCopy) } // coalesce coalesces the dest values and the chart values, giving priority to the dest values. @@ -186,19 +183,18 @@ func CoalesceTables(dst, src map[string]interface{}) map[string]interface{} { // Because dest has higher precedence than src, dest values override src // values. for key, val := range src { - if istable(val) { - switch innerdst, ok := dst[key]; { - case !ok: - dst[key] = val - case istable(innerdst): - CoalesceTables(innerdst.(map[string]interface{}), val.(map[string]interface{})) - default: + if dv, ok := dst[key]; ok && dv == nil { + delete(dst, key) + } else if !ok { + dst[key] = val + } else if istable(val) { + if istable(dv) { + CoalesceTables(dv.(map[string]interface{}), val.(map[string]interface{})) + } else { log.Printf("warning: cannot overwrite table with non table for %s (%v)", key, val) } - } else if dv, ok := dst[key]; ok && istable(dv) { + } else if istable(dv) { log.Printf("warning: destination for %s is a table. Ignoring non-table value %v", key, val) - } else if !ok { // <- ok is still in scope from preceding conditional. - dst[key] = val } } return dst diff --git a/pkg/chartutil/coalesce_test.go b/pkg/chartutil/coalesce_test.go index 6e82de590..dc1017385 100644 --- a/pkg/chartutil/coalesce_test.go +++ b/pkg/chartutil/coalesce_test.go @@ -31,6 +31,8 @@ right: Null left: NULL front: ~ back: "" +nested: + boat: null global: name: Ishmael @@ -47,6 +49,10 @@ pequod: sail: true ahab: scope: whale + boat: null + nested: + foo: true + bar: null `) func TestCoalesceValues(t *testing.T) { @@ -86,6 +92,7 @@ func TestCoalesceValues(t *testing.T) { {"{{.pequod.name}}", "pequod"}, {"{{.pequod.ahab.name}}", "ahab"}, {"{{.pequod.ahab.scope}}", "whale"}, + {"{{.pequod.ahab.nested.foo}}", "true"}, {"{{.pequod.ahab.global.name}}", "Ishmael"}, {"{{.pequod.ahab.global.subject}}", "Queequeg"}, {"{{.pequod.ahab.global.harpooner}}", "Tashtego"}, @@ -114,6 +121,19 @@ func TestCoalesceValues(t *testing.T) { } } + if _, ok := v["nested"].(map[string]interface{})["boat"]; ok { + t.Error("Expected nested boat key to be removed, still present") + } + + subchart := v["pequod"].(map[string]interface{})["ahab"].(map[string]interface{}) + if _, ok := subchart["boat"]; ok { + t.Error("Expected subchart boat key to be removed, still present") + } + + if _, ok := subchart["nested"].(map[string]interface{})["bar"]; ok { + t.Error("Expected subchart nested bar key to be removed, still present") + } + // CoalesceValues should not mutate the passed arguments is.Equal(valsCopy, vals) } @@ -122,24 +142,28 @@ func TestCoalesceTables(t *testing.T) { dst := map[string]interface{}{ "name": "Ishmael", "address": map[string]interface{}{ - "street": "123 Spouter Inn Ct.", - "city": "Nantucket", + "street": "123 Spouter Inn Ct.", + "city": "Nantucket", + "country": nil, }, "details": map[string]interface{}{ "friends": []string{"Tashtego"}, }, "boat": "pequod", + "hole": nil, } src := map[string]interface{}{ "occupation": "whaler", "address": map[string]interface{}{ - "state": "MA", - "street": "234 Spouter Inn Ct.", + "state": "MA", + "street": "234 Spouter Inn Ct.", + "country": "US", }, "details": "empty", "boat": map[string]interface{}{ "mast": true, }, + "hole": "black", } // What we expect is that anything in dst overrides anything in src, but that @@ -170,6 +194,10 @@ func TestCoalesceTables(t *testing.T) { t.Errorf("Unexpected state: %v", addr["state"]) } + if _, ok = addr["country"]; ok { + t.Error("The country is not left out.") + } + if det, ok := dst["details"].(map[string]interface{}); !ok { t.Fatalf("Details is the wrong type: %v", dst["details"]) } else if _, ok := det["friends"]; !ok { @@ -179,4 +207,8 @@ func TestCoalesceTables(t *testing.T) { if dst["boat"].(string) != "pequod" { t.Errorf("Expected boat string, got %v", dst["boat"]) } + + if _, ok = dst["hole"]; ok { + t.Error("The hole still exists.") + } } diff --git a/pkg/chartutil/testdata/moby/charts/pequod/charts/ahab/values.yaml b/pkg/chartutil/testdata/moby/charts/pequod/charts/ahab/values.yaml index 86c3f63aa..eee6980fa 100644 --- a/pkg/chartutil/testdata/moby/charts/pequod/charts/ahab/values.yaml +++ b/pkg/chartutil/testdata/moby/charts/pequod/charts/ahab/values.yaml @@ -1,2 +1,6 @@ scope: ahab name: ahab +boat: true +nested: + foo: false + bar: true diff --git a/pkg/chartutil/testdata/moby/values.yaml b/pkg/chartutil/testdata/moby/values.yaml index 54e1ce463..2169d7566 100644 --- a/pkg/chartutil/testdata/moby/values.yaml +++ b/pkg/chartutil/testdata/moby/values.yaml @@ -7,3 +7,5 @@ right: exists left: exists front: exists back: exists +nested: + boat: true From 7b89e66e0c350c7d61443ed6de647298fc9e9a2b Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Thu, 16 Apr 2020 14:31:45 -0600 Subject: [PATCH 063/281] fix: Fixed a regression that was introduced with changed nil handling (#7938) Signed-off-by: Matt Butcher --- pkg/chartutil/create.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/chartutil/create.go b/pkg/chartutil/create.go index a02cf4b98..28fb28e00 100644 --- a/pkg/chartutil/create.go +++ b/pkg/chartutil/create.go @@ -113,7 +113,7 @@ serviceAccount: annotations: {} # The name of the service account to use. # If not set and create is true, a name is generated using the fullname template - name: + name: "" podAnnotations: {} From 21d2aa7f2b28e0ecec0cd17825a422a692c71752 Mon Sep 17 00:00:00 2001 From: Elliot Maincourt Date: Thu, 16 Apr 2020 22:53:40 +0200 Subject: [PATCH 064/281] Migrate SQL storage driver to Helm 3 (#7635) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Migrate SQL storage driver to Helm 3 Signed-off-by: Elliot Maincourt * Update pkg/storage/driver/sql.go Co-Authored-By: Sebastian Pöhn Signed-off-by: Elliot Maincourt * Add authentication to releases_v3 Signed-off-by: Elliot Maincourt * Fix migration Signed-off-by: Elliot Maincourt * Template the init migration Signed-off-by: Elliot Maincourt * Prevent potential SQL injection Signed-off-by: Elliot Maincourt * Use an SQL querybuilder Signed-off-by: Elliot Maincourt * Remove references to HELM_DRIVER_SQL_DIALECT Signed-off-by: Elliot Maincourt Co-authored-by: Sebastian Pöhn Co-authored-by: Matt Butcher --- cmd/helm/root.go | 21 +- go.mod | 5 + go.sum | 122 ++++---- pkg/action/action.go | 12 + pkg/storage/driver/mock_test.go | 20 ++ pkg/storage/driver/sql.go | 492 ++++++++++++++++++++++++++++++++ pkg/storage/driver/sql_test.go | 442 ++++++++++++++++++++++++++++ 7 files changed, 1048 insertions(+), 66 deletions(-) create mode 100644 pkg/storage/driver/sql.go create mode 100644 pkg/storage/driver/sql_test.go diff --git a/cmd/helm/root.go b/cmd/helm/root.go index 3e3dfa012..2c66d3a09 100644 --- a/cmd/helm/root.go +++ b/cmd/helm/root.go @@ -43,16 +43,17 @@ Common actions for Helm: Environment variables: -+------------------+-----------------------------------------------------------------------------+ -| Name | Description | -+------------------+-----------------------------------------------------------------------------+ -| $XDG_CACHE_HOME | set an alternative location for storing cached files. | -| $XDG_CONFIG_HOME | set an alternative location for storing Helm configuration. | -| $XDG_DATA_HOME | set an alternative location for storing Helm data. | -| $HELM_DRIVER | set the backend storage driver. Values are: configmap, secret, memory | -| $HELM_NO_PLUGINS | disable plugins. Set HELM_NO_PLUGINS=1 to disable plugins. | -| $KUBECONFIG | set an alternative Kubernetes configuration file (default "~/.kube/config") | -+------------------+-----------------------------------------------------------------------------+ ++------------------+--------------------------------------------------------------------------------------------------------+ +| Name | Description | ++------------------+--------------------------------------------------------------------------------------------------------+ +| $XDG_CACHE_HOME | set an alternative location for storing cached files. | +| $XDG_CONFIG_HOME | set an alternative location for storing Helm configuration. | +| $XDG_DATA_HOME | set an alternative location for storing Helm data. | +| $HELM_DRIVER | set the backend storage driver. Values are: configmap, secret, memory, postgres | +| $HELM_DRIVER_SQL_CONNECTION_STRING | set the connection string the SQL storage driver should use. | +| $HELM_NO_PLUGINS | disable plugins. Set HELM_NO_PLUGINS=1 to disable plugins. | +| $KUBECONFIG | set an alternative Kubernetes configuration file (default "~/.kube/config") | ++------------------+--------------------------------------------------------------------------------------------------------+ Helm stores configuration based on the XDG base directory specification, so diff --git a/go.mod b/go.mod index 3d6977a5c..64ebfe307 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,10 @@ go 1.13 require ( github.com/BurntSushi/toml v0.3.1 + github.com/DATA-DOG/go-sqlmock v1.4.1 github.com/Masterminds/semver/v3 v3.1.0 github.com/Masterminds/sprig/v3 v3.1.0 + github.com/Masterminds/squirrel v1.2.0 github.com/Masterminds/vcs v1.13.1 github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496 github.com/containerd/containerd v1.3.2 @@ -18,11 +20,14 @@ require ( github.com/gobwas/glob v0.2.3 github.com/gofrs/flock v0.7.1 github.com/gosuri/uitable v0.0.4 + github.com/jmoiron/sqlx v1.2.0 + github.com/lib/pq v1.3.0 github.com/mattn/go-shellwords v1.0.10 github.com/mitchellh/copystructure v1.0.0 github.com/opencontainers/go-digest v1.0.0-rc1 github.com/opencontainers/image-spec v1.0.1 github.com/pkg/errors v0.9.1 + github.com/rubenv/sql-migrate v0.0.0-20200212082348-64f95ea68aa3 github.com/sirupsen/logrus v1.4.2 github.com/spf13/cobra v1.0.0 github.com/spf13/pflag v1.0.5 diff --git a/go.sum b/go.sum index c5ed4ef82..0a51a72e8 100644 --- a/go.sum +++ b/go.sum @@ -23,7 +23,8 @@ github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VY github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DATA-DOG/go-sqlmock v1.4.1 h1:ThlnYciV1iM/V0OSF/dtkqWb6xo5qITT1TJBG1MRDJM= +github.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd h1:sjQovDkwrZp8u+gxLtPgKGjk5hCxuy2hrRejBTA9xFU= github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= @@ -32,10 +33,10 @@ github.com/Masterminds/semver/v3 v3.0.3 h1:znjIyLfpXEDQjOIEWh+ehwpTU14UzUPub3c3s github.com/Masterminds/semver/v3 v3.0.3/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/semver/v3 v3.1.0 h1:Y2lUDsFKVRSYGojLJ1yLxSXdMmMYTYls0rCvoqmMUQk= github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/Masterminds/sprig/v3 v3.0.2 h1:wz22D0CiSctrliXiI9ZO3HoNApweeRGftyDN+BQa3B8= -github.com/Masterminds/sprig/v3 v3.0.2/go.mod h1:oesJ8kPONMONaZgtiHNzUShJbksypC5kWczhZAf6+aU= github.com/Masterminds/sprig/v3 v3.1.0 h1:j7GpgZ7PdFqNsmncycTHsLmVPf5/3wJtlgW9TNDYD9Y= github.com/Masterminds/sprig/v3 v3.1.0/go.mod h1:ONGMf7UfYGAbMXCZmQLy8x3lCDIPrEZE/rU8pmrbihA= +github.com/Masterminds/squirrel v1.2.0 h1:K1NhbTO21BWG47IVR0OnIZuE0LZcXAYqywrC3Ko53KI= +github.com/Masterminds/squirrel v1.2.0/go.mod h1:yaPeOnPG5ZRwL9oKdTsO/prlkPbXWZlRVMQ/gGlzIuA= github.com/Masterminds/vcs v1.13.1 h1:NL3G1X7/7xduQtA2sJLpVpfHTNBALVNSjob6KEjPXNQ= github.com/Masterminds/vcs v1.13.1/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5 h1:ygIc8M6trr62pF5DucadTWGdEB4mEyvzi0e2nbcmcyA= @@ -58,6 +59,7 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496 h1:zV3ejI06GQ59hwDQAvmK1qxOQGB3WuVTRoY0okPTAv0= @@ -118,13 +120,13 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsr github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= -github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE= github.com/deislabs/oras v0.8.1 h1:If674KraJVpujYR00rzdi0QAmW4BxzMJPVAZJKuhQ0c= github.com/deislabs/oras v0.8.1/go.mod h1:Mx0rMSbBNaNfY9hjpccEnxkOqJL6KGjtxNHPLC4G4As= +github.com/denisenkom/go-mssqldb v0.0.0-20191001013358-cfbb681360f0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= @@ -231,7 +233,19 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= +github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/envy v1.7.1 h1:OQl5ys5MBea7OGCdvPbBJWRgnhC/fGona6QKfvFeau8= +github.com/gobuffalo/envy v1.7.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w= +github.com/gobuffalo/logger v1.0.1 h1:ZEgyRGgAm4ZAhAO45YXMs5Fp+bzGLESFewzAVBMKuTg= +github.com/gobuffalo/logger v1.0.1/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs= +github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4= +github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q= +github.com/gobuffalo/packr/v2 v2.7.1 h1:n3CIW5T17T8v4GGK5sWXLVWJhCz7b5aNLSxW6gYim4o= +github.com/gobuffalo/packr/v2 v2.7.1/go.mod h1:qYEvAazPaVxy7Y7KR0W8qYEE+RymX74kETFqjFoFlOc= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= @@ -239,9 +253,9 @@ github.com/gofrs/flock v0.7.1 h1:DP+LD/t0njgoPBvT5MJLeliUIVQR03hiKR6vezdwHlc= github.com/gofrs/flock v0.7.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903 h1:LbsanbbD6LieFkXbj9YNNBupiGHJgFeLpO0j0Fza1h8= @@ -265,7 +279,6 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= @@ -299,7 +312,9 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -319,8 +334,11 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= +github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= +github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok= @@ -331,8 +349,9 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -340,6 +359,14 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= +github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= +github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= +github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU= +github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= @@ -354,14 +381,20 @@ github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7 github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-runewidth v0.0.2 h1:UnlwIPBGaTZfPQ6T1IGzPI0EkYAQmT9fAEJ/poFC63o= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= +github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-shellwords v1.0.10 h1:Y7Xqm8piKOO3v10Thp7Z36h4FYFjt5xB//6XvOrs2Gw= github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= +github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v1.12.0 h1:u/x3mp++qUxvYfulZ4HKOvVO0JWhk7HtE8lWhbGz/Do= +github.com/mattn/go-sqlite3 v1.12.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -375,7 +408,6 @@ github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= @@ -388,6 +420,8 @@ github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+ github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/olekukonko/tablewriter v0.0.2/go.mod h1:rSAaSIOAGT9odnlyGlUfAJaoc5w2fSBUmeGDbRWPxyQ= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo= @@ -420,9 +454,9 @@ github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -448,8 +482,14 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8= github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.4.0 h1:LUa41nrWTQNGhzdsZ5lTnkwbNjj6rXTdazA1cSdjkOY= +github.com/rogpeppe/go-internal v1.4.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rubenv/sql-migrate v0.0.0-20200212082348-64f95ea68aa3 h1:xkBtI5JktwbW/vf4vopBbhYsRFTGfQWHYXzC0/qYwxI= +github.com/rubenv/sql-migrate v0.0.0-20200212082348-64f95ea68aa3/go.mod h1:rtQlpHw+eR6UrqaS3kX1VYeaCxzCVdimDS7g5Ln4pPc= github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= @@ -491,7 +531,6 @@ github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/y github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= @@ -523,6 +562,8 @@ github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMzt github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= +github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= +github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= @@ -542,9 +583,10 @@ golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d h1:9FCpayM9Egr1baVnV1SX0H87m+XB0B8S0hAMi99X/3U= golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -553,14 +595,10 @@ golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 h1:bXoxMPcSLOq08zI3/c5dEBT6lE4eh+jOh886GHrn6V8= golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -575,6 +613,7 @@ golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -594,6 +633,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -602,12 +642,12 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -630,34 +670,30 @@ golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= @@ -676,9 +712,12 @@ gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/gorp.v1 v1.7.2 h1:j3DWlAyGVv8whO7AcIWznQ2Yj7yJkn34B8s63GViAAw= +gopkg.in/gorp.v1 v1.7.2/go.mod h1:Wo3h+DBQZIxATwftsglhdD/62zRFPhGhTiu5jUJmCaw= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= @@ -689,8 +728,8 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= @@ -698,66 +737,37 @@ gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -k8s.io/api v0.17.3 h1:XAm3PZp3wnEdzekNkcmj/9Y1zdmQYJ1I4GKSBBZ8aG0= -k8s.io/api v0.17.3/go.mod h1:YZ0OTkuw7ipbe305fMpIdf3GLXZKRigjtZaV5gzC2J0= k8s.io/api v0.18.0 h1:lwYk8Vt7rsVTwjRU6pzEsa9YNhThbmbocQlKvNBB4EQ= k8s.io/api v0.18.0/go.mod h1:q2HRQkfDzHMBZL9l/y9rH63PkQl4vae0xRT+8prbrK8= -k8s.io/apiextensions-apiserver v0.17.3 h1:WDZWkPcbgvchEdDd7ysL21GGPx3UKZQLDZXEkevT6n4= -k8s.io/apiextensions-apiserver v0.17.3/go.mod h1:CJbCyMfkKftAd/X/V6OTHYhVn7zXnDdnkUjS1h0GTeY= k8s.io/apiextensions-apiserver v0.18.0 h1:HN4/P8vpGZFvB5SOMuPPH2Wt9Y/ryX+KRvIyAkchu1Q= k8s.io/apiextensions-apiserver v0.18.0/go.mod h1:18Cwn1Xws4xnWQNC00FLq1E350b9lUF+aOdIWDOZxgo= -k8s.io/apimachinery v0.17.3 h1:f+uZV6rm4/tHE7xXgLyToprg6xWairaClGVkm2t8omg= -k8s.io/apimachinery v0.17.3/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g= k8s.io/apimachinery v0.18.0 h1:fuPfYpk3cs1Okp/515pAf0dNhL66+8zk8RLbSX+EgAE= k8s.io/apimachinery v0.18.0/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= -k8s.io/apiserver v0.17.3/go.mod h1:iJtsPpu1ZpEnHaNawpSV0nYTGBhhX2dUlnn7/QS7QiY= k8s.io/apiserver v0.18.0/go.mod h1:3S2O6FeBBd6XTo0njUrLxiqk8GNy6wWOftjhJcXYnjw= -k8s.io/cli-runtime v0.17.3 h1:0ZlDdJgJBKsu77trRUynNiWsRuAvAVPBNaQfnt/1qtc= -k8s.io/cli-runtime v0.17.3/go.mod h1:X7idckYphH4SZflgNpOOViSxetiMj6xI0viMAjM81TA= k8s.io/cli-runtime v0.18.0 h1:jG8XpSqQ5TrV0N+EZ3PFz6+gqlCk71dkggWCCq9Mq34= k8s.io/cli-runtime v0.18.0/go.mod h1:1eXfmBsIJosjn9LjEBUd2WVPoPAY9XGTqTFcPMIBsUQ= -k8s.io/client-go v0.17.3 h1:deUna1Ksx05XeESH6XGCyONNFfiQmDdqeqUvicvP6nU= -k8s.io/client-go v0.17.3/go.mod h1:cLXlTMtWHkuK4tD360KpWz2gG2KtdWEr/OT02i3emRQ= k8s.io/client-go v0.18.0 h1:yqKw4cTUQraZK3fcVCMeSa+lqKwcjZ5wtcOIPnxQno4= k8s.io/client-go v0.18.0/go.mod h1:uQSYDYs4WhVZ9i6AIoEZuwUggLVEF64HOD37boKAtF8= -k8s.io/code-generator v0.17.3/go.mod h1:l8BLVwASXQZTo2xamW5mQNFCe1XPiAesVq7Y1t7PiQQ= k8s.io/code-generator v0.18.0/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc= -k8s.io/component-base v0.17.3 h1:hQzTSshY14aLSR6WGIYvmw+w+u6V4d+iDR2iDGMrlUg= -k8s.io/component-base v0.17.3/go.mod h1:GeQf4BrgelWm64PXkIXiPh/XS0hnO42d9gx9BtbZRp8= k8s.io/component-base v0.18.0 h1:I+lP0fNfsEdTDpHaL61bCAqTZLoiWjEEP304Mo5ZQgE= k8s.io/component-base v0.18.0/go.mod h1:u3BCg0z1uskkzrnAKFzulmYaEpZF7XC9Pf/uFyb1v2c= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= -k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLyy7rfbeuf1PYyBf973pgyU= -k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c h1:/KUFqjjqAcY4Us6luF5RDNZ16KJtb49HfR3ZHB9qYXM= k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= -k8s.io/kubectl v0.17.3 h1:9HHYj07kuFkM+sMJMOyQX29CKWq4lvKAG1UIPxNPMQ4= -k8s.io/kubectl v0.17.3/go.mod h1:NUn4IBY7f7yCMwSop2HCXlw/MVYP4HJBiUmOR3n9w28= k8s.io/kubectl v0.18.0 h1:hu52Ndq/d099YW+3sS3VARxFz61Wheiq8K9S7oa82Dk= k8s.io/kubectl v0.18.0/go.mod h1:LOkWx9Z5DXMEg5KtOjHhRiC1fqJPLyCr3KtQgEolCkU= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= -k8s.io/metrics v0.17.3/go.mod h1:HEJGy1fhHOjHggW9rMDBJBD3YuGroH3Y1pnIRw9FFaI= k8s.io/metrics v0.18.0/go.mod h1:8aYTW18koXqjLVKL7Ds05RPMX9ipJZI3mywYvBOxXd4= -k8s.io/utils v0.0.0-20191114184206-e782cd3c129f h1:GiPwtSzdP43eI1hpPCbROQCCIgCuiMMNF8YUVLF3vJo= -k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89 h1:d4vVOjXm687F1iLSP2q3lyPPuyvTUt3aVoBpi2DqRsU= k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= -modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= -modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= -modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= -modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= -modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0= sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0= sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= -sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= -sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18= sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= sigs.k8s.io/structured-merge-diff/v3 v3.0.0 h1:dOmIZBMfhcHS09XZkMyUgkq5trg3/jRyJYFZUiaOp8E= sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= diff --git a/pkg/action/action.go b/pkg/action/action.go index 05a133abf..5da901635 100644 --- a/pkg/action/action.go +++ b/pkg/action/action.go @@ -17,6 +17,8 @@ limitations under the License. package action import ( + "fmt" + "os" "path" "regexp" @@ -255,6 +257,16 @@ func (c *Configuration) Init(getter genericclioptions.RESTClientGetter, namespac } d.SetNamespace(namespace) store = storage.Init(d) + case "sql": + d, err := driver.NewSQL( + os.Getenv("HELM_DRIVER_SQL_CONNECTION_STRING"), + log, + namespace, + ) + if err != nil { + panic(fmt.Sprintf("Unable to instantiate SQL driver: %v", err)) + } + store = storage.Init(d) default: // Not sure what to do here. panic("Unknown driver in HELM_DRIVER: " + helmDriver) diff --git a/pkg/storage/driver/mock_test.go b/pkg/storage/driver/mock_test.go index 77ddca43d..c0236ece8 100644 --- a/pkg/storage/driver/mock_test.go +++ b/pkg/storage/driver/mock_test.go @@ -21,6 +21,10 @@ import ( "fmt" "testing" + sqlmock "github.com/DATA-DOG/go-sqlmock" + sq "github.com/Masterminds/squirrel" + "github.com/jmoiron/sqlx" + v1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -243,3 +247,19 @@ func (mock *MockSecretsInterface) Delete(_ context.Context, name string, _ metav delete(mock.objects, name) return nil } + +// newTestFixtureSQL mocks the SQL database (for testing purposes) +func newTestFixtureSQL(t *testing.T, releases ...*rspb.Release) (*SQL, sqlmock.Sqlmock) { + sqlDB, mock, err := sqlmock.New() + if err != nil { + t.Fatalf("error when opening stub database connection: %v", err) + } + + sqlxDB := sqlx.NewDb(sqlDB, "sqlmock") + return &SQL{ + db: sqlxDB, + Log: func(a string, b ...interface{}) {}, + namespace: "default", + statementBuilder: sq.StatementBuilder.PlaceholderFormat(sq.Dollar), + }, mock +} diff --git a/pkg/storage/driver/sql.go b/pkg/storage/driver/sql.go new file mode 100644 index 000000000..f68f50f54 --- /dev/null +++ b/pkg/storage/driver/sql.go @@ -0,0 +1,492 @@ +/* +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 driver // import "helm.sh/helm/v3/pkg/storage/driver" + +import ( + "fmt" + "sort" + "time" + + "github.com/jmoiron/sqlx" + migrate "github.com/rubenv/sql-migrate" + + sq "github.com/Masterminds/squirrel" + + // Import pq for postgres dialect + _ "github.com/lib/pq" + + rspb "helm.sh/helm/v3/pkg/release" +) + +var _ Driver = (*SQL)(nil) + +var labelMap = map[string]struct{}{ + "modifiedAt": {}, + "createdAt": {}, + "version": {}, + "status": {}, + "owner": {}, + "name": {}, +} + +const postgreSQLDialect = "postgres" + +// SQLDriverName is the string name of this driver. +const SQLDriverName = "SQL" + +const sqlReleaseTableName = "releases_v1" + +const ( + sqlReleaseTableKeyColumn = "key" + sqlReleaseTableTypeColumn = "type" + sqlReleaseTableBodyColumn = "body" + sqlReleaseTableNameColumn = "name" + sqlReleaseTableNamespaceColumn = "namespace" + sqlReleaseTableVersionColumn = "version" + sqlReleaseTableStatusColumn = "status" + sqlReleaseTableOwnerColumn = "owner" + sqlReleaseTableCreatedAtColumn = "createdAt" + sqlReleaseTableModifiedAtColumn = "modifiedAt" +) + +const ( + sqlReleaseDefaultOwner = "helm" + sqlReleaseDefaultType = "helm.sh/release.v1" +) + +// SQL is the sql storage driver implementation. +type SQL struct { + db *sqlx.DB + namespace string + statementBuilder sq.StatementBuilderType + + Log func(string, ...interface{}) +} + +// Name returns the name of the driver. +func (s *SQL) Name() string { + return SQLDriverName +} + +func (s *SQL) ensureDBSetup() error { + // Populate the database with the relations we need if they don't exist yet + migrations := &migrate.MemoryMigrationSource{ + Migrations: []*migrate.Migration{ + { + Id: "init", + Up: []string{ + fmt.Sprintf(` + CREATE TABLE %s ( + %s VARCHAR(67), + %s VARCHAR(64) NOT NULL, + %s TEXT NOT NULL, + %s VARCHAR(64) NOT NULL, + %s VARCHAR(64) NOT NULL, + %s INTEGER NOT NULL, + %s TEXT NOT NULL, + %s TEXT NOT NULL, + %s INTEGER NOT NULL, + %s INTEGER NOT NULL DEFAULT 0, + PRIMARY KEY(%s, %s) + ); + CREATE INDEX ON %s (%s, %s); + CREATE INDEX ON %s (%s); + CREATE INDEX ON %s (%s); + CREATE INDEX ON %s (%s); + CREATE INDEX ON %s (%s); + CREATE INDEX ON %s (%s); + + GRANT ALL ON %s TO PUBLIC; + + ALTER TABLE %s ENABLE ROW LEVEL SECURITY; + `, + sqlReleaseTableName, + sqlReleaseTableKeyColumn, + sqlReleaseTableTypeColumn, + sqlReleaseTableBodyColumn, + sqlReleaseTableNameColumn, + sqlReleaseTableNamespaceColumn, + sqlReleaseTableVersionColumn, + sqlReleaseTableStatusColumn, + sqlReleaseTableOwnerColumn, + sqlReleaseTableCreatedAtColumn, + sqlReleaseTableModifiedAtColumn, + sqlReleaseTableKeyColumn, + sqlReleaseTableNamespaceColumn, + sqlReleaseTableName, + sqlReleaseTableKeyColumn, + sqlReleaseTableNamespaceColumn, + sqlReleaseTableName, + sqlReleaseTableVersionColumn, + sqlReleaseTableName, + sqlReleaseTableStatusColumn, + sqlReleaseTableName, + sqlReleaseTableOwnerColumn, + sqlReleaseTableName, + sqlReleaseTableCreatedAtColumn, + sqlReleaseTableName, + sqlReleaseTableModifiedAtColumn, + sqlReleaseTableName, + sqlReleaseTableName, + ), + }, + Down: []string{ + fmt.Sprintf(` + DROP TABLE %s; + `, sqlReleaseTableName), + }, + }, + }, + } + + _, err := migrate.Exec(s.db.DB, postgreSQLDialect, migrations, migrate.Up) + return err +} + +// SQLReleaseWrapper describes how Helm releases are stored in an SQL database +type SQLReleaseWrapper struct { + // The primary key, made of {release-name}.{release-version} + Key string `db:"key"` + + // See https://github.com/helm/helm/blob/master/pkg/storage/driver/secrets.go#L236 + Type string `db:"type"` + + // The rspb.Release body, as a base64-encoded string + Body string `db:"body"` + + // Release "labels" that can be used as filters in the storage.Query(labels map[string]string) + // we implemented. Note that allowing Helm users to filter against new dimensions will require a + // new migration to be added, and the Create and/or update functions to be updated accordingly. + Name string `db:"name"` + Namespace string `db:"namespace"` + Version int `db:"version"` + Status string `db:"status"` + Owner string `db:"owner"` + CreatedAt int `db:"createdAt"` + ModifiedAt int `db:"modifiedAt"` +} + +// NewSQL initializes a new sql driver. +func NewSQL(connectionString string, logger func(string, ...interface{}), namespace string) (*SQL, error) { + db, err := sqlx.Connect(postgreSQLDialect, connectionString) + if err != nil { + return nil, err + } + + driver := &SQL{ + db: db, + Log: logger, + statementBuilder: sq.StatementBuilder.PlaceholderFormat(sq.Dollar), + } + + if err := driver.ensureDBSetup(); err != nil { + return nil, err + } + + driver.namespace = namespace + + return driver, nil +} + +// Get returns the release named by key. +func (s *SQL) Get(key string) (*rspb.Release, error) { + var record SQLReleaseWrapper + + qb := s.statementBuilder. + Select(sqlReleaseTableBodyColumn). + From(sqlReleaseTableName). + Where(sq.Eq{sqlReleaseTableKeyColumn: key}). + Where(sq.Eq{sqlReleaseTableNamespaceColumn: s.namespace}) + + query, args, err := qb.ToSql() + if err != nil { + s.Log("failed to build query: %v", err) + return nil, err + } + + // Get will return an error if the result is empty + if err := s.db.Get(&record, query, args...); err != nil { + s.Log("got SQL error when getting release %s: %v", key, err) + return nil, ErrReleaseNotFound + } + + release, err := decodeRelease(record.Body) + if err != nil { + s.Log("get: failed to decode data %q: %v", key, err) + return nil, err + } + + return release, nil +} + +// List returns the list of all releases such that filter(release) == true +func (s *SQL) List(filter func(*rspb.Release) bool) ([]*rspb.Release, error) { + sb := s.statementBuilder. + Select(sqlReleaseTableBodyColumn). + From(sqlReleaseTableName). + Where(sq.Eq{sqlReleaseTableOwnerColumn: sqlReleaseDefaultOwner}) + + // If a namespace was specified, we only list releases from that namespace + if s.namespace != "" { + sb = sb.Where(sq.Eq{sqlReleaseTableNamespaceColumn: s.namespace}) + } + + query, args, err := sb.ToSql() + if err != nil { + s.Log("failed to build query: %v", err) + return nil, err + } + + var records = []SQLReleaseWrapper{} + if err := s.db.Select(&records, query, args...); err != nil { + s.Log("list: failed to list: %v", err) + return nil, err + } + + var releases []*rspb.Release + for _, record := range records { + release, err := decodeRelease(record.Body) + if err != nil { + s.Log("list: failed to decode release: %v: %v", record, err) + continue + } + if filter(release) { + releases = append(releases, release) + } + } + + return releases, nil +} + +// Query returns the set of releases that match the provided set of labels. +func (s *SQL) Query(labels map[string]string) ([]*rspb.Release, error) { + sb := s.statementBuilder. + Select(sqlReleaseTableBodyColumn). + From(sqlReleaseTableName) + + keys := make([]string, 0, len(labels)) + for key := range labels { + keys = append(keys, key) + } + sort.Strings(keys) + for _, key := range keys { + if _, ok := labelMap[key]; ok { + sb = sb.Where(sq.Eq{key: labels[key]}) + } else { + s.Log("unknown label %s", key) + return nil, fmt.Errorf("unknow label %s", key) + } + } + + // If a namespace was specified, we only list releases from that namespace + if s.namespace != "" { + sb = sb.Where(sq.Eq{sqlReleaseTableNamespaceColumn: s.namespace}) + } + + // Build our query + query, args, err := sb.ToSql() + if err != nil { + s.Log("failed to build query: %v", err) + return nil, err + } + + var records = []SQLReleaseWrapper{} + if err := s.db.Select(&records, query, args...); err != nil { + s.Log("list: failed to query with labels: %v", err) + return nil, err + } + + var releases []*rspb.Release + for _, record := range records { + release, err := decodeRelease(record.Body) + if err != nil { + s.Log("list: failed to decode release: %v: %v", record, err) + continue + } + releases = append(releases, release) + } + + if len(releases) == 0 { + return nil, ErrReleaseNotFound + } + + return releases, nil +} + +// Create creates a new release. +func (s *SQL) Create(key string, rls *rspb.Release) error { + namespace := rls.Namespace + if namespace == "" { + namespace = defaultNamespace + } + s.namespace = namespace + + body, err := encodeRelease(rls) + if err != nil { + s.Log("failed to encode release: %v", err) + return err + } + + transaction, err := s.db.Beginx() + if err != nil { + s.Log("failed to start SQL transaction: %v", err) + return fmt.Errorf("error beginning transaction: %v", err) + } + + insertQuery, args, err := s.statementBuilder. + Insert(sqlReleaseTableName). + Columns( + sqlReleaseTableKeyColumn, + sqlReleaseTableTypeColumn, + sqlReleaseTableBodyColumn, + sqlReleaseTableNameColumn, + sqlReleaseTableNamespaceColumn, + sqlReleaseTableVersionColumn, + sqlReleaseTableStatusColumn, + sqlReleaseTableOwnerColumn, + sqlReleaseTableCreatedAtColumn, + ). + Values( + key, + sqlReleaseDefaultType, + body, + rls.Name, + namespace, + int(rls.Version), + rls.Info.Status.String(), + sqlReleaseDefaultOwner, + int(time.Now().Unix()), + ).ToSql() + if err != nil { + s.Log("failed to build insert query: %v", err) + return err + } + + if _, err := transaction.Exec(insertQuery, args...); err != nil { + defer transaction.Rollback() + + selectQuery, args, buildErr := s.statementBuilder. + Select(sqlReleaseTableKeyColumn). + From(sqlReleaseTableName). + Where(sq.Eq{sqlReleaseTableKeyColumn: key}). + Where(sq.Eq{sqlReleaseTableNamespaceColumn: s.namespace}). + ToSql() + if buildErr != nil { + s.Log("failed to build select query: %v", buildErr) + return err + } + + var record SQLReleaseWrapper + if err := transaction.Get(&record, selectQuery, args...); err == nil { + s.Log("release %s already exists", key) + return ErrReleaseExists + } + + s.Log("failed to store release %s in SQL database: %v", key, err) + return err + } + defer transaction.Commit() + + return nil +} + +// Update updates a release. +func (s *SQL) Update(key string, rls *rspb.Release) error { + namespace := rls.Namespace + if namespace == "" { + namespace = defaultNamespace + } + s.namespace = namespace + + body, err := encodeRelease(rls) + if err != nil { + s.Log("failed to encode release: %v", err) + return err + } + + query, args, err := s.statementBuilder. + Update(sqlReleaseTableName). + Set(sqlReleaseTableBodyColumn, body). + Set(sqlReleaseTableNameColumn, rls.Name). + Set(sqlReleaseTableVersionColumn, int(rls.Version)). + Set(sqlReleaseTableStatusColumn, rls.Info.Status.String()). + Set(sqlReleaseTableOwnerColumn, sqlReleaseDefaultOwner). + Set(sqlReleaseTableModifiedAtColumn, int(time.Now().Unix())). + Where(sq.Eq{sqlReleaseTableKeyColumn: key}). + Where(sq.Eq{sqlReleaseTableNamespaceColumn: namespace}). + ToSql() + + if err != nil { + s.Log("failed to build update query: %v", err) + return err + } + + if _, err := s.db.Exec(query, args...); err != nil { + s.Log("failed to update release %s in SQL database: %v", key, err) + return err + } + + return nil +} + +// Delete deletes a release or returns ErrReleaseNotFound. +func (s *SQL) Delete(key string) (*rspb.Release, error) { + transaction, err := s.db.Beginx() + if err != nil { + s.Log("failed to start SQL transaction: %v", err) + return nil, fmt.Errorf("error beginning transaction: %v", err) + } + + selectQuery, args, err := s.statementBuilder. + Select(sqlReleaseTableBodyColumn). + From(sqlReleaseTableName). + Where(sq.Eq{sqlReleaseTableKeyColumn: key}). + Where(sq.Eq{sqlReleaseTableNamespaceColumn: s.namespace}). + ToSql() + if err != nil { + s.Log("failed to build select query: %v", err) + return nil, err + } + + var record SQLReleaseWrapper + err = transaction.Get(&record, selectQuery, args...) + if err != nil { + s.Log("release %s not found: %v", key, err) + return nil, ErrReleaseNotFound + } + + release, err := decodeRelease(record.Body) + if err != nil { + s.Log("failed to decode release %s: %v", key, err) + transaction.Rollback() + return nil, err + } + defer transaction.Commit() + + deleteQuery, args, err := s.statementBuilder. + Delete(sqlReleaseTableName). + Where(sq.Eq{sqlReleaseTableKeyColumn: key}). + Where(sq.Eq{sqlReleaseTableNamespaceColumn: s.namespace}). + ToSql() + if err != nil { + s.Log("failed to build select query: %v", err) + return nil, err + } + + _, err = transaction.Exec(deleteQuery, args...) + return release, err +} diff --git a/pkg/storage/driver/sql_test.go b/pkg/storage/driver/sql_test.go new file mode 100644 index 000000000..1562a90aa --- /dev/null +++ b/pkg/storage/driver/sql_test.go @@ -0,0 +1,442 @@ +/* +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 driver + +import ( + "fmt" + "reflect" + "regexp" + "testing" + "time" + + sqlmock "github.com/DATA-DOG/go-sqlmock" + + rspb "helm.sh/helm/v3/pkg/release" +) + +func TestSQLName(t *testing.T) { + sqlDriver, _ := newTestFixtureSQL(t) + if sqlDriver.Name() != SQLDriverName { + t.Errorf("Expected name to be %s, got %s", SQLDriverName, sqlDriver.Name()) + } +} + +func TestSQLGet(t *testing.T) { + vers := int(1) + name := "smug-pigeon" + namespace := "default" + key := testKey(name, vers) + rel := releaseStub(name, vers, namespace, rspb.StatusDeployed) + + body, _ := encodeRelease(rel) + + sqlDriver, mock := newTestFixtureSQL(t) + + query := fmt.Sprintf( + regexp.QuoteMeta("SELECT %s FROM %s WHERE %s = $1 AND %s = $2"), + sqlReleaseTableBodyColumn, + sqlReleaseTableName, + sqlReleaseTableKeyColumn, + sqlReleaseTableNamespaceColumn, + ) + + mock. + ExpectQuery(query). + WithArgs(key, namespace). + WillReturnRows( + mock.NewRows([]string{ + sqlReleaseTableBodyColumn, + }).AddRow( + body, + ), + ).RowsWillBeClosed() + + got, err := sqlDriver.Get(key) + if err != nil { + t.Fatalf("Failed to get release: %v", err) + } + + if !reflect.DeepEqual(rel, got) { + t.Errorf("Expected release {%v}, got {%v}", rel, got) + } + + if err := mock.ExpectationsWereMet(); err != nil { + t.Errorf("sql expectations weren't met: %v", err) + } +} + +func TestSQLList(t *testing.T) { + body1, _ := encodeRelease(releaseStub("key-1", 1, "default", rspb.StatusUninstalled)) + body2, _ := encodeRelease(releaseStub("key-2", 1, "default", rspb.StatusUninstalled)) + body3, _ := encodeRelease(releaseStub("key-3", 1, "default", rspb.StatusDeployed)) + body4, _ := encodeRelease(releaseStub("key-4", 1, "default", rspb.StatusDeployed)) + body5, _ := encodeRelease(releaseStub("key-5", 1, "default", rspb.StatusSuperseded)) + body6, _ := encodeRelease(releaseStub("key-6", 1, "default", rspb.StatusSuperseded)) + + sqlDriver, mock := newTestFixtureSQL(t) + + for i := 0; i < 3; i++ { + query := fmt.Sprintf( + "SELECT %s FROM %s WHERE %s = $1 AND %s = $2", + sqlReleaseTableBodyColumn, + sqlReleaseTableName, + sqlReleaseTableOwnerColumn, + sqlReleaseTableNamespaceColumn, + ) + + mock. + ExpectQuery(regexp.QuoteMeta(query)). + WithArgs(sqlReleaseDefaultOwner, sqlDriver.namespace). + WillReturnRows( + mock.NewRows([]string{ + sqlReleaseTableBodyColumn, + }). + AddRow(body1). + AddRow(body2). + AddRow(body3). + AddRow(body4). + AddRow(body5). + AddRow(body6), + ).RowsWillBeClosed() + } + + // list all deleted releases + del, err := sqlDriver.List(func(rel *rspb.Release) bool { + return rel.Info.Status == rspb.StatusUninstalled + }) + // check + if err != nil { + t.Errorf("Failed to list deleted: %v", err) + } + if len(del) != 2 { + t.Errorf("Expected 2 deleted, got %d:\n%v\n", len(del), del) + } + + // list all deployed releases + dpl, err := sqlDriver.List(func(rel *rspb.Release) bool { + return rel.Info.Status == rspb.StatusDeployed + }) + // check + if err != nil { + t.Errorf("Failed to list deployed: %v", err) + } + if len(dpl) != 2 { + t.Errorf("Expected 2 deployed, got %d:\n%v\n", len(dpl), dpl) + } + + // list all superseded releases + ssd, err := sqlDriver.List(func(rel *rspb.Release) bool { + return rel.Info.Status == rspb.StatusSuperseded + }) + // check + if err != nil { + t.Errorf("Failed to list superseded: %v", err) + } + if len(ssd) != 2 { + t.Errorf("Expected 2 superseded, got %d:\n%v\n", len(ssd), ssd) + } + + if err := mock.ExpectationsWereMet(); err != nil { + t.Errorf("sql expectations weren't met: %v", err) + } +} + +func TestSqlCreate(t *testing.T) { + vers := 1 + name := "smug-pigeon" + namespace := "default" + key := testKey(name, vers) + rel := releaseStub(name, vers, namespace, rspb.StatusDeployed) + + sqlDriver, mock := newTestFixtureSQL(t) + body, _ := encodeRelease(rel) + + query := fmt.Sprintf( + "INSERT INTO %s (%s,%s,%s,%s,%s,%s,%s,%s,%s) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)", + sqlReleaseTableName, + sqlReleaseTableKeyColumn, + sqlReleaseTableTypeColumn, + sqlReleaseTableBodyColumn, + sqlReleaseTableNameColumn, + sqlReleaseTableNamespaceColumn, + sqlReleaseTableVersionColumn, + sqlReleaseTableStatusColumn, + sqlReleaseTableOwnerColumn, + sqlReleaseTableCreatedAtColumn, + ) + + mock.ExpectBegin() + mock. + ExpectExec(regexp.QuoteMeta(query)). + WithArgs(key, sqlReleaseDefaultType, body, rel.Name, rel.Namespace, int(rel.Version), rel.Info.Status.String(), sqlReleaseDefaultOwner, int(time.Now().Unix())). + WillReturnResult(sqlmock.NewResult(1, 1)) + mock.ExpectCommit() + + if err := sqlDriver.Create(key, rel); err != nil { + t.Fatalf("failed to create release with key %s: %v", key, err) + } + + if err := mock.ExpectationsWereMet(); err != nil { + t.Errorf("sql expectations weren't met: %v", err) + } +} + +func TestSqlCreateAlreadyExists(t *testing.T) { + vers := 1 + name := "smug-pigeon" + namespace := "default" + key := testKey(name, vers) + rel := releaseStub(name, vers, namespace, rspb.StatusDeployed) + + sqlDriver, mock := newTestFixtureSQL(t) + body, _ := encodeRelease(rel) + + insertQuery := fmt.Sprintf( + "INSERT INTO %s (%s,%s,%s,%s,%s,%s,%s,%s,%s) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)", + sqlReleaseTableName, + sqlReleaseTableKeyColumn, + sqlReleaseTableTypeColumn, + sqlReleaseTableBodyColumn, + sqlReleaseTableNameColumn, + sqlReleaseTableNamespaceColumn, + sqlReleaseTableVersionColumn, + sqlReleaseTableStatusColumn, + sqlReleaseTableOwnerColumn, + sqlReleaseTableCreatedAtColumn, + ) + + // Insert fails (primary key already exists) + mock.ExpectBegin() + mock. + ExpectExec(regexp.QuoteMeta(insertQuery)). + WithArgs(key, sqlReleaseDefaultType, body, rel.Name, rel.Namespace, int(rel.Version), rel.Info.Status.String(), sqlReleaseDefaultOwner, int(time.Now().Unix())). + WillReturnError(fmt.Errorf("dialect dependent SQL error")) + + selectQuery := fmt.Sprintf( + regexp.QuoteMeta("SELECT %s FROM %s WHERE %s = $1 AND %s = $2"), + sqlReleaseTableKeyColumn, + sqlReleaseTableName, + sqlReleaseTableKeyColumn, + sqlReleaseTableNamespaceColumn, + ) + + // Let's check that we do make sure the error is due to a release already existing + mock. + ExpectQuery(selectQuery). + WithArgs(key, namespace). + WillReturnRows( + mock.NewRows([]string{ + sqlReleaseTableKeyColumn, + }).AddRow( + key, + ), + ).RowsWillBeClosed() + mock.ExpectRollback() + + if err := sqlDriver.Create(key, rel); err == nil { + t.Fatalf("failed to create release with key %s: %v", key, err) + } + + if err := mock.ExpectationsWereMet(); err != nil { + t.Errorf("sql expectations weren't met: %v", err) + } +} + +func TestSqlUpdate(t *testing.T) { + vers := 1 + name := "smug-pigeon" + namespace := "default" + key := testKey(name, vers) + rel := releaseStub(name, vers, namespace, rspb.StatusDeployed) + + sqlDriver, mock := newTestFixtureSQL(t) + body, _ := encodeRelease(rel) + + query := fmt.Sprintf( + "UPDATE %s SET %s = $1, %s = $2, %s = $3, %s = $4, %s = $5, %s = $6 WHERE %s = $7 AND %s = $8", + sqlReleaseTableName, + sqlReleaseTableBodyColumn, + sqlReleaseTableNameColumn, + sqlReleaseTableVersionColumn, + sqlReleaseTableStatusColumn, + sqlReleaseTableOwnerColumn, + sqlReleaseTableModifiedAtColumn, + sqlReleaseTableKeyColumn, + sqlReleaseTableNamespaceColumn, + ) + + mock. + ExpectExec(regexp.QuoteMeta(query)). + WithArgs(body, rel.Name, int(rel.Version), rel.Info.Status.String(), sqlReleaseDefaultOwner, int(time.Now().Unix()), key, namespace). + WillReturnResult(sqlmock.NewResult(0, 1)) + + if err := sqlDriver.Update(key, rel); err != nil { + t.Fatalf("failed to update release with key %s: %v", key, err) + } + + if err := mock.ExpectationsWereMet(); err != nil { + t.Errorf("sql expectations weren't met: %v", err) + } +} + +func TestSqlQuery(t *testing.T) { + // Reflect actual use cases in ../storage.go + labelSetDeployed := map[string]string{ + "name": "smug-pigeon", + "owner": sqlReleaseDefaultOwner, + "status": "deployed", + } + labelSetAll := map[string]string{ + "name": "smug-pigeon", + "owner": sqlReleaseDefaultOwner, + } + + supersededRelease := releaseStub("smug-pigeon", 1, "default", rspb.StatusSuperseded) + supersededReleaseBody, _ := encodeRelease(supersededRelease) + deployedRelease := releaseStub("smug-pigeon", 2, "default", rspb.StatusDeployed) + deployedReleaseBody, _ := encodeRelease(deployedRelease) + + // Let's actually start our test + sqlDriver, mock := newTestFixtureSQL(t) + + query := fmt.Sprintf( + "SELECT %s FROM %s WHERE %s = $1 AND %s = $2 AND %s = $3 AND %s = $4", + sqlReleaseTableBodyColumn, + sqlReleaseTableName, + sqlReleaseTableNameColumn, + sqlReleaseTableOwnerColumn, + sqlReleaseTableStatusColumn, + sqlReleaseTableNamespaceColumn, + ) + + mock. + ExpectQuery(regexp.QuoteMeta(query)). + WithArgs("smug-pigeon", sqlReleaseDefaultOwner, "deployed", "default"). + WillReturnRows( + mock.NewRows([]string{ + sqlReleaseTableBodyColumn, + }).AddRow( + deployedReleaseBody, + ), + ).RowsWillBeClosed() + + query = fmt.Sprintf( + "SELECT %s FROM %s WHERE %s = $1 AND %s = $2 AND %s = $3", + sqlReleaseTableBodyColumn, + sqlReleaseTableName, + sqlReleaseTableNameColumn, + sqlReleaseTableOwnerColumn, + sqlReleaseTableNamespaceColumn, + ) + + mock. + ExpectQuery(regexp.QuoteMeta(query)). + WithArgs("smug-pigeon", sqlReleaseDefaultOwner, "default"). + WillReturnRows( + mock.NewRows([]string{ + sqlReleaseTableBodyColumn, + }).AddRow( + supersededReleaseBody, + ).AddRow( + deployedReleaseBody, + ), + ).RowsWillBeClosed() + + results, err := sqlDriver.Query(labelSetDeployed) + if err != nil { + t.Fatalf("failed to query for deployed smug-pigeon release: %v", err) + } + + for _, res := range results { + if !reflect.DeepEqual(res, deployedRelease) { + t.Errorf("Expected release {%v}, got {%v}", deployedRelease, res) + } + } + + results, err = sqlDriver.Query(labelSetAll) + if err != nil { + t.Fatalf("failed to query release history for smug-pigeon: %v", err) + } + + if len(results) != 2 { + t.Errorf("expected a resultset of size 2, got %d", len(results)) + } + + for _, res := range results { + if !reflect.DeepEqual(res, deployedRelease) && !reflect.DeepEqual(res, supersededRelease) { + t.Errorf("Expected release {%v} or {%v}, got {%v}", deployedRelease, supersededRelease, res) + } + } + + if err := mock.ExpectationsWereMet(); err != nil { + t.Errorf("sql expectations weren't met: %v", err) + } +} + +func TestSqlDelete(t *testing.T) { + vers := 1 + name := "smug-pigeon" + namespace := "default" + key := testKey(name, vers) + rel := releaseStub(name, vers, namespace, rspb.StatusDeployed) + + body, _ := encodeRelease(rel) + + sqlDriver, mock := newTestFixtureSQL(t) + + selectQuery := fmt.Sprintf( + "SELECT %s FROM %s WHERE %s = $1 AND %s = $2", + sqlReleaseTableBodyColumn, + sqlReleaseTableName, + sqlReleaseTableKeyColumn, + sqlReleaseTableNamespaceColumn, + ) + + mock.ExpectBegin() + mock. + ExpectQuery(regexp.QuoteMeta(selectQuery)). + WithArgs(key, namespace). + WillReturnRows( + mock.NewRows([]string{ + sqlReleaseTableBodyColumn, + }).AddRow( + body, + ), + ).RowsWillBeClosed() + + deleteQuery := fmt.Sprintf( + "DELETE FROM %s WHERE %s = $1 AND %s = $2", + sqlReleaseTableName, + sqlReleaseTableKeyColumn, + sqlReleaseTableNamespaceColumn, + ) + + mock. + ExpectExec(regexp.QuoteMeta(deleteQuery)). + WithArgs(key, namespace). + WillReturnResult(sqlmock.NewResult(0, 1)) + mock.ExpectCommit() + + deletedRelease, err := sqlDriver.Delete(key) + if err := mock.ExpectationsWereMet(); err != nil { + t.Errorf("sql expectations weren't met: %v", err) + } + if err != nil { + t.Fatalf("failed to delete release with key %q: %v", key, err) + } + + if !reflect.DeepEqual(rel, deletedRelease) { + t.Errorf("Expected release {%v}, got {%v}", rel, deletedRelease) + } +} From 853ba2de16a04d0715c44937162e0b58752a99d6 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Thu, 16 Apr 2020 14:54:15 -0600 Subject: [PATCH 065/281] fix: removed inaccurate comment (#7937) Signed-off-by: Matt Butcher --- pkg/chartutil/coalesce.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkg/chartutil/coalesce.go b/pkg/chartutil/coalesce.go index a5ff66aed..94b7f35fa 100644 --- a/pkg/chartutil/coalesce.go +++ b/pkg/chartutil/coalesce.go @@ -35,8 +35,6 @@ import ( // - A chart has access to all of the variables for it, as well as all of // the values destined for its dependencies. func CoalesceValues(chrt *chart.Chart, vals map[string]interface{}) (Values, error) { - // create a copy of vals and then pass it to coalesce - // and coalesceDeps, as both will mutate the passed values v, err := copystructure.Copy(vals) if err != nil { return vals, err From 8e1c34ef045b350c68974f8ca12e2cb6d245d3f3 Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Fri, 17 Apr 2020 10:42:52 -0400 Subject: [PATCH 066/281] Updating get stripts to skip pre-releases A recent change to the get scripts causes them to pickup pre-releases in addition to stable releases. This update causes only stable releases to be fetched by the get scripts. Fixed #7941 Signed-off-by: Matt Farina --- scripts/get | 4 ++-- scripts/get-helm-3 | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/get b/scripts/get index 3da11d4a4..afa02bbb1 100755 --- a/scripts/get +++ b/scripts/get @@ -82,9 +82,9 @@ checkDesiredVersion() { local release_url="https://github.com/helm/helm/releases" if type "curl" > /dev/null; then - TAG=$(curl -Ls $release_url | grep 'href="/helm/helm/releases/tag/v2.' | grep -v no-underline | head -n 1 | cut -d '"' -f 2 | awk '{n=split($NF,a,"/");print a[n]}' | awk 'a !~ $0{print}; {a=$0}') + TAG=$(curl -Ls $release_url | grep 'href="/helm/helm/releases/tag/v2.[0-9]*.[0-9]*\"' | grep -v no-underline | head -n 1 | cut -d '"' -f 2 | awk '{n=split($NF,a,"/");print a[n]}' | awk 'a !~ $0{print}; {a=$0}') elif type "wget" > /dev/null; then - TAG=$(wget $release_url -O - 2>&1 | grep 'href="/helm/helm/releases/tag/v2.' | grep -v no-underline | head -n 1 | cut -d '"' -f 2 | awk '{n=split($NF,a,"/");print a[n]}' | awk 'a !~ $0{print}; {a=$0}') + TAG=$(wget $release_url -O - 2>&1 | grep 'href="/helm/helm/releases/tag/v2.[0-9]*.[0-9]*\"' | grep -v no-underline | head -n 1 | cut -d '"' -f 2 | awk '{n=split($NF,a,"/");print a[n]}' | awk 'a !~ $0{print}; {a=$0}') fi else TAG=$DESIRED_VERSION diff --git a/scripts/get-helm-3 b/scripts/get-helm-3 index adadf5953..201065717 100755 --- a/scripts/get-helm-3 +++ b/scripts/get-helm-3 @@ -79,9 +79,9 @@ checkDesiredVersion() { # Get tag from release URL local latest_release_url="https://github.com/helm/helm/releases" if type "curl" > /dev/null; then - TAG=$(curl -Ls $latest_release_url | grep 'href="/helm/helm/releases/tag/v3.' | grep -v no-underline | head -n 1 | cut -d '"' -f 2 | awk '{n=split($NF,a,"/");print a[n]}' | awk 'a !~ $0{print}; {a=$0}') + TAG=$(curl -Ls $latest_release_url | grep 'href="/helm/helm/releases/tag/v3.[0-9]*.[0-9]*\"' | grep -v no-underline | head -n 1 | cut -d '"' -f 2 | awk '{n=split($NF,a,"/");print a[n]}' | awk 'a !~ $0{print}; {a=$0}') elif type "wget" > /dev/null; then - TAG=$(wget $latest_release_url -O - 2>&1 | grep 'href="/helm/helm/releases/tag/v3.' | grep -v no-underline | head -n 1 | cut -d '"' -f 2 | awk '{n=split($NF,a,"/");print a[n]}' | awk 'a !~ $0{print}; {a=$0}') + TAG=$(wget $latest_release_url -O - 2>&1 | grep 'href="/helm/helm/releases/tag/v3.[0-9]*.[0-9]*\"' | grep -v no-underline | head -n 1 | cut -d '"' -f 2 | awk '{n=split($NF,a,"/");print a[n]}' | awk 'a !~ $0{print}; {a=$0}') fi else TAG=$DESIRED_VERSION From 1911870958098b774973c6fe56bfdf4441f61596 Mon Sep 17 00:00:00 2001 From: Matthew Morrissette Date: Fri, 17 Apr 2020 10:56:29 -0700 Subject: [PATCH 067/281] fix(helm): allow a previously failed release to be upgraded (#7653) Signed-off-by: Matt Morrissette --- ...e-with-bad-or-missing-existing-release.txt | 1 + cmd/helm/upgrade_test.go | 23 +++++++++++++ pkg/action/upgrade.go | 32 ++++++++++++++----- pkg/storage/driver/driver.go | 25 ++++++++++++++- pkg/storage/storage.go | 4 +-- 5 files changed, 74 insertions(+), 11 deletions(-) create mode 100644 cmd/helm/testdata/output/upgrade-with-bad-or-missing-existing-release.txt diff --git a/cmd/helm/testdata/output/upgrade-with-bad-or-missing-existing-release.txt b/cmd/helm/testdata/output/upgrade-with-bad-or-missing-existing-release.txt new file mode 100644 index 000000000..8f24574a6 --- /dev/null +++ b/cmd/helm/testdata/output/upgrade-with-bad-or-missing-existing-release.txt @@ -0,0 +1 @@ +Error: UPGRADE FAILED: "funny-bunny" has no deployed releases diff --git a/cmd/helm/upgrade_test.go b/cmd/helm/upgrade_test.go index 3a6d75adc..6f260ae57 100644 --- a/cmd/helm/upgrade_test.go +++ b/cmd/helm/upgrade_test.go @@ -80,6 +80,10 @@ func TestUpgradeCmd(t *testing.T) { missingDepsPath := "testdata/testcharts/chart-missing-deps" badDepsPath := "testdata/testcharts/chart-bad-requirements" + relWithStatusMock := func(n string, v int, ch *chart.Chart, status release.Status) *release.Release { + return release.Mock(&release.MockReleaseOptions{Name: n, Version: v, Chart: ch, Status: status}) + } + relMock := func(n string, v int, ch *chart.Chart) *release.Release { return release.Mock(&release.MockReleaseOptions{Name: n, Version: v, Chart: ch}) } @@ -139,6 +143,25 @@ func TestUpgradeCmd(t *testing.T) { golden: "output/upgrade-with-bad-dependencies.txt", wantError: true, }, + { + name: "upgrade a non-existent release", + cmd: fmt.Sprintf("upgrade funny-bunny '%s'", chartPath), + golden: "output/upgrade-with-bad-or-missing-existing-release.txt", + wantError: true, + }, + { + name: "upgrade a failed release", + cmd: fmt.Sprintf("upgrade funny-bunny '%s'", chartPath), + golden: "output/upgrade.txt", + rels: []*release.Release{relWithStatusMock("funny-bunny", 2, ch, release.StatusFailed)}, + }, + { + name: "upgrade a pending install release", + cmd: fmt.Sprintf("upgrade funny-bunny '%s'", chartPath), + golden: "output/upgrade-with-bad-or-missing-existing-release.txt", + wantError: true, + rels: []*release.Release{relWithStatusMock("funny-bunny", 2, ch, release.StatusPendingInstall)}, + }, } runTestCmd(t, tests) } diff --git a/pkg/action/upgrade.go b/pkg/action/upgrade.go index 7565da5a9..67872aa2f 100644 --- a/pkg/action/upgrade.go +++ b/pkg/action/upgrade.go @@ -33,6 +33,7 @@ import ( "helm.sh/helm/v3/pkg/postrender" "helm.sh/helm/v3/pkg/release" "helm.sh/helm/v3/pkg/releaseutil" + "helm.sh/helm/v3/pkg/storage/driver" ) // Upgrade is the action for upgrading releases. @@ -159,12 +160,33 @@ func (u *Upgrade) prepareUpgrade(name string, chart *chart.Chart, vals map[strin return nil, nil, errMissingChart } - // finds the deployed release with the given name - currentRelease, err := u.cfg.Releases.Deployed(name) + // finds the last non-deleted release with the given name + lastRelease, err := u.cfg.Releases.Last(name) if err != nil { + // to keep existing behavior of returning the "%q has no deployed releases" error when an existing release does not exist + if errors.Is(err, driver.ErrReleaseNotFound) { + return nil, nil, driver.NewErrNoDeployedReleases(name) + } return nil, nil, err } + var currentRelease *release.Release + if lastRelease.Info.Status == release.StatusDeployed { + // no need to retrieve the last deployed release from storage as the last release is deployed + currentRelease = lastRelease + } else { + // finds the deployed release with the given name + currentRelease, err = u.cfg.Releases.Deployed(name) + if err != nil { + if errors.Is(err, driver.ErrNoDeployedReleases) && + (lastRelease.Info.Status == release.StatusFailed || lastRelease.Info.Status == release.StatusSuperseded) { + currentRelease = lastRelease + } else { + return nil, nil, err + } + } + } + // determine if values will be reused vals, err = u.reuseValues(chart, currentRelease, vals) if err != nil { @@ -175,12 +197,6 @@ func (u *Upgrade) prepareUpgrade(name string, chart *chart.Chart, vals map[strin return nil, nil, err } - // finds the non-deleted release with the given name - lastRelease, err := u.cfg.Releases.Last(name) - if err != nil { - return nil, nil, err - } - // Increment revision count. This is passed to templates, and also stored on // the release object. revision := lastRelease.Version + 1 diff --git a/pkg/storage/driver/driver.go b/pkg/storage/driver/driver.go index 9a1fbc579..9c01f3766 100644 --- a/pkg/storage/driver/driver.go +++ b/pkg/storage/driver/driver.go @@ -17,6 +17,8 @@ limitations under the License. package driver // import "helm.sh/helm/v3/pkg/storage/driver" import ( + "fmt" + "github.com/pkg/errors" rspb "helm.sh/helm/v3/pkg/release" @@ -28,9 +30,30 @@ var ( // ErrReleaseExists indicates that a release already exists. ErrReleaseExists = errors.New("release: already exists") // ErrInvalidKey indicates that a release key could not be parsed. - ErrInvalidKey = errors.Errorf("release: invalid key") + ErrInvalidKey = errors.New("release: invalid key") + // ErrNoDeployedReleases indicates that there are no releases with the given key in the deployed state + ErrNoDeployedReleases = errors.New("has no deployed releases") ) +// StorageDriverError records an error and the release name that caused it +type StorageDriverError struct { + ReleaseName string + Err error +} + +func (e *StorageDriverError) Error() string { + return fmt.Sprintf("%q %s", e.ReleaseName, e.Err.Error()) +} + +func (e *StorageDriverError) Unwrap() error { return e.Err } + +func NewErrNoDeployedReleases(releaseName string) error { + return &StorageDriverError{ + ReleaseName: releaseName, + Err: ErrNoDeployedReleases, + } +} + // Creator is the interface that wraps the Create method. // // Create stores the release or returns ErrReleaseExists diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go index 3e62ae9ee..c195120cd 100644 --- a/pkg/storage/storage.go +++ b/pkg/storage/storage.go @@ -116,7 +116,7 @@ func (s *Storage) Deployed(name string) (*rspb.Release, error) { } if len(ls) == 0 { - return nil, errors.Errorf("%q has no deployed releases", name) + return nil, driver.NewErrNoDeployedReleases(name) } // If executed concurrently, Helm's database gets corrupted @@ -140,7 +140,7 @@ func (s *Storage) DeployedAll(name string) ([]*rspb.Release, error) { return ls, nil } if strings.Contains(err.Error(), "not found") { - return nil, errors.Errorf("%q has no deployed releases", name) + return nil, driver.NewErrNoDeployedReleases(name) } return nil, err } From b83d3d415c8a55ad2af0f2ace0642445b0dbde0c Mon Sep 17 00:00:00 2001 From: Predrag Knezevic Date: Mon, 20 Apr 2020 14:48:11 +0200 Subject: [PATCH 068/281] fs_test: use os.Getuid() instead user.Current() to determine if a test is executed with root privileges. This change lower the expectations on test env setup, i.e. tests could be executed in a container under a random UID, without require an user in /etc/passwd Signed-off-by: Predrag Knezevic --- internal/third_party/dep/fs/fs_test.go | 49 +++++++------------------- 1 file changed, 12 insertions(+), 37 deletions(-) diff --git a/internal/third_party/dep/fs/fs_test.go b/internal/third_party/dep/fs/fs_test.go index bf4b803f8..d3ff2c694 100644 --- a/internal/third_party/dep/fs/fs_test.go +++ b/internal/third_party/dep/fs/fs_test.go @@ -35,7 +35,6 @@ import ( "io/ioutil" "os" "os/exec" - "os/user" "path/filepath" "runtime" "sync" @@ -175,13 +174,9 @@ func TestCopyDirFail_SrcInaccessible(t *testing.T) { t.Skip("skipping on windows") } - var currentUser, err = user.Current() + var currentUID = os.Getuid() - if err != nil { - t.Fatalf("Failed to get name of current user: %s", err) - } - - if currentUser.Name == "root" { + if currentUID == 0 { // Skipping if root, because all files are accessible t.Skip("Skipping for root user") } @@ -214,13 +209,9 @@ func TestCopyDirFail_DstInaccessible(t *testing.T) { t.Skip("skipping on windows") } - var currentUser, err = user.Current() + var currentUID = os.Getuid() - if err != nil { - t.Fatalf("Failed to get name of current user: %s", err) - } - - if currentUser.Name == "root" { + if currentUID == 0 { // Skipping if root, because all files are accessible t.Skip("Skipping for root user") } @@ -314,13 +305,9 @@ func TestCopyDirFailOpen(t *testing.T) { t.Skip("skipping on windows") } - var currentUser, err = user.Current() - - if err != nil { - t.Fatalf("Failed to get name of current user: %s", err) - } + var currentUID = os.Getuid() - if currentUser.Name == "root" { + if currentUID == 0 { // Skipping if root, because all files are accessible t.Skip("Skipping for root user") } @@ -483,13 +470,9 @@ func TestCopyFileFail(t *testing.T) { t.Skip("skipping on windows") } - var currentUser, err = user.Current() + var currentUID = os.Getuid() - if err != nil { - t.Fatalf("Failed to get name of current user: %s", err) - } - - if currentUser.Name == "root" { + if currentUID == 0 { // Skipping if root, because all files are accessible t.Skip("Skipping for root user") } @@ -574,13 +557,9 @@ func setupInaccessibleDir(t *testing.T, op func(dir string) error) func() { func TestIsDir(t *testing.T) { - var currentUser, err = user.Current() + var currentUID = os.Getuid() - if err != nil { - t.Fatalf("Failed to get name of current user: %s", err) - } - - if currentUser.Name == "root" { + if currentUID == 0 { // Skipping if root, because all files are accessible t.Skip("Skipping for root user") } @@ -631,13 +610,9 @@ func TestIsDir(t *testing.T) { func TestIsSymlink(t *testing.T) { - var currentUser, err = user.Current() - - if err != nil { - t.Fatalf("Failed to get name of current user: %s", err) - } + var currentUID = os.Getuid() - if currentUser.Name == "root" { + if currentUID == 0 { // Skipping if root, because all files are accessible t.Skip("Skipping for root user") } From 999e4f266f0c69de023a55858b1284e9d89ee20e Mon Sep 17 00:00:00 2001 From: alan Date: Sat, 11 Apr 2020 23:14:24 +0800 Subject: [PATCH 069/281] group command for easy read Signed-off-by: Alan Zhu --- cmd/helm/root.go | 63 ++++++++++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 23 deletions(-) diff --git a/cmd/helm/root.go b/cmd/helm/root.go index 3ebea3bae..4752ccec5 100644 --- a/cmd/helm/root.go +++ b/cmd/helm/root.go @@ -25,6 +25,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/tools/clientcmd" + "k8s.io/kubectl/pkg/util/templates" "helm.sh/helm/v3/internal/completion" "helm.sh/helm/v3/internal/experimental/registry" @@ -134,31 +135,47 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string flags.ParseErrorsWhitelist.UnknownFlags = true flags.Parse(args) + commandGroups := templates.CommandGroups{ + { + Message: "Release Management Commands:", + Commands: []*cobra.Command{ + newInstallCmd(actionConfig, out), + newListCmd(actionConfig, out), + newGetCmd(actionConfig, out), + newStatusCmd(actionConfig, out), + newUpgradeCmd(actionConfig, out), + newHistoryCmd(actionConfig, out), + newRollbackCmd(actionConfig, out), + newReleaseTestCmd(actionConfig, out), + newUninstallCmd(actionConfig, out), + }, + }, + { + Message: "Chart Commands:", + Commands: []*cobra.Command{ + newCreateCmd(out), + newDependencyCmd(out), + newPackageCmd(out), + newTemplateCmd(actionConfig, out), + newLintCmd(out), + newVerifyCmd(out), + }, + }, + { + Message: "Chart Repository Commands:", + Commands: []*cobra.Command{ + newRepoCmd(out), + newSearchCmd(out), + newPullCmd(out), + newShowCmd(out), + }, + }, + } + commandGroups.Add(cmd) + templates.ActsAsRootCommand(cmd, []string{"options"}, commandGroups...) + // Add subcommands cmd.AddCommand( - // chart commands - newCreateCmd(out), - newDependencyCmd(out), - newPullCmd(out), - newShowCmd(out), - newLintCmd(out), - newPackageCmd(out), - newRepoCmd(out), - newSearchCmd(out), - newVerifyCmd(out), - - // release commands - newGetCmd(actionConfig, out), - newHistoryCmd(actionConfig, out), - newInstallCmd(actionConfig, out), - newListCmd(actionConfig, out), - newReleaseTestCmd(actionConfig, out), - newRollbackCmd(actionConfig, out), - newStatusCmd(actionConfig, out), - newTemplateCmd(actionConfig, out), - newUninstallCmd(actionConfig, out), - newUpgradeCmd(actionConfig, out), - newCompletionCmd(out), newEnvCmd(out), newPluginCmd(out), From d0726e07abed91554f1c62351dc62da4bf21f469 Mon Sep 17 00:00:00 2001 From: Andre Sencioles Date: Wed, 22 Apr 2020 07:16:55 +1200 Subject: [PATCH 070/281] Parse reference templates in predictable order (#7702) * Parse reference templates in predictable order Fix issue #7701 Signed-off-by: Andre Sencioles * Add test case for issue #7701 regression Signed-off-by: Andre Sencioles * gofmt Signed-off-by: Andre Sencioles --- pkg/engine/engine.go | 4 +++- pkg/engine/engine_test.go | 40 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index 1cc94d685..94ab1da95 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -213,6 +213,7 @@ func (e Engine) renderWithReferences(tpls, referenceTpls map[string]renderable) // We want to parse the templates in a predictable order. The order favors // higher-level (in file system) templates over deeply nested templates. keys := sortTemplates(tpls) + referenceKeys := sortTemplates(referenceTpls) for _, filename := range keys { r := tpls[filename] @@ -223,8 +224,9 @@ func (e Engine) renderWithReferences(tpls, referenceTpls map[string]renderable) // Adding the reference templates to the template context // so they can be referenced in the tpl function - for filename, r := range referenceTpls { + for _, filename := range referenceKeys { if t.Lookup(filename) == nil { + r := referenceTpls[filename] if _, err := t.New(filename).Parse(r.tpl); err != nil { return map[string]string{}, cleanupParseError(filename, err) } diff --git a/pkg/engine/engine_test.go b/pkg/engine/engine_test.go index d5f36aac8..c1cdf625e 100644 --- a/pkg/engine/engine_test.go +++ b/pkg/engine/engine_test.go @@ -126,6 +126,46 @@ func TestRender(t *testing.T) { } } +func TestRenderRefsOrdering(t *testing.T) { + parentChart := &chart.Chart{ + Metadata: &chart.Metadata{ + Name: "parent", + Version: "1.2.3", + }, + Templates: []*chart.File{ + {Name: "templates/_helpers.tpl", Data: []byte(`{{- define "test" -}}parent value{{- end -}}`)}, + {Name: "templates/test.yaml", Data: []byte(`{{ tpl "{{ include \"test\" . }}" . }}`)}, + }, + } + childChart := &chart.Chart{ + Metadata: &chart.Metadata{ + Name: "child", + Version: "1.2.3", + }, + Templates: []*chart.File{ + {Name: "templates/_helpers.tpl", Data: []byte(`{{- define "test" -}}child value{{- end -}}`)}, + }, + } + parentChart.AddDependency(childChart) + + expect := map[string]string{ + "parent/templates/test.yaml": "parent value", + } + + for i := 0; i < 100; i++ { + out, err := Render(parentChart, chartutil.Values{}) + if err != nil { + t.Fatalf("Failed to render templates: %s", err) + } + + for name, data := range expect { + if out[name] != data { + t.Fatalf("Expected %q, got %q (iteraction %d)", data, out[name], i+1) + } + } + } +} + func TestRenderInternals(t *testing.T) { // Test the internals of the rendering tool. From bb47286f09331271e88e6047c51fd6e0ea936506 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Wed, 22 Apr 2020 10:09:34 -0600 Subject: [PATCH 071/281] fix linting error with lookup function (#7969) Signed-off-by: Matt Butcher --- pkg/action/action.go | 133 +++++++++++++++++++++++++++++++++++++ pkg/action/install.go | 122 +--------------------------------- pkg/action/install_test.go | 22 ++++++ pkg/action/upgrade.go | 2 +- pkg/engine/engine.go | 5 +- pkg/engine/engine_test.go | 2 +- pkg/engine/funcs.go | 5 ++ pkg/engine/funcs_test.go | 5 ++ pkg/engine/lookup_func.go | 8 ++- 9 files changed, 178 insertions(+), 126 deletions(-) diff --git a/pkg/action/action.go b/pkg/action/action.go index 5da901635..a8437d729 100644 --- a/pkg/action/action.go +++ b/pkg/action/action.go @@ -17,10 +17,13 @@ limitations under the License. package action import ( + "bytes" "fmt" "os" "path" + "path/filepath" "regexp" + "strings" "github.com/pkg/errors" "k8s.io/apimachinery/pkg/api/meta" @@ -30,9 +33,13 @@ import ( "k8s.io/client-go/rest" "helm.sh/helm/v3/internal/experimental/registry" + "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/chartutil" + "helm.sh/helm/v3/pkg/engine" "helm.sh/helm/v3/pkg/kube" + "helm.sh/helm/v3/pkg/postrender" "helm.sh/helm/v3/pkg/release" + "helm.sh/helm/v3/pkg/releaseutil" "helm.sh/helm/v3/pkg/storage" "helm.sh/helm/v3/pkg/storage/driver" "helm.sh/helm/v3/pkg/time" @@ -86,6 +93,132 @@ type Configuration struct { Log func(string, ...interface{}) } +// renderResources renders the templates in a chart +// +// TODO: This function is badly in need of a refactor. +func (c *Configuration) renderResources(ch *chart.Chart, values chartutil.Values, releaseName, outputDir string, subNotes, useReleaseName, includeCrds bool, pr postrender.PostRenderer, dryRun bool) ([]*release.Hook, *bytes.Buffer, string, error) { + hs := []*release.Hook{} + b := bytes.NewBuffer(nil) + + caps, err := c.getCapabilities() + if err != nil { + return hs, b, "", err + } + + if ch.Metadata.KubeVersion != "" { + if !chartutil.IsCompatibleRange(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()) + } + } + + var files map[string]string + var err2 error + + // A `helm template` or `helm install --dry-run` should not talk to the remote cluster. + // It will break in interesting and exotic ways because other data (e.g. discovery) + // is mocked. It is not up to the template author to decide when the user wants to + // connect to the cluster. So when the user says to dry run, respect the user's + // wishes and do not connect to the cluster. + if !dryRun && c.RESTClientGetter != nil { + rest, err := c.RESTClientGetter.ToRESTConfig() + if err != nil { + return hs, b, "", err + } + files, err2 = engine.RenderWithClient(ch, values, rest) + } else { + files, err2 = engine.Render(ch, values) + } + + if err2 != nil { + return hs, b, "", err2 + } + + // NOTES.txt gets rendered like all the other files, but because it's not a hook nor a resource, + // pull it out of here into a separate file so that we can actually use the output of the rendered + // text file. We have to spin through this map because the file contains path information, so we + // look for terminating NOTES.txt. We also remove it from the files so that we don't have to skip + // it in the sortHooks. + var notesBuffer bytes.Buffer + for k, v := range files { + if strings.HasSuffix(k, notesFileSuffix) { + if subNotes || (k == path.Join(ch.Name(), "templates", notesFileSuffix)) { + // If buffer contains data, add newline before adding more + if notesBuffer.Len() > 0 { + notesBuffer.WriteString("\n") + } + notesBuffer.WriteString(v) + } + delete(files, k) + } + } + notes := notesBuffer.String() + + // Sort hooks, manifests, and partials. Only hooks and manifests are returned, + // as partials are not used after renderer.Render. Empty manifests are also + // removed here. + hs, manifests, err := releaseutil.SortManifests(files, caps.APIVersions, releaseutil.InstallOrder) + if err != nil { + // By catching parse errors here, we can prevent bogus releases from going + // to Kubernetes. + // + // We return the files as a big blob of data to help the user debug parser + // errors. + for name, content := range files { + if strings.TrimSpace(content) == "" { + continue + } + fmt.Fprintf(b, "---\n# Source: %s\n%s\n", name, content) + } + return hs, b, "", err + } + + // Aggregate all valid manifests into one big doc. + fileWritten := make(map[string]bool) + + if includeCrds { + for _, crd := range ch.CRDObjects() { + if outputDir == "" { + fmt.Fprintf(b, "---\n# Source: %s\n%s\n", crd.Name, string(crd.File.Data[:])) + } else { + err = writeToFile(outputDir, crd.Filename, string(crd.File.Data[:]), fileWritten[crd.Name]) + if err != nil { + return hs, b, "", err + } + fileWritten[crd.Name] = true + } + } + } + + for _, m := range manifests { + if outputDir == "" { + fmt.Fprintf(b, "---\n# Source: %s\n%s\n", m.Name, m.Content) + } else { + newDir := outputDir + if useReleaseName { + newDir = filepath.Join(outputDir, releaseName) + } + // NOTE: We do not have to worry about the post-renderer because + // output dir is only used by `helm template`. In the next major + // release, we should move this logic to template only as it is not + // used by install or upgrade + err = writeToFile(newDir, m.Name, m.Content, fileWritten[m.Name]) + if err != nil { + return hs, b, "", err + } + fileWritten[m.Name] = true + } + } + + if pr != nil { + b, err = pr.Run(b) + if err != nil { + return hs, b, notes, errors.Wrap(err, "error while running post render on files") + } + } + + return hs, b, notes, nil +} + // RESTClientGetter gets the rest client type RESTClientGetter interface { ToRESTConfig() (*rest.Config, error) diff --git a/pkg/action/install.go b/pkg/action/install.go index 4b4dd9214..10a9644dd 100644 --- a/pkg/action/install.go +++ b/pkg/action/install.go @@ -39,7 +39,6 @@ import ( "helm.sh/helm/v3/pkg/chartutil" "helm.sh/helm/v3/pkg/cli" "helm.sh/helm/v3/pkg/downloader" - "helm.sh/helm/v3/pkg/engine" "helm.sh/helm/v3/pkg/getter" "helm.sh/helm/v3/pkg/kube" kubefake "helm.sh/helm/v3/pkg/kube/fake" @@ -232,7 +231,7 @@ func (i *Install) Run(chrt *chart.Chart, vals map[string]interface{}) (*release. rel := i.createRelease(chrt, vals) var manifestDoc *bytes.Buffer - rel.Hooks, manifestDoc, rel.Info.Notes, err = i.cfg.renderResources(chrt, valuesToRender, i.ReleaseName, i.OutputDir, i.SubNotes, i.UseReleaseName, i.IncludeCRDs, i.PostRenderer) + rel.Hooks, manifestDoc, rel.Info.Notes, err = i.cfg.renderResources(chrt, valuesToRender, i.ReleaseName, i.OutputDir, i.SubNotes, i.UseReleaseName, i.IncludeCRDs, i.PostRenderer, i.DryRun) // Even for errors, attach this if available if manifestDoc != nil { rel.Manifest = manifestDoc.String() @@ -475,125 +474,6 @@ func (i *Install) replaceRelease(rel *release.Release) error { return i.recordRelease(last) } -// renderResources renders the templates in a chart -func (c *Configuration) renderResources(ch *chart.Chart, values chartutil.Values, releaseName, outputDir string, subNotes, useReleaseName, includeCrds bool, pr postrender.PostRenderer) ([]*release.Hook, *bytes.Buffer, string, error) { - hs := []*release.Hook{} - b := bytes.NewBuffer(nil) - - caps, err := c.getCapabilities() - if err != nil { - return hs, b, "", err - } - - if ch.Metadata.KubeVersion != "" { - if !chartutil.IsCompatibleRange(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()) - } - } - - var files map[string]string - var err2 error - - if c.RESTClientGetter != nil { - rest, err := c.RESTClientGetter.ToRESTConfig() - if err != nil { - return hs, b, "", err - } - files, err2 = engine.RenderWithClient(ch, values, rest) - } else { - files, err2 = engine.Render(ch, values) - } - - if err2 != nil { - return hs, b, "", err2 - } - - // NOTES.txt gets rendered like all the other files, but because it's not a hook nor a resource, - // pull it out of here into a separate file so that we can actually use the output of the rendered - // text file. We have to spin through this map because the file contains path information, so we - // look for terminating NOTES.txt. We also remove it from the files so that we don't have to skip - // it in the sortHooks. - var notesBuffer bytes.Buffer - for k, v := range files { - if strings.HasSuffix(k, notesFileSuffix) { - if subNotes || (k == path.Join(ch.Name(), "templates", notesFileSuffix)) { - // If buffer contains data, add newline before adding more - if notesBuffer.Len() > 0 { - notesBuffer.WriteString("\n") - } - notesBuffer.WriteString(v) - } - delete(files, k) - } - } - notes := notesBuffer.String() - - // Sort hooks, manifests, and partials. Only hooks and manifests are returned, - // as partials are not used after renderer.Render. Empty manifests are also - // removed here. - hs, manifests, err := releaseutil.SortManifests(files, caps.APIVersions, releaseutil.InstallOrder) - if err != nil { - // By catching parse errors here, we can prevent bogus releases from going - // to Kubernetes. - // - // We return the files as a big blob of data to help the user debug parser - // errors. - for name, content := range files { - if strings.TrimSpace(content) == "" { - continue - } - fmt.Fprintf(b, "---\n# Source: %s\n%s\n", name, content) - } - return hs, b, "", err - } - - // Aggregate all valid manifests into one big doc. - fileWritten := make(map[string]bool) - - if includeCrds { - for _, crd := range ch.CRDObjects() { - if outputDir == "" { - fmt.Fprintf(b, "---\n# Source: %s\n%s\n", crd.Name, string(crd.File.Data[:])) - } else { - err = writeToFile(outputDir, crd.Filename, string(crd.File.Data[:]), fileWritten[crd.Name]) - if err != nil { - return hs, b, "", err - } - fileWritten[crd.Name] = true - } - } - } - - for _, m := range manifests { - if outputDir == "" { - fmt.Fprintf(b, "---\n# Source: %s\n%s\n", m.Name, m.Content) - } else { - newDir := outputDir - if useReleaseName { - newDir = filepath.Join(outputDir, releaseName) - } - // NOTE: We do not have to worry about the post-renderer because - // output dir is only used by `helm template`. In the next major - // release, we should move this logic to template only as it is not - // used by install or upgrade - err = writeToFile(newDir, m.Name, m.Content, fileWritten[m.Name]) - if err != nil { - return hs, b, "", err - } - fileWritten[m.Name] = true - } - } - - if pr != nil { - b, err = pr.Run(b) - if err != nil { - return hs, b, notes, errors.Wrap(err, "error while running post render on files") - } - } - - return hs, b, notes, nil -} - // write the to /. controls if the file is created or content will be appended func writeToFile(outputDir string, name string, data string, append bool) error { outfileName := strings.Join([]string{outputDir, name}, string(filepath.Separator)) diff --git a/pkg/action/install_test.go b/pkg/action/install_test.go index bf47895a1..6c4012cfd 100644 --- a/pkg/action/install_test.go +++ b/pkg/action/install_test.go @@ -29,6 +29,7 @@ import ( "github.com/stretchr/testify/assert" "helm.sh/helm/v3/internal/test" + "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/chartutil" kubefake "helm.sh/helm/v3/pkg/kube/fake" "helm.sh/helm/v3/pkg/release" @@ -240,6 +241,27 @@ func TestInstallRelease_DryRun(t *testing.T) { is.Equal(res.Info.Description, "Dry run complete") } +// Regression test for #7955: Lookup must not connect to Kubernetes on a dry-run. +func TestInstallRelease_DryRun_Lookup(t *testing.T) { + is := assert.New(t) + instAction := installAction(t) + instAction.DryRun = true + vals := map[string]interface{}{} + + mockChart := buildChart(withSampleTemplates()) + mockChart.Templates = append(mockChart.Templates, &chart.File{ + Name: "templates/lookup", + Data: []byte(`goodbye: {{ lookup "v1" "Namespace" "" "___" }}`), + }) + + res, err := instAction.Run(mockChart, vals) + if err != nil { + t.Fatalf("Failed install: %s", err) + } + + is.Contains(res.Manifest, "goodbye: map[]") +} + func TestInstallReleaseIncorrectTemplate_DryRun(t *testing.T) { is := assert.New(t) instAction := installAction(t) diff --git a/pkg/action/upgrade.go b/pkg/action/upgrade.go index 67872aa2f..fc289dbab 100644 --- a/pkg/action/upgrade.go +++ b/pkg/action/upgrade.go @@ -217,7 +217,7 @@ func (u *Upgrade) prepareUpgrade(name string, chart *chart.Chart, vals map[strin return nil, nil, err } - hooks, manifestDoc, notesTxt, err := u.cfg.renderResources(chart, valuesToRender, "", "", u.SubNotes, false, false, u.PostRenderer) + hooks, manifestDoc, notesTxt, err := u.cfg.renderResources(chart, valuesToRender, "", "", u.SubNotes, false, false, u.PostRenderer, u.DryRun) if err != nil { return nil, nil, err } diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index 94ab1da95..c5d064ad5 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -172,7 +172,10 @@ func (e Engine) initFunMap(t *template.Template, referenceTpls map[string]render } return val, nil } - if e.config != nil { + + // If we are not linting and have a cluster connection, provide a Kubernetes-backed + // implementation. + if !e.LintMode && e.config != nil { funcMap["lookup"] = NewLookupFunction(e.config) } diff --git a/pkg/engine/engine_test.go b/pkg/engine/engine_test.go index c1cdf625e..87e84c48b 100644 --- a/pkg/engine/engine_test.go +++ b/pkg/engine/engine_test.go @@ -70,7 +70,7 @@ func TestFuncMap(t *testing.T) { } // Test for Engine-specific template functions. - expect := []string{"include", "required", "tpl", "toYaml", "fromYaml", "toToml", "toJson", "fromJson"} + expect := []string{"include", "required", "tpl", "toYaml", "fromYaml", "toToml", "toJson", "fromJson", "lookup"} for _, f := range expect { if _, ok := fns[f]; !ok { t.Errorf("Expected add-on function %q", f) diff --git a/pkg/engine/funcs.go b/pkg/engine/funcs.go index e5769cbe0..92b4c3383 100644 --- a/pkg/engine/funcs.go +++ b/pkg/engine/funcs.go @@ -62,6 +62,11 @@ func funcMap() template.FuncMap { "include": func(string, interface{}) string { return "not implemented" }, "tpl": func(string, interface{}) interface{} { return "not implemented" }, "required": func(string, interface{}) (interface{}, error) { return "not implemented", nil }, + // Provide a placeholder for the "lookup" function, which requires a kubernetes + // connection. + "lookup": func(string, string, string, string) (map[string]interface{}, error) { + return map[string]interface{}{}, nil + }, } for k, v := range extra { diff --git a/pkg/engine/funcs_test.go b/pkg/engine/funcs_test.go index ddcf6624c..62c63ec2b 100644 --- a/pkg/engine/funcs_test.go +++ b/pkg/engine/funcs_test.go @@ -94,6 +94,11 @@ func TestFuncs(t *testing.T) { tpl: `{{ fromYamlArray . }}`, expect: `[error unmarshaling JSON: while decoding JSON: json: cannot unmarshal object into Go value of type []interface {}]`, vars: `hello: world`, + }, { + // This should never result in a network lookup. Regression for #7955 + tpl: `{{ lookup "v1" "Namespace" "" "unlikelynamespace99999999" }}`, + expect: `map[]`, + vars: `["one", "two"]`, }} for _, tt := range tests { diff --git a/pkg/engine/lookup_func.go b/pkg/engine/lookup_func.go index 2527954fa..20be9189e 100644 --- a/pkg/engine/lookup_func.go +++ b/pkg/engine/lookup_func.go @@ -32,8 +32,12 @@ import ( type lookupFunc = func(apiversion string, resource string, namespace string, name string) (map[string]interface{}, error) -// NewLookupFunction returns a function for looking up objects in the cluster. If the resource does not exist, no error -// is raised. +// NewLookupFunction returns a function for looking up objects in the cluster. +// +// If the resource does not exist, no error is raised. +// +// This function is considered deprecated, and will be renamed in Helm 4. It will no +// longer be a public function. func NewLookupFunction(config *rest.Config) lookupFunc { return func(apiversion string, resource string, namespace string, name string) (map[string]interface{}, error) { var client dynamic.ResourceInterface From 1cdd0a20488637c77d92e9a4844e89e8cefd578d Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Wed, 22 Apr 2020 15:33:01 -0700 Subject: [PATCH 072/281] fix(pkg/plugin): copy plugins directly to the data directory (#7962) Copy plugins from the cache rather than create a symlink. fixes: #7206 Signed-off-by: Adam Reese --- cmd/helm/load_plugins.go | 16 +------ cmd/helm/plugin_list.go | 6 ++- cmd/helm/plugin_uninstall.go | 2 +- cmd/helm/plugin_update.go | 2 +- pkg/plugin/installer/base.go | 9 +--- pkg/plugin/installer/http_installer.go | 28 ++++--------- pkg/plugin/installer/http_installer_test.go | 44 ++++++++++---------- pkg/plugin/installer/installer.go | 6 ++- pkg/plugin/installer/local_installer.go | 4 +- pkg/plugin/installer/local_installer_test.go | 4 +- pkg/plugin/installer/vcs_installer.go | 8 ++-- pkg/plugin/installer/vcs_installer_test.go | 36 ++++++++-------- pkg/plugin/plugin_test.go | 24 +++++------ 13 files changed, 83 insertions(+), 106 deletions(-) diff --git a/cmd/helm/load_plugins.go b/cmd/helm/load_plugins.go index e56feab40..a23a067fb 100644 --- a/cmd/helm/load_plugins.go +++ b/cmd/helm/load_plugins.go @@ -58,7 +58,7 @@ func loadPlugins(baseCmd *cobra.Command, out io.Writer) { return } - found, err := findPlugins(settings.PluginsDirectory) + found, err := plugin.FindPlugins(settings.PluginsDirectory) if err != nil { fmt.Fprintf(os.Stderr, "failed to load plugins: %s", err) return @@ -238,20 +238,6 @@ func manuallyProcessArgs(args []string) ([]string, []string) { return known, unknown } -// findPlugins returns a list of YAML files that describe plugins. -func findPlugins(plugdirs string) ([]*plugin.Plugin, error) { - found := []*plugin.Plugin{} - // Let's get all UNIXy and allow path separators - for _, p := range filepath.SplitList(plugdirs) { - matches, err := plugin.LoadAll(p) - if err != nil { - return matches, err - } - found = append(found, matches...) - } - return found, nil -} - // pluginCommand represents the optional completion.yaml file of a plugin type pluginCommand struct { Name string `json:"name"` diff --git a/cmd/helm/plugin_list.go b/cmd/helm/plugin_list.go index 0440b0b5e..49a454963 100644 --- a/cmd/helm/plugin_list.go +++ b/cmd/helm/plugin_list.go @@ -22,6 +22,8 @@ import ( "github.com/gosuri/uitable" "github.com/spf13/cobra" + + "helm.sh/helm/v3/pkg/plugin" ) func newPluginListCmd(out io.Writer) *cobra.Command { @@ -31,7 +33,7 @@ func newPluginListCmd(out io.Writer) *cobra.Command { Short: "list installed Helm plugins", RunE: func(cmd *cobra.Command, args []string) error { debug("pluginDirs: %s", settings.PluginsDirectory) - plugins, err := findPlugins(settings.PluginsDirectory) + plugins, err := plugin.FindPlugins(settings.PluginsDirectory) if err != nil { return err } @@ -51,7 +53,7 @@ func newPluginListCmd(out io.Writer) *cobra.Command { // Provide dynamic auto-completion for plugin names func compListPlugins(toComplete string) []string { var pNames []string - plugins, err := findPlugins(settings.PluginsDirectory) + plugins, err := plugin.FindPlugins(settings.PluginsDirectory) if err == nil { for _, p := range plugins { if strings.HasPrefix(p.Metadata.Name, toComplete) { diff --git a/cmd/helm/plugin_uninstall.go b/cmd/helm/plugin_uninstall.go index f703ddcfb..66cdaccdc 100644 --- a/cmd/helm/plugin_uninstall.go +++ b/cmd/helm/plugin_uninstall.go @@ -68,7 +68,7 @@ func (o *pluginUninstallOptions) complete(args []string) error { func (o *pluginUninstallOptions) run(out io.Writer) error { debug("loading installed plugins from %s", settings.PluginsDirectory) - plugins, err := findPlugins(settings.PluginsDirectory) + plugins, err := plugin.FindPlugins(settings.PluginsDirectory) if err != nil { return err } diff --git a/cmd/helm/plugin_update.go b/cmd/helm/plugin_update.go index a24e80518..23f840b4a 100644 --- a/cmd/helm/plugin_update.go +++ b/cmd/helm/plugin_update.go @@ -70,7 +70,7 @@ func (o *pluginUpdateOptions) complete(args []string) error { func (o *pluginUpdateOptions) run(out io.Writer) error { installer.Debug = settings.Debug debug("loading installed plugins from %s", settings.PluginsDirectory) - plugins, err := findPlugins(settings.PluginsDirectory) + plugins, err := plugin.FindPlugins(settings.PluginsDirectory) if err != nil { return err } diff --git a/pkg/plugin/installer/base.go b/pkg/plugin/installer/base.go index a8ec97416..dcc3ad644 100644 --- a/pkg/plugin/installer/base.go +++ b/pkg/plugin/installer/base.go @@ -16,7 +16,6 @@ limitations under the License. package installer // import "helm.sh/helm/v3/pkg/plugin/installer" import ( - "os" "path/filepath" "helm.sh/helm/v3/pkg/helmpath" @@ -31,13 +30,7 @@ func newBase(source string) base { return base{source} } -// link creates a symlink from the plugin source to the base path. -func (b *base) link(from string) error { - debug("symlinking %s to %s", from, b.Path()) - return os.Symlink(from, b.Path()) -} - -// Path is where the plugin will be symlinked to. +// Path is where the plugin will be installed. func (b *base) Path() string { if b.Source == "" { return "" diff --git a/pkg/plugin/installer/http_installer.go b/pkg/plugin/installer/http_installer.go index ea4ac7bcd..629bbec39 100644 --- a/pkg/plugin/installer/http_installer.go +++ b/pkg/plugin/installer/http_installer.go @@ -27,6 +27,7 @@ import ( "github.com/pkg/errors" + "helm.sh/helm/v3/internal/third_party/dep/fs" "helm.sh/helm/v3/pkg/cli" "helm.sh/helm/v3/pkg/getter" "helm.sh/helm/v3/pkg/helmpath" @@ -68,7 +69,6 @@ func NewExtractor(source string) (Extractor, error) { // NewHTTPInstaller creates a new HttpInstaller. func NewHTTPInstaller(source string) (*HTTPInstaller, error) { - key, err := cache.Key(source) if err != nil { return nil, err @@ -108,18 +108,16 @@ func stripPluginName(name string) string { } // Install downloads and extracts the tarball into the cache directory -// and creates a symlink to the plugin directory. +// and installs into the plugin directory. // // Implements Installer. func (i *HTTPInstaller) Install() error { - pluginData, err := i.getter.Get(i.Source) if err != nil { return err } - err = i.extractor.Extract(pluginData, i.CacheDir) - if err != nil { + if err := i.extractor.Extract(pluginData, i.CacheDir); err != nil { return err } @@ -132,7 +130,8 @@ func (i *HTTPInstaller) Install() error { return err } - return i.link(src) + debug("copying %s to %s", src, i.Path()) + return fs.CopyDir(src, i.Path()) } // Update updates a local repository @@ -141,12 +140,6 @@ func (i *HTTPInstaller) Update() error { return errors.Errorf("method Update() not implemented for HttpInstaller") } -// Override link because we want to use HttpInstaller.Path() not base.Path() -func (i *HTTPInstaller) link(from string) error { - debug("symlinking %s to %s", from, i.Path()) - return os.Symlink(from, i.Path()) -} - // Path is overridden because we want to join on the plugin name not the file name func (i HTTPInstaller) Path() string { if i.base.Source == "" { @@ -164,17 +157,16 @@ func (g *TarGzExtractor) Extract(buffer *bytes.Buffer, targetDir string) error { return err } - tarReader := tar.NewReader(uncompressedStream) - - os.MkdirAll(targetDir, 0755) + if err := os.MkdirAll(targetDir, 0755); err != nil { + return err + } + tarReader := tar.NewReader(uncompressedStream) for { header, err := tarReader.Next() - if err == io.EOF { break } - if err != nil { return err } @@ -200,7 +192,5 @@ func (g *TarGzExtractor) Extract(buffer *bytes.Buffer, targetDir string) error { return errors.Errorf("unknown type: %b in %s", header.Typeflag, header.Name) } } - return nil - } diff --git a/pkg/plugin/installer/http_installer_test.go b/pkg/plugin/installer/http_installer_test.go index cfa2e4cbe..b496a1b01 100644 --- a/pkg/plugin/installer/http_installer_test.go +++ b/pkg/plugin/installer/http_installer_test.go @@ -73,13 +73,13 @@ func TestHTTPInstaller(t *testing.T) { i, err := NewForSource(source, "0.0.1") if err != nil { - t.Errorf("unexpected error: %s", err) + t.Fatalf("unexpected error: %s", err) } // ensure a HTTPInstaller was returned httpInstaller, ok := i.(*HTTPInstaller) if !ok { - t.Error("expected a HTTPInstaller") + t.Fatal("expected a HTTPInstaller") } // inject fake http client responding with minimal plugin tarball @@ -94,17 +94,17 @@ func TestHTTPInstaller(t *testing.T) { // install the plugin if err := Install(i); err != nil { - t.Error(err) + t.Fatal(err) } if i.Path() != helmpath.DataPath("plugins", "fake-plugin") { - t.Errorf("expected path '$XDG_CONFIG_HOME/helm/plugins/fake-plugin', got %q", i.Path()) + t.Fatalf("expected path '$XDG_CONFIG_HOME/helm/plugins/fake-plugin', got %q", i.Path()) } // Install again to test plugin exists error if err := Install(i); err == nil { - t.Error("expected error for plugin exists, got none") + t.Fatal("expected error for plugin exists, got none") } else if err.Error() != "plugin already exists" { - t.Errorf("expected error for plugin exists, got (%v)", err) + t.Fatalf("expected error for plugin exists, got (%v)", err) } } @@ -119,13 +119,13 @@ func TestHTTPInstallerNonExistentVersion(t *testing.T) { i, err := NewForSource(source, "0.0.2") if err != nil { - t.Errorf("unexpected error: %s", err) + t.Fatalf("unexpected error: %s", err) } // ensure a HTTPInstaller was returned httpInstaller, ok := i.(*HTTPInstaller) if !ok { - t.Error("expected a HTTPInstaller") + t.Fatal("expected a HTTPInstaller") } // inject fake http client responding with error @@ -135,7 +135,7 @@ func TestHTTPInstallerNonExistentVersion(t *testing.T) { // attempt to install the plugin if err := Install(i); err == nil { - t.Error("expected error from http client") + t.Fatal("expected error from http client") } } @@ -150,13 +150,13 @@ func TestHTTPInstallerUpdate(t *testing.T) { i, err := NewForSource(source, "0.0.1") if err != nil { - t.Errorf("unexpected error: %s", err) + t.Fatalf("unexpected error: %s", err) } // ensure a HTTPInstaller was returned httpInstaller, ok := i.(*HTTPInstaller) if !ok { - t.Error("expected a HTTPInstaller") + t.Fatal("expected a HTTPInstaller") } // inject fake http client responding with minimal plugin tarball @@ -171,15 +171,15 @@ func TestHTTPInstallerUpdate(t *testing.T) { // install the plugin before updating if err := Install(i); err != nil { - t.Error(err) + t.Fatal(err) } if i.Path() != helmpath.DataPath("plugins", "fake-plugin") { - t.Errorf("expected path '$XDG_CONFIG_HOME/helm/plugins/fake-plugin', got %q", i.Path()) + t.Fatalf("expected path '$XDG_CONFIG_HOME/helm/plugins/fake-plugin', got %q", i.Path()) } // Update plugin, should fail because it is not implemented if err := Update(i); err == nil { - t.Error("update method not implemented for http installer") + t.Fatal("update method not implemented for http installer") } } @@ -240,29 +240,27 @@ func TestExtract(t *testing.T) { } if err = extractor.Extract(&buf, tempDir); err != nil { - t.Errorf("Did not expect error but got error: %v", err) + t.Fatalf("Did not expect error but got error: %v", err) } pluginYAMLFullPath := filepath.Join(tempDir, "plugin.yaml") if info, err := os.Stat(pluginYAMLFullPath); err != nil { if os.IsNotExist(err) { - t.Errorf("Expected %s to exist but doesn't", pluginYAMLFullPath) - } else { - t.Error(err) + t.Fatalf("Expected %s to exist but doesn't", pluginYAMLFullPath) } + t.Fatal(err) } else if info.Mode().Perm() != 0600 { - t.Errorf("Expected %s to have 0600 mode it but has %o", pluginYAMLFullPath, info.Mode().Perm()) + t.Fatalf("Expected %s to have 0600 mode it but has %o", pluginYAMLFullPath, info.Mode().Perm()) } readmeFullPath := filepath.Join(tempDir, "README.md") if info, err := os.Stat(readmeFullPath); err != nil { if os.IsNotExist(err) { - t.Errorf("Expected %s to exist but doesn't", readmeFullPath) - } else { - t.Error(err) + t.Fatalf("Expected %s to exist but doesn't", readmeFullPath) } + t.Fatal(err) } else if info.Mode().Perm() != 0777 { - t.Errorf("Expected %s to have 0777 mode it but has %o", readmeFullPath, info.Mode().Perm()) + t.Fatalf("Expected %s to have 0777 mode it but has %o", readmeFullPath, info.Mode().Perm()) } } diff --git a/pkg/plugin/installer/installer.go b/pkg/plugin/installer/installer.go index 14a02a87e..65c61cd7d 100644 --- a/pkg/plugin/installer/installer.go +++ b/pkg/plugin/installer/installer.go @@ -17,6 +17,7 @@ package installer import ( "fmt" + "log" "os" "path/filepath" "strings" @@ -103,9 +104,10 @@ func isPlugin(dirname string) bool { return err == nil } +var logger = log.New(os.Stderr, "[debug] ", log.Lshortfile) + func debug(format string, args ...interface{}) { if Debug { - format = fmt.Sprintf("[debug] %s\n", format) - fmt.Printf(format, args...) + logger.Output(2, fmt.Sprintf(format, args...)) } } diff --git a/pkg/plugin/installer/local_installer.go b/pkg/plugin/installer/local_installer.go index 662ef5b29..c92bc3fb0 100644 --- a/pkg/plugin/installer/local_installer.go +++ b/pkg/plugin/installer/local_installer.go @@ -16,6 +16,7 @@ limitations under the License. package installer // import "helm.sh/helm/v3/pkg/plugin/installer" import ( + "os" "path/filepath" "github.com/pkg/errors" @@ -45,7 +46,8 @@ func (i *LocalInstaller) Install() error { if !isPlugin(i.Source) { return ErrMissingMetadata } - return i.link(i.Source) + debug("symlinking %s to %s", i.Source, i.Path()) + return os.Symlink(i.Source, i.Path()) } // Update updates a local repository diff --git a/pkg/plugin/installer/local_installer_test.go b/pkg/plugin/installer/local_installer_test.go index d11e44860..3d9607331 100644 --- a/pkg/plugin/installer/local_installer_test.go +++ b/pkg/plugin/installer/local_installer_test.go @@ -40,7 +40,7 @@ func TestLocalInstaller(t *testing.T) { source := "../testdata/plugdir/echo" i, err := NewForSource(source, "") if err != nil { - t.Errorf("unexpected error: %s", err) + t.Fatalf("unexpected error: %s", err) } if err := Install(i); err != nil { @@ -48,6 +48,6 @@ func TestLocalInstaller(t *testing.T) { } if i.Path() != helmpath.DataPath("plugins", "echo") { - t.Errorf("expected path '$XDG_CONFIG_HOME/helm/plugins/helm-env', got %q", i.Path()) + t.Fatalf("expected path '$XDG_CONFIG_HOME/helm/plugins/helm-env', got %q", i.Path()) } } diff --git a/pkg/plugin/installer/vcs_installer.go b/pkg/plugin/installer/vcs_installer.go index 1a5d74cca..f7df5b322 100644 --- a/pkg/plugin/installer/vcs_installer.go +++ b/pkg/plugin/installer/vcs_installer.go @@ -23,6 +23,7 @@ import ( "github.com/Masterminds/vcs" "github.com/pkg/errors" + "helm.sh/helm/v3/internal/third_party/dep/fs" "helm.sh/helm/v3/pkg/helmpath" "helm.sh/helm/v3/pkg/plugin/cache" ) @@ -43,7 +44,7 @@ func existingVCSRepo(location string) (Installer, error) { Repo: repo, base: newBase(repo.Remote()), } - return i, err + return i, nil } // NewVCSInstaller creates a new VCSInstaller. @@ -65,7 +66,7 @@ func NewVCSInstaller(source, version string) (*VCSInstaller, error) { return i, err } -// Install clones a remote repository and creates a symlink to the plugin directory. +// Install clones a remote repository and installs into the plugin directory. // // Implements Installer. func (i *VCSInstaller) Install() error { @@ -87,7 +88,8 @@ func (i *VCSInstaller) Install() error { return ErrMissingMetadata } - return i.link(i.Repo.LocalPath()) + debug("copying %s to %s", i.Repo.LocalPath(), i.Path()) + return fs.CopyDir(i.Repo.LocalPath(), i.Path()) } // Update updates a remote repository diff --git a/pkg/plugin/installer/vcs_installer_test.go b/pkg/plugin/installer/vcs_installer_test.go index ce1ee609e..b8dc6b1e2 100644 --- a/pkg/plugin/installer/vcs_installer_test.go +++ b/pkg/plugin/installer/vcs_installer_test.go @@ -80,24 +80,24 @@ func TestVCSInstaller(t *testing.T) { t.Fatal(err) } if repo.current != "0.1.1" { - t.Errorf("expected version '0.1.1', got %q", repo.current) + t.Fatalf("expected version '0.1.1', got %q", repo.current) } if i.Path() != helmpath.DataPath("plugins", "helm-env") { - t.Errorf("expected path '$XDG_CONFIG_HOME/helm/plugins/helm-env', got %q", i.Path()) + t.Fatalf("expected path '$XDG_CONFIG_HOME/helm/plugins/helm-env', got %q", i.Path()) } // Install again to test plugin exists error if err := Install(i); err == nil { - t.Error("expected error for plugin exists, got none") + t.Fatalf("expected error for plugin exists, got none") } else if err.Error() != "plugin already exists" { - t.Errorf("expected error for plugin exists, got (%v)", err) + t.Fatalf("expected error for plugin exists, got (%v)", err) } // Testing FindSource method, expect error because plugin code is not a cloned repository if _, err := FindSource(i.Path()); err == nil { - t.Error("expected error for inability to find plugin source, got none") + t.Fatalf("expected error for inability to find plugin source, got none") } else if err.Error() != "cannot get information about plugin source" { - t.Errorf("expected error for inability to find plugin source, got (%v)", err) + t.Fatalf("expected error for inability to find plugin source, got (%v)", err) } } @@ -113,15 +113,14 @@ func TestVCSInstallerNonExistentVersion(t *testing.T) { } // ensure a VCSInstaller was returned - _, ok := i.(*VCSInstaller) - if !ok { + if _, ok := i.(*VCSInstaller); !ok { t.Fatal("expected a VCSInstaller") } if err := Install(i); err == nil { - t.Error("expected error for version does not exists, got none") + t.Fatalf("expected error for version does not exists, got none") } else if err.Error() != fmt.Sprintf("requested version %q does not exist for plugin %q", version, source) { - t.Errorf("expected error for version does not exists, got (%v)", err) + t.Fatalf("expected error for version does not exists, got (%v)", err) } } func TestVCSInstallerUpdate(t *testing.T) { @@ -135,8 +134,7 @@ func TestVCSInstallerUpdate(t *testing.T) { } // ensure a VCSInstaller was returned - _, ok := i.(*VCSInstaller) - if !ok { + if _, ok := i.(*VCSInstaller); !ok { t.Fatal("expected a VCSInstaller") } @@ -157,7 +155,9 @@ func TestVCSInstallerUpdate(t *testing.T) { t.Fatal(err) } - repoRemote := pluginInfo.(*VCSInstaller).Repo.Remote() + vcsInstaller := pluginInfo.(*VCSInstaller) + + repoRemote := vcsInstaller.Repo.Remote() if repoRemote != source { t.Fatalf("invalid source found, expected %q got %q", source, repoRemote) } @@ -168,12 +168,14 @@ func TestVCSInstallerUpdate(t *testing.T) { } // Test update failure - os.Remove(filepath.Join(i.Path(), "plugin.yaml")) + if err := os.Remove(filepath.Join(vcsInstaller.Repo.LocalPath(), "plugin.yaml")); err != nil { + t.Fatal(err) + } // Testing update for error - if err := Update(i); err == nil { - t.Error("expected error for plugin modified, got none") + if err := Update(vcsInstaller); err == nil { + t.Fatalf("expected error for plugin modified, got none") } else if err.Error() != "plugin repo was modified" { - t.Errorf("expected error for plugin modified, got (%v)", err) + t.Fatalf("expected error for plugin modified, got (%v)", err) } } diff --git a/pkg/plugin/plugin_test.go b/pkg/plugin/plugin_test.go index 7bbc3b442..a869255c4 100644 --- a/pkg/plugin/plugin_test.go +++ b/pkg/plugin/plugin_test.go @@ -28,14 +28,14 @@ import ( func checkCommand(p *Plugin, extraArgs []string, osStrCmp string, t *testing.T) { cmd, args, err := p.PrepareCommand(extraArgs) if err != nil { - t.Errorf(err.Error()) + t.Fatal(err) } if cmd != "echo" { - t.Errorf("Expected echo, got %q", cmd) + t.Fatalf("Expected echo, got %q", cmd) } if l := len(args); l != 5 { - t.Errorf("expected 5 args, got %d", l) + t.Fatalf("expected 5 args, got %d", l) } expect := []string{"-n", osStrCmp, "--debug", "--foo", "bar"} @@ -49,13 +49,13 @@ func checkCommand(p *Plugin, extraArgs []string, osStrCmp string, t *testing.T) p.Metadata.IgnoreFlags = true cmd, args, err = p.PrepareCommand(extraArgs) if err != nil { - t.Errorf(err.Error()) + t.Fatal(err) } if cmd != "echo" { - t.Errorf("Expected echo, got %q", cmd) + t.Fatalf("Expected echo, got %q", cmd) } if l := len(args); l != 2 { - t.Errorf("expected 2 args, got %d", l) + t.Fatalf("expected 2 args, got %d", l) } expect = []string{"-n", osStrCmp} for i := 0; i < len(args); i++ { @@ -155,7 +155,7 @@ func TestNoPrepareCommand(t *testing.T) { _, _, err := p.PrepareCommand(argv) if err == nil { - t.Errorf("Expected error to be returned") + t.Fatalf("Expected error to be returned") } } @@ -172,7 +172,7 @@ func TestNoMatchPrepareCommand(t *testing.T) { argv := []string{"--debug", "--foo", "bar"} if _, _, err := p.PrepareCommand(argv); err == nil { - t.Errorf("Expected error to be returned") + t.Fatalf("Expected error to be returned") } } @@ -184,7 +184,7 @@ func TestLoadDir(t *testing.T) { } if plug.Dir != dirname { - t.Errorf("Expected dir %q, got %q", dirname, plug.Dir) + t.Fatalf("Expected dir %q, got %q", dirname, plug.Dir) } expect := &Metadata{ @@ -200,7 +200,7 @@ func TestLoadDir(t *testing.T) { } if !reflect.DeepEqual(expect, plug.Metadata) { - t.Errorf("Expected plugin metadata %v, got %v", expect, plug.Metadata) + t.Fatalf("Expected plugin metadata %v, got %v", expect, plug.Metadata) } } @@ -212,7 +212,7 @@ func TestDownloader(t *testing.T) { } if plug.Dir != dirname { - t.Errorf("Expected dir %q, got %q", dirname, plug.Dir) + t.Fatalf("Expected dir %q, got %q", dirname, plug.Dir) } expect := &Metadata{ @@ -230,7 +230,7 @@ func TestDownloader(t *testing.T) { } if !reflect.DeepEqual(expect, plug.Metadata) { - t.Errorf("Expected metadata %v, got %v", expect, plug.Metadata) + t.Fatalf("Expected metadata %v, got %v", expect, plug.Metadata) } } From 6aefbdcf38e14998d0bcb24030061822b1e8888d Mon Sep 17 00:00:00 2001 From: David Pait Date: Wed, 22 Apr 2020 18:55:26 -0400 Subject: [PATCH 073/281] Helm upgrades with --reuse-values and nil user values -- with tests (#7959) * return the new values if modifications dont yet exist Signed-off-by: David Pait * fix tests Signed-off-by: David Pait * removed outter if statement as its not needed now Signed-off-by: David Pait --- pkg/chartutil/coalesce.go | 6 +++- pkg/chartutil/coalesce_test.go | 53 ++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/pkg/chartutil/coalesce.go b/pkg/chartutil/coalesce.go index 94b7f35fa..1d3d45e99 100644 --- a/pkg/chartutil/coalesce.go +++ b/pkg/chartutil/coalesce.go @@ -175,7 +175,11 @@ func coalesceValues(c *chart.Chart, v map[string]interface{}) { // // dest is considered authoritative. func CoalesceTables(dst, src map[string]interface{}) map[string]interface{} { - if dst == nil || src == nil { + // When --reuse-values is set but there are no modifications yet, return new values + if src == nil { + return dst + } + if dst == nil { return src } // Because dest has higher precedence than src, dest values override src diff --git a/pkg/chartutil/coalesce_test.go b/pkg/chartutil/coalesce_test.go index dc1017385..2a3d848fa 100644 --- a/pkg/chartutil/coalesce_test.go +++ b/pkg/chartutil/coalesce_test.go @@ -211,4 +211,57 @@ func TestCoalesceTables(t *testing.T) { if _, ok = dst["hole"]; ok { t.Error("The hole still exists.") } + + dst2 := map[string]interface{}{ + "name": "Ishmael", + "address": map[string]interface{}{ + "street": "123 Spouter Inn Ct.", + "city": "Nantucket", + "country": "US", + }, + "details": map[string]interface{}{ + "friends": []string{"Tashtego"}, + }, + "boat": "pequod", + "hole": "black", + } + + // What we expect is that anything in dst should have all values set, + // this happens when the --reuse-values flag is set but the chart has no modifications yet + CoalesceTables(dst2, nil) + + if dst2["name"] != "Ishmael" { + t.Errorf("Unexpected name: %s", dst2["name"]) + } + + addr2, ok := dst2["address"].(map[string]interface{}) + if !ok { + t.Fatal("Address went away.") + } + + if addr2["street"].(string) != "123 Spouter Inn Ct." { + t.Errorf("Unexpected address: %v", addr2["street"]) + } + + if addr2["city"].(string) != "Nantucket" { + t.Errorf("Unexpected city: %v", addr2["city"]) + } + + if addr2["country"].(string) != "US" { + t.Errorf("Unexpected Country: %v", addr2["country"]) + } + + if det2, ok := dst2["details"].(map[string]interface{}); !ok { + t.Fatalf("Details is the wrong type: %v", dst2["details"]) + } else if _, ok := det2["friends"]; !ok { + t.Error("Could not find your friends. Maybe you don't have any. :-(") + } + + if dst2["boat"].(string) != "pequod" { + t.Errorf("Expected boat string, got %v", dst2["boat"]) + } + + if dst2["hole"].(string) != "black" { + t.Errorf("Expected hole string, got %v", dst2["boat"]) + } } From 27ebfa8c561e758b60872b9bb081253036e559ff Mon Sep 17 00:00:00 2001 From: Thomas FREYSS Date: Thu, 23 Apr 2020 14:47:08 +0200 Subject: [PATCH 074/281] fix(*): remove bom in utf files when loading chart files (#6081) Removes the BOM prefix if present, in read files before processing the data. Affects the following pkg: - pkg/chart/loader: directory and archive loader - internal/ignore: when loading .helmignore file Signed-off-by: Thomas FREYSS --- internal/ignore/rules.go | 13 +++++- pkg/chart/loader/archive.go | 4 +- pkg/chart/loader/directory.go | 5 +++ pkg/chart/loader/load_test.go | 40 ++++++++++++++++++ .../loader/testdata/frobnitz_with_bom.tgz | Bin 0 -> 3523 bytes .../testdata/frobnitz_with_bom/.helmignore | 1 + .../testdata/frobnitz_with_bom/Chart.lock | 8 ++++ .../testdata/frobnitz_with_bom/Chart.yaml | 27 ++++++++++++ .../testdata/frobnitz_with_bom/INSTALL.txt | 1 + .../loader/testdata/frobnitz_with_bom/LICENSE | 1 + .../testdata/frobnitz_with_bom/README.md | 11 +++++ .../frobnitz_with_bom/charts/_ignore_me | 1 + .../charts/alpine/Chart.yaml | 5 +++ .../frobnitz_with_bom/charts/alpine/README.md | 9 ++++ .../charts/alpine/charts/mast1/Chart.yaml | 5 +++ .../charts/alpine/charts/mast1/values.yaml | 4 ++ .../charts/alpine/charts/mast2-0.1.0.tgz | Bin 0 -> 252 bytes .../charts/alpine/templates/alpine-pod.yaml | 14 ++++++ .../charts/alpine/values.yaml | 2 + .../charts/mariner-4.3.2.tgz | Bin 0 -> 967 bytes .../testdata/frobnitz_with_bom/docs/README.md | 1 + .../testdata/frobnitz_with_bom/icon.svg | 8 ++++ .../testdata/frobnitz_with_bom/ignore/me.txt | 0 .../frobnitz_with_bom/templates/template.tpl | 1 + .../testdata/frobnitz_with_bom/values.yaml | 6 +++ 25 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 pkg/chart/loader/testdata/frobnitz_with_bom.tgz create mode 100644 pkg/chart/loader/testdata/frobnitz_with_bom/.helmignore create mode 100644 pkg/chart/loader/testdata/frobnitz_with_bom/Chart.lock create mode 100644 pkg/chart/loader/testdata/frobnitz_with_bom/Chart.yaml create mode 100644 pkg/chart/loader/testdata/frobnitz_with_bom/INSTALL.txt create mode 100644 pkg/chart/loader/testdata/frobnitz_with_bom/LICENSE create mode 100644 pkg/chart/loader/testdata/frobnitz_with_bom/README.md create mode 100644 pkg/chart/loader/testdata/frobnitz_with_bom/charts/_ignore_me create mode 100644 pkg/chart/loader/testdata/frobnitz_with_bom/charts/alpine/Chart.yaml create mode 100644 pkg/chart/loader/testdata/frobnitz_with_bom/charts/alpine/README.md create mode 100644 pkg/chart/loader/testdata/frobnitz_with_bom/charts/alpine/charts/mast1/Chart.yaml create mode 100644 pkg/chart/loader/testdata/frobnitz_with_bom/charts/alpine/charts/mast1/values.yaml create mode 100644 pkg/chart/loader/testdata/frobnitz_with_bom/charts/alpine/charts/mast2-0.1.0.tgz create mode 100644 pkg/chart/loader/testdata/frobnitz_with_bom/charts/alpine/templates/alpine-pod.yaml create mode 100644 pkg/chart/loader/testdata/frobnitz_with_bom/charts/alpine/values.yaml create mode 100644 pkg/chart/loader/testdata/frobnitz_with_bom/charts/mariner-4.3.2.tgz create mode 100644 pkg/chart/loader/testdata/frobnitz_with_bom/docs/README.md create mode 100644 pkg/chart/loader/testdata/frobnitz_with_bom/icon.svg create mode 100644 pkg/chart/loader/testdata/frobnitz_with_bom/ignore/me.txt create mode 100644 pkg/chart/loader/testdata/frobnitz_with_bom/templates/template.tpl create mode 100644 pkg/chart/loader/testdata/frobnitz_with_bom/values.yaml diff --git a/internal/ignore/rules.go b/internal/ignore/rules.go index 9049aff0d..a80923baf 100644 --- a/internal/ignore/rules.go +++ b/internal/ignore/rules.go @@ -18,6 +18,7 @@ package ignore import ( "bufio" + "bytes" "io" "log" "os" @@ -65,8 +66,18 @@ func Parse(file io.Reader) (*Rules, error) { r := &Rules{patterns: []*pattern{}} s := bufio.NewScanner(file) + currentLine := 0 + utf8bom := []byte{0xEF, 0xBB, 0xBF} for s.Scan() { - if err := r.parseRule(s.Text()); err != nil { + scannedBytes := s.Bytes() + // We trim UTF8 BOM + if currentLine == 0 { + scannedBytes = bytes.TrimPrefix(scannedBytes, utf8bom) + } + line := string(scannedBytes) + currentLine++ + + if err := r.parseRule(line); err != nil { return r, err } } diff --git a/pkg/chart/loader/archive.go b/pkg/chart/loader/archive.go index 7e187a170..8b38cb89f 100644 --- a/pkg/chart/loader/archive.go +++ b/pkg/chart/loader/archive.go @@ -173,7 +173,9 @@ func LoadArchiveFiles(in io.Reader) ([]*BufferedFile, error) { return nil, err } - files = append(files, &BufferedFile{Name: n, Data: b.Bytes()}) + data := bytes.TrimPrefix(b.Bytes(), utf8bom) + + files = append(files, &BufferedFile{Name: n, Data: data}) b.Reset() } diff --git a/pkg/chart/loader/directory.go b/pkg/chart/loader/directory.go index a12c5158e..bbe543870 100644 --- a/pkg/chart/loader/directory.go +++ b/pkg/chart/loader/directory.go @@ -17,6 +17,7 @@ limitations under the License. package loader import ( + "bytes" "fmt" "io/ioutil" "os" @@ -30,6 +31,8 @@ import ( "helm.sh/helm/v3/pkg/chart" ) +var utf8bom = []byte{0xEF, 0xBB, 0xBF} + // DirLoader loads a chart from a directory type DirLoader string @@ -104,6 +107,8 @@ func LoadDir(dir string) (*chart.Chart, error) { return errors.Wrapf(err, "error reading %s", n) } + data = bytes.TrimPrefix(data, utf8bom) + files = append(files, &BufferedFile{Name: n, Data: data}) return nil } diff --git a/pkg/chart/loader/load_test.go b/pkg/chart/loader/load_test.go index 26513d359..3fa6bba0d 100644 --- a/pkg/chart/loader/load_test.go +++ b/pkg/chart/loader/load_test.go @@ -85,6 +85,38 @@ func TestLoadDirWithSymlink(t *testing.T) { verifyDependenciesLock(t, c) } +func TestLoadDirWithUTFBOM(t *testing.T) { + l, err := Loader("testdata/frobnitz_with_bom") + if err != nil { + t.Fatalf("Failed to load testdata: %s", err) + } + c, err := l.Load() + if err != nil { + t.Fatalf("Failed to load testdata: %s", err) + } + verifyFrobnitz(t, c) + verifyChart(t, c) + verifyDependencies(t, c) + verifyDependenciesLock(t, c) + verifyBomStripped(t, c.Files) +} + +func TestLoadArchiveWithUTFBOM(t *testing.T) { + l, err := Loader("testdata/frobnitz_with_bom.tgz") + if err != nil { + t.Fatalf("Failed to load testdata: %s", err) + } + c, err := l.Load() + if err != nil { + t.Fatalf("Failed to load testdata: %s", err) + } + verifyFrobnitz(t, c) + verifyChart(t, c) + verifyDependencies(t, c) + verifyDependenciesLock(t, c) + verifyBomStripped(t, c.Files) +} + func TestLoadV1(t *testing.T) { l, err := Loader("testdata/frobnitz.v1") if err != nil { @@ -465,3 +497,11 @@ func verifyChartFileAndTemplate(t *testing.T, c *chart.Chart, name string) { } } } + +func verifyBomStripped(t *testing.T, files []*chart.File) { + for _, file := range files { + if bytes.HasPrefix(file.Data, utf8bom) { + t.Errorf("Byte Order Mark still present in processed file %s", file.Name) + } + } +} diff --git a/pkg/chart/loader/testdata/frobnitz_with_bom.tgz b/pkg/chart/loader/testdata/frobnitz_with_bom.tgz new file mode 100644 index 0000000000000000000000000000000000000000..be0cd027db34d392a875cf8151642f9922016dbd GIT binary patch literal 3523 zcmV;!4LtH6iwFpsexY6f17>n>Vs2@4dS7>GbZB2l` znyB~YxdYx5VfMWn1PB3&iueFgG=d@A)?MdMF5{FG{)R-w{q^;#sSRI4>wKcMw~Q(D5t@dOKi zA7!-`+)B<<`fn83E%ZMmB{eNBIT_~*crVotZ@BcY(W;T0Qmaxb{D8ts z_1_YIaQ)Y8DN8d^96jbvO|I-O_N{vddll(tl;3oR_>LlRN{?j%e|Fv3;H2(Vp zjr~7>TstE)F*Jz==xkxQcDaEBPcas0JpMhRCI6F$#3!btCVH_BkNj6Fn%jSBwZwm) z0P)|Y@W$+yxc)~$Oa8};_P(7k)84AX1OJsuWy|=lP9^c*7eM?ctW-K=IW)ybf;^?n zLYQGBaQ6!t2{|K6S$Q$J!BlXk z1%gDHgN?IT7z0Dvn`Gh`8*7BF8cjTJNEv94Q{pM$OwytaT-rzO|XDx3G{X%omY7TZ4=r_AR!ZE#RS*2Gl_&&Xd$7+NWmuW zhM=wJeTh%l@bVS75?5g2%?2O(#tjrbdahA{W`Y$I(5yrauEtfZD)CNtW(loE%kkf- zOK(O1+Tj1|)GEpU_XTdM|4iqyK&JWjSAjg)|HuGnHvc1OY5w;K-2OjjVr(=C3=lZ= zjxOzDXqILO+5!cF<_vI)XPD*)$no^E>` zp6q|ULSH}rYjnc+uh;05690XHKC=V-*8OGC6MpT_yp(I|Hu%rm*Cb8IC=QPas9e!g zE*rY@z}YiL{@3fl$KUr)4oMhwctFL(W9+cYPrd&7@VQucQrz6}pR6i=wfI7OW#=dR zl-zrtc6^sPYd)(k8v2F-3|d?H9;U1~b97$FKSwQ@U3~sXR(4Ic!?Eegz<*C4r|x>m zt||rr?GoFaT~&IvU+vNAeVx1X%}Bp@RQHolbm==PEh_f&>J_6O)jjjFpBDkmn}YS-*cw3 z@jJr|^B1b3Ws|CtP0PTEeA()Us))BgExwi;*e`x_yN?0~x9i%m=t@ce@nBbyyV$?0 zN`7t{{$$@b3$~`kc6w#~&Q7^I?`QqGO@H&ARkOg37tLSS_-*f(UDa!?HNHzi;B!@R zC7s`yIVgGXlzplx{rlL)Jd!zNzy;ZdJ3CG~Yc4smrTox)UnKV|sQg(ztTZ$r5rFF|=V;9}m_XP*lVf*QUz1}>X+1b{i>J`dw<{C%& z$ZNx%yFS0tv3p^MW6J_~{+iw6zhH_h*eQ|FG7EE2b$H52uYt`~^ zvE2*zC6Pt{JzVm)={wHV^hhWxKU}B@3i_da_#VT_x~wEe$+nlfZ@e73bniP?9Fa3; z7iL^sS>p(Oy`v#Eur}l8`Tm>c8l%gqHQ|@%oZK{?So%OropsFQQKx1q7f-IB>GHtv zsM=~vy1nZCi{C%J{o{?P+AAyLd%XAX?g{y7)!Oa{tM;#hpPpUa-8isn+LQ^E70btl z_KqKTsP_buvhZn)N3<#&3|#?>WUa^<`R4Q)!Kx=me@8v<~X?ZskK{c z_Z~aY>*DE66`xg9z=KCGUO!d>@wMumYp&1I>c2@g+o!Dgp!S0ek1hY<+>nUJ zA}>{ky-;>&UY$*IKyBN<^>l~Ay?ahg+g!G8rFFx?KABrCo>yP~c5cb~k*`Kv8NcLx zCUw``{YMW@I(_!EwXbCq_BdYh$imIfU!7L_Qqd(H~ z@yh46`}MmG{@--`cRHC~aRXX!|Lc?r$^Q2VJn7#X3xS^Oe~rF*{9muuO8dWj0=NC| z-tCc3a3BXF!;j#Rfp6m|OOEIU7#Xvfu#g~2h`#|N3sJf&5S4`sTS6en;vp&m-RI<4 zfTUPx6aq#lrx@Q8f`)kzedj2#A}d1z7CPf_KO|tr&sHw z{a-%8FVw%8;CN+QdqJ!9uTp4~YDxdTK&$lMMq|L^{IAwGoBvg6wG{vN32rw3iwbW# z{l_^Mc=o?qQBeGD*n5Cu+|O$p7^g)Hn;y(T4DZ|`2Xue zJpYl<(s>pYj?01@ZTZQF8=f4F(#!-e#0CT(To6crMw(!uxRu1&Ly-4QvB)iFgvCND z!ExeJA^K`Z-hRCU&Iy@?OduLyVm@iO@I6|=e^t0RxVL^S&=ddF`uhFHN1(&}Vi)H}S&J-){hsG+JRy8m>#WM`#V|bOo8JrN*a5=tiWc>xPe|pCl}#J#7>d zE!Y8mr}wTc{YcTx|4@bf`KY*>lKm58Kj8SYTPD9fI80+6^3}7&HTWQYRZ($T*F7J+ zri)+D{`h=t%!!h!Z#Mqp?3{NN2KvP=g=_B#8r!dKa^1XBL*EJ;5wc?A?r}9mPX#WG z$=$zymSW=ikmGM{=wH3;(^sdQEX6-@6h^H4uKXv>pV_Y?H=P(;-Ah)TXbkJFN|_&o z_XcD7f3jtQq4T513l+8z(@HxWnsoh{B|kg*e{rSo?{&|dFAp*vJQ0K*)N!O}Z>C?T zJN)AB5_sZ&Lrk~rb+p0%Qwsc-{J&oxzg+*#BI&KD!_)Y$Q>vQTe``h8bg=`*WbQW-Q`ECnKu76Z6TBGW30h*i3loto`e&nGC}wS$^6qbEcy zaS|p{jYl{)QM;DVBEbYqZlEl3j)u^Rg<%nh0@%bmWczyJe;ZxCCh)|6g+|kS{YRyh z`0o>l{-3bGDy+u}UD*}?!OZq>XBUvXvfndW@?SXEf^&H}-mAkC|FsS4zpmeZLzmL| zPoE%aV1b!-ANq;Lgfm0~V`NcC{{SUexJYyrjk(T0<>%+)`D&bDbL1+dH#thfoMN{W z5obSVX$3T8$-R-VG9n^EE~a9Qk0Ci*1oA18H$`JAtqwyIK9ytQU0*0N8bc-mkHw%| zQ9`&nCegKlL+Hm*a`BdsCTvF$#j`0E$%6t^0tW2jUsv%OMMLR2be&DnbTnom^6Z$r zX&D?ug_B|-O08Im#zQGAB!xnvgclNX7mcVb`R@&nuPIu;{)?;tjpYCN0=LqC{Z=X7 zdRu3KJ==fH&;ROmdMW-za0w`V9Evj@UhfyrdZ5E3^B z)JKRgaS#y?2D;KD|M70d&3^xbW{kPss>9>@pP~i-RT{m-e_sIc|2OwD6C{;`T)s#& x%AjG3BBk*~f&>W?BuJ1TL4pJc5+q2FAVGoz2@)hokRYKE{tus-sa61Z0036V1Lgn# literal 0 HcmV?d00001 diff --git a/pkg/chart/loader/testdata/frobnitz_with_bom/.helmignore b/pkg/chart/loader/testdata/frobnitz_with_bom/.helmignore new file mode 100644 index 000000000..7a4b92da2 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz_with_bom/.helmignore @@ -0,0 +1 @@ +ignore/ diff --git a/pkg/chart/loader/testdata/frobnitz_with_bom/Chart.lock b/pkg/chart/loader/testdata/frobnitz_with_bom/Chart.lock new file mode 100644 index 000000000..ed43b227f --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz_with_bom/Chart.lock @@ -0,0 +1,8 @@ +dependencies: + - name: alpine + version: "0.1.0" + repository: https://example.com/charts + - name: mariner + version: "4.3.2" + repository: https://example.com/charts +digest: invalid diff --git a/pkg/chart/loader/testdata/frobnitz_with_bom/Chart.yaml b/pkg/chart/loader/testdata/frobnitz_with_bom/Chart.yaml new file mode 100644 index 000000000..21b21f0b5 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz_with_bom/Chart.yaml @@ -0,0 +1,27 @@ +apiVersion: v1 +name: frobnitz +description: This is a frobnitz. +version: "1.2.3" +keywords: + - frobnitz + - sprocket + - dodad +maintainers: + - name: The Helm Team + email: helm@example.com + - name: Someone Else + email: nobody@example.com +sources: + - https://example.com/foo/bar +home: http://example.com +icon: https://example.com/64x64.png +annotations: + extrakey: extravalue + anotherkey: anothervalue +dependencies: + - name: alpine + version: "0.1.0" + repository: https://example.com/charts + - name: mariner + version: "4.3.2" + repository: https://example.com/charts diff --git a/pkg/chart/loader/testdata/frobnitz_with_bom/INSTALL.txt b/pkg/chart/loader/testdata/frobnitz_with_bom/INSTALL.txt new file mode 100644 index 000000000..77c4e724a --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz_with_bom/INSTALL.txt @@ -0,0 +1 @@ +This is an install document. The client may display this. diff --git a/pkg/chart/loader/testdata/frobnitz_with_bom/LICENSE b/pkg/chart/loader/testdata/frobnitz_with_bom/LICENSE new file mode 100644 index 000000000..c27b00bf2 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz_with_bom/LICENSE @@ -0,0 +1 @@ +LICENSE placeholder. diff --git a/pkg/chart/loader/testdata/frobnitz_with_bom/README.md b/pkg/chart/loader/testdata/frobnitz_with_bom/README.md new file mode 100644 index 000000000..e9c40031b --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz_with_bom/README.md @@ -0,0 +1,11 @@ +# Frobnitz + +This is an example chart. + +## Usage + +This is an example. It has no usage. + +## Development + +For developer info, see the top-level repository. diff --git a/pkg/chart/loader/testdata/frobnitz_with_bom/charts/_ignore_me b/pkg/chart/loader/testdata/frobnitz_with_bom/charts/_ignore_me new file mode 100644 index 000000000..a7e3a38b7 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz_with_bom/charts/_ignore_me @@ -0,0 +1 @@ +This should be ignored by the loader, but may be included in a chart. diff --git a/pkg/chart/loader/testdata/frobnitz_with_bom/charts/alpine/Chart.yaml b/pkg/chart/loader/testdata/frobnitz_with_bom/charts/alpine/Chart.yaml new file mode 100644 index 000000000..adb9853c6 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz_with_bom/charts/alpine/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +name: alpine +description: Deploy a basic Alpine Linux pod +version: 0.1.0 +home: https://helm.sh/helm diff --git a/pkg/chart/loader/testdata/frobnitz_with_bom/charts/alpine/README.md b/pkg/chart/loader/testdata/frobnitz_with_bom/charts/alpine/README.md new file mode 100644 index 000000000..ea7526bee --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz_with_bom/charts/alpine/README.md @@ -0,0 +1,9 @@ +This example was generated using the command `helm create alpine`. + +The `templates/` directory contains a very simple pod resource with a +couple of parameters. + +The `values.toml` file contains the default values for the +`alpine-pod.yaml` template. + +You can install this example using `helm install ./alpine`. diff --git a/pkg/chart/loader/testdata/frobnitz_with_bom/charts/alpine/charts/mast1/Chart.yaml b/pkg/chart/loader/testdata/frobnitz_with_bom/charts/alpine/charts/mast1/Chart.yaml new file mode 100644 index 000000000..1ad84b346 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz_with_bom/charts/alpine/charts/mast1/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +name: mast1 +description: A Helm chart for Kubernetes +version: 0.1.0 +home: "" diff --git a/pkg/chart/loader/testdata/frobnitz_with_bom/charts/alpine/charts/mast1/values.yaml b/pkg/chart/loader/testdata/frobnitz_with_bom/charts/alpine/charts/mast1/values.yaml new file mode 100644 index 000000000..f690d53c4 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz_with_bom/charts/alpine/charts/mast1/values.yaml @@ -0,0 +1,4 @@ +# Default values for mast1. +# This is a YAML-formatted file. +# Declare name/value pairs to be passed into your templates. +# name = "value" diff --git a/pkg/chart/loader/testdata/frobnitz_with_bom/charts/alpine/charts/mast2-0.1.0.tgz b/pkg/chart/loader/testdata/frobnitz_with_bom/charts/alpine/charts/mast2-0.1.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..61cb62051110b55f3d08213dc81dcf0b1c2d8e53 GIT binary patch literal 252 zcmVDc zVQyr3R8em|NM&qo0PNJUs=_c72H?(liabH@pWIst-7YSIyL+rhEHrIN(t?QZE=F{y zgNRfS&$pa5Ly`mMk2OB%pV`*9knW7FlL-Joo@KED7*{C$d;N~z z37$S{+}wvSU9}|VtF|fRpv9Ve>8dWo|9?5B+RE}Y9CFh-x#(Bq8Vck^V=NUiPLCKa z8z5CF#JgK!4>;$4Fm+FUst4d+{(+nP|0&J+e}(;l^U4@w-{=?s0RR8vgVbLD3;+OM Cs&R<` literal 0 HcmV?d00001 diff --git a/pkg/chart/loader/testdata/frobnitz_with_bom/charts/alpine/templates/alpine-pod.yaml b/pkg/chart/loader/testdata/frobnitz_with_bom/charts/alpine/templates/alpine-pod.yaml new file mode 100644 index 000000000..f3e662a28 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz_with_bom/charts/alpine/templates/alpine-pod.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Pod +metadata: + name: {{.Release.Name}}-{{.Chart.Name}} + labels: + app.kubernetes.io/managed-by: {{.Release.Service}} + app.kubernetes.io/name: {{.Chart.Name}} + helm.sh/chart: "{{.Chart.Name}}-{{.Chart.Version}}" +spec: + restartPolicy: {{default "Never" .restart_policy}} + containers: + - name: waiter + image: "alpine:3.9" + command: ["/bin/sleep","9000"] diff --git a/pkg/chart/loader/testdata/frobnitz_with_bom/charts/alpine/values.yaml b/pkg/chart/loader/testdata/frobnitz_with_bom/charts/alpine/values.yaml new file mode 100644 index 000000000..6b7cb2596 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz_with_bom/charts/alpine/values.yaml @@ -0,0 +1,2 @@ +# The pod name +name: "my-alpine" diff --git a/pkg/chart/loader/testdata/frobnitz_with_bom/charts/mariner-4.3.2.tgz b/pkg/chart/loader/testdata/frobnitz_with_bom/charts/mariner-4.3.2.tgz new file mode 100644 index 0000000000000000000000000000000000000000..3190136b050e62c628b3c817fd963ac9dc4a9e25 GIT binary patch literal 967 zcmV;&133I2iwFR+9h6)E1MQb_y%a`Bd>#= zhbqf2w!b6}wZ9@rvIhtwzm?~C&+QLm+G2!l%`$_aUgS(@pdjdX3a%E}VXVc7`)dg( zL%IRN2}c1D3xoMi2w@WuWOMZ?5i&3FelBVyq_Ce_8fREdP%NDf<&d!wu3{&VUQNs{N%vK$Ha~k^gB2!0bO7r0ic0 zbqCp*X#j?=|H@GNONsuE)&Idbh>2MX(Z}|+=@*sLod*wS?A8Ugp#mMPuMO0NnZmos9_rr z3xpDL+otj~lRh?B4hHFTl+d5-8NBXmUXB~EyF^bx7m*+!*g>obczvGF|8xkWsHN8; z%#+wiWP{=2pC*98@$VNzzsll&G#D7&11-;D>HT0x|DV2?6}a~*p46@R|2l??e_8dX z@Bb>D3t~VC_*wjq2Dy!6J-_5ME%%J+xmsbK5a#qO>ZV?Wt`d|TDdrB^B&LqFr@lYdUA zs&4-JAg3&uc4dA0gv*bXU9QePa9^8wR{HovA)j@)JOAIkak0Jl)aKqA_3XLM#?H=V z-{tlG=xy^os=1Z><53;&+C4=zp|%rwv!)UyY=%k_t%Y|wNRQl`C6N_Z&S;S+~wb1=)2Uh_4I81`y>DO zoLPSt=btB&x{CUK`0DA&){efW_O36RxnsYZNT0r;JbTLR{H4M3C$8(Cee==aQ~Gbq p$`5v3?NB{4-j0XQHf literal 0 HcmV?d00001 diff --git a/pkg/chart/loader/testdata/frobnitz_with_bom/docs/README.md b/pkg/chart/loader/testdata/frobnitz_with_bom/docs/README.md new file mode 100644 index 000000000..816c3e431 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz_with_bom/docs/README.md @@ -0,0 +1 @@ +This is a placeholder for documentation. diff --git a/pkg/chart/loader/testdata/frobnitz_with_bom/icon.svg b/pkg/chart/loader/testdata/frobnitz_with_bom/icon.svg new file mode 100644 index 000000000..892130606 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz_with_bom/icon.svg @@ -0,0 +1,8 @@ + + + Example icon + + + diff --git a/pkg/chart/loader/testdata/frobnitz_with_bom/ignore/me.txt b/pkg/chart/loader/testdata/frobnitz_with_bom/ignore/me.txt new file mode 100644 index 000000000..e69de29bb diff --git a/pkg/chart/loader/testdata/frobnitz_with_bom/templates/template.tpl b/pkg/chart/loader/testdata/frobnitz_with_bom/templates/template.tpl new file mode 100644 index 000000000..bb29c5491 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz_with_bom/templates/template.tpl @@ -0,0 +1 @@ +Hello {{.Name | default "world"}} diff --git a/pkg/chart/loader/testdata/frobnitz_with_bom/values.yaml b/pkg/chart/loader/testdata/frobnitz_with_bom/values.yaml new file mode 100644 index 000000000..c24ceadf9 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz_with_bom/values.yaml @@ -0,0 +1,6 @@ +# A values file contains configuration. + +name: "Some Name" + +section: + name: "Name in a section" From 9ced0165aba1f0d90990396306d6f7e7a6725a91 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Thu, 23 Apr 2020 11:21:04 -0700 Subject: [PATCH 075/281] fix(cmd/env): make helm env command respect cli flags (#7978) Running `helm env` should respect cli flag overrides. Signed-off-by: Adam Reese --- cmd/helm/env.go | 46 ++++++++++++++-------------------------------- 1 file changed, 14 insertions(+), 32 deletions(-) diff --git a/cmd/helm/env.go b/cmd/helm/env.go index efb6dfea9..0fbfb9da4 100644 --- a/cmd/helm/env.go +++ b/cmd/helm/env.go @@ -21,53 +21,35 @@ import ( "io" "sort" - "helm.sh/helm/v3/pkg/cli" - "github.com/spf13/cobra" "helm.sh/helm/v3/cmd/helm/require" ) -var ( - envHelp = ` +var envHelp = ` Env prints out all the environment information in use by Helm. ` -) func newEnvCmd(out io.Writer) *cobra.Command { - o := &envOptions{} - o.settings = cli.New() - cmd := &cobra.Command{ Use: "env", Short: "helm client environment information", Long: envHelp, Args: require.NoArgs, - RunE: func(cmd *cobra.Command, args []string) error { - return o.run(out) + Run: func(cmd *cobra.Command, args []string) { + envVars := settings.EnvVars() + + // Sort the variables by alphabetical order. + // This allows for a constant output across calls to 'helm env'. + var keys []string + for k := range envVars { + keys = append(keys, k) + } + sort.Strings(keys) + for _, k := range keys { + fmt.Fprintf(out, "%s=\"%s\"\n", k, envVars[k]) + } }, } - return cmd } - -type envOptions struct { - settings *cli.EnvSettings -} - -func (o *envOptions) run(out io.Writer) error { - envVars := o.settings.EnvVars() - - // Sort the variables by alphabetical order. - // This allows for a constant output across calls to 'helm env'. - var keys []string - for k := range envVars { - keys = append(keys, k) - } - sort.Strings(keys) - - for _, k := range keys { - fmt.Printf("%s=\"%s\"\n", k, envVars[k]) - } - return nil -} From 4a0dfbe53b2191e09a7034ce97e4caf84989d6db Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Thu, 23 Apr 2020 12:20:14 -0700 Subject: [PATCH 076/281] fix(pkg/cli): ensure correct configuration from kubeconfig file Bind Helm flags to Kubernetes configuration loader to get a merged config with kubeconfig. Fixes: #7539 Signed-off-by: Adam Reese --- cmd/helm/helm.go | 21 ++++++------- pkg/cli/environment.go | 53 +++++++++++++-------------------- pkg/getter/getter_test.go | 14 +++++---- pkg/getter/httpgetter_test.go | 2 +- pkg/getter/plugingetter_test.go | 17 +++++------ pkg/kube/config.go | 2 ++ pkg/plugin/plugin_test.go | 5 ++-- 7 files changed, 53 insertions(+), 61 deletions(-) diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index 7e1fcb6e1..fcc7315f5 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -43,9 +43,7 @@ import ( // FeatureGateOCI is the feature gate for checking if `helm chart` and `helm registry` commands should work const FeatureGateOCI = gates.Gate("HELM_EXPERIMENTAL_OCI") -var ( - settings = cli.New() -) +var settings = cli.New() func init() { log.SetFlags(log.Lshortfile) @@ -72,13 +70,16 @@ func main() { actionConfig := new(action.Configuration) cmd := newRootCmd(actionConfig, os.Stdout, os.Args[1:]) - helmDriver := os.Getenv("HELM_DRIVER") - if err := actionConfig.Init(settings.RESTClientGetter(), settings.Namespace(), helmDriver, debug); err != nil { - log.Fatal(err) - } - if helmDriver == "memory" { - loadReleasesInMemory(actionConfig) - } + // run when each command's execute method is called + cobra.OnInitialize(func() { + helmDriver := os.Getenv("HELM_DRIVER") + if err := actionConfig.Init(settings.RESTClientGetter(), settings.Namespace(), helmDriver, debug); err != nil { + log.Fatal(err) + } + if helmDriver == "memory" { + loadReleasesInMemory(actionConfig) + } + }) if err := cmd.Execute(); err != nil { debug("%+v", err) diff --git a/pkg/cli/environment.go b/pkg/cli/environment.go index e279331b0..2e64e0810 100644 --- a/pkg/cli/environment.go +++ b/pkg/cli/environment.go @@ -26,21 +26,17 @@ import ( "fmt" "os" "strconv" - "sync" "github.com/spf13/pflag" - "k8s.io/cli-runtime/pkg/genericclioptions" "helm.sh/helm/v3/pkg/helmpath" - "helm.sh/helm/v3/pkg/kube" ) // EnvSettings describes all of the environment settings. type EnvSettings struct { - namespace string - config genericclioptions.RESTClientGetter - configOnce sync.Once + namespace string + config *genericclioptions.ConfigFlags // KubeConfig is the path to the kubeconfig file KubeConfig string @@ -63,8 +59,7 @@ type EnvSettings struct { } func New() *EnvSettings { - - env := EnvSettings{ + env := &EnvSettings{ namespace: os.Getenv("HELM_NAMESPACE"), KubeContext: os.Getenv("HELM_KUBECONTEXT"), KubeToken: os.Getenv("HELM_KUBETOKEN"), @@ -75,7 +70,16 @@ func New() *EnvSettings { RepositoryCache: envOr("HELM_REPOSITORY_CACHE", helmpath.CachePath("repository")), } env.Debug, _ = strconv.ParseBool(os.Getenv("HELM_DEBUG")) - return &env + + // bind to kubernetes config flags + env.config = &genericclioptions.ConfigFlags{ + Namespace: &env.namespace, + Context: &env.KubeContext, + BearerToken: &env.KubeToken, + APIServer: &env.KubeAPIServer, + KubeConfig: &env.KubeConfig, + } + return env } // AddFlags binds flags to the given flagset. @@ -107,42 +111,27 @@ func (s *EnvSettings) EnvVars() map[string]string { "HELM_REPOSITORY_CACHE": s.RepositoryCache, "HELM_REPOSITORY_CONFIG": s.RepositoryConfig, "HELM_NAMESPACE": s.Namespace(), - "HELM_KUBECONTEXT": s.KubeContext, - "HELM_KUBETOKEN": s.KubeToken, - "HELM_KUBEAPISERVER": s.KubeAPIServer, - } + // broken, these are populated from helm flags and not kubeconfig. + "HELM_KUBECONTEXT": s.KubeContext, + "HELM_KUBETOKEN": s.KubeToken, + "HELM_KUBEAPISERVER": s.KubeAPIServer, + } if s.KubeConfig != "" { envvars["KUBECONFIG"] = s.KubeConfig } - return envvars } -//Namespace gets the namespace from the configuration +// Namespace gets the namespace from the configuration func (s *EnvSettings) Namespace() string { - if s.namespace != "" { - return s.namespace - } - - if ns, _, err := s.RESTClientGetter().ToRawKubeConfigLoader().Namespace(); err == nil { + if ns, _, err := s.config.ToRawKubeConfigLoader().Namespace(); err == nil { return ns } return "default" } -//RESTClientGetter gets the kubeconfig from EnvSettings +// RESTClientGetter gets the kubeconfig from EnvSettings func (s *EnvSettings) RESTClientGetter() genericclioptions.RESTClientGetter { - s.configOnce.Do(func() { - clientConfig := kube.GetConfig(s.KubeConfig, s.KubeContext, s.namespace) - if s.KubeToken != "" { - clientConfig.BearerToken = &s.KubeToken - } - if s.KubeAPIServer != "" { - clientConfig.APIServer = &s.KubeAPIServer - } - - s.config = clientConfig - }) return s.config } diff --git a/pkg/getter/getter_test.go b/pkg/getter/getter_test.go index 60eb4738e..79a3338e9 100644 --- a/pkg/getter/getter_test.go +++ b/pkg/getter/getter_test.go @@ -53,9 +53,10 @@ func TestProviders(t *testing.T) { } func TestAll(t *testing.T) { - all := All(&cli.EnvSettings{ - PluginsDirectory: pluginDir, - }) + env := cli.New() + env.PluginsDirectory = pluginDir + + all := All(env) if len(all) != 3 { t.Errorf("expected 3 providers (default plus two plugins), got %d", len(all)) } @@ -66,9 +67,10 @@ func TestAll(t *testing.T) { } func TestByScheme(t *testing.T) { - g := All(&cli.EnvSettings{ - PluginsDirectory: pluginDir, - }) + env := cli.New() + env.PluginsDirectory = pluginDir + + g := All(env) if _, err := g.ByScheme("test"); err != nil { t.Error(err) } diff --git a/pkg/getter/httpgetter_test.go b/pkg/getter/httpgetter_test.go index a1288bf47..4d7ada852 100644 --- a/pkg/getter/httpgetter_test.go +++ b/pkg/getter/httpgetter_test.go @@ -122,7 +122,7 @@ func TestDownload(t *testing.T) { })) defer srv.Close() - g, err := All(new(cli.EnvSettings)).ByScheme("http") + g, err := All(cli.New()).ByScheme("http") if err != nil { t.Fatal(err) } diff --git a/pkg/getter/plugingetter_test.go b/pkg/getter/plugingetter_test.go index 71563e169..a18fa302b 100644 --- a/pkg/getter/plugingetter_test.go +++ b/pkg/getter/plugingetter_test.go @@ -24,9 +24,9 @@ import ( ) func TestCollectPlugins(t *testing.T) { - env := &cli.EnvSettings{ - PluginsDirectory: pluginDir, - } + env := cli.New() + env.PluginsDirectory = pluginDir + p, err := collectPlugins(env) if err != nil { t.Fatal(err) @@ -54,9 +54,8 @@ func TestPluginGetter(t *testing.T) { t.Skip("TODO: refactor this test to work on windows") } - env := &cli.EnvSettings{ - PluginsDirectory: pluginDir, - } + env := cli.New() + env.PluginsDirectory = pluginDir pg := NewPluginGetter("echo", env, "test", ".") g, err := pg() if err != nil { @@ -80,9 +79,9 @@ func TestPluginSubCommands(t *testing.T) { t.Skip("TODO: refactor this test to work on windows") } - env := &cli.EnvSettings{ - PluginsDirectory: pluginDir, - } + env := cli.New() + env.PluginsDirectory = pluginDir + pg := NewPluginGetter("echo -n", env, "test", ".") g, err := pg() if err != nil { diff --git a/pkg/kube/config.go b/pkg/kube/config.go index 624c4a1f7..e00c9acb1 100644 --- a/pkg/kube/config.go +++ b/pkg/kube/config.go @@ -19,6 +19,8 @@ package kube // import "helm.sh/helm/v3/pkg/kube" import "k8s.io/cli-runtime/pkg/genericclioptions" // GetConfig returns a Kubernetes client config. +// +// Deprecated func GetConfig(kubeconfig, context, namespace string) *genericclioptions.ConfigFlags { cf := genericclioptions.NewConfigFlags(true) cf.Namespace = &namespace diff --git a/pkg/plugin/plugin_test.go b/pkg/plugin/plugin_test.go index a869255c4..af0b61846 100644 --- a/pkg/plugin/plugin_test.go +++ b/pkg/plugin/plugin_test.go @@ -305,9 +305,8 @@ func TestSetupEnv(t *testing.T) { name := "pequod" base := filepath.Join("testdata/helmhome/helm/plugins", name) - s := &cli.EnvSettings{ - PluginsDirectory: "testdata/helmhome/helm/plugins", - } + s := cli.New() + s.PluginsDirectory = "testdata/helmhome/helm/plugins" SetupPluginEnv(s, name, base) for _, tt := range []struct { From 78d57d3d2059de200ed0b1c9c7cb1fb0104630c3 Mon Sep 17 00:00:00 2001 From: Josh Dolitsky <393494+jdolitsky@users.noreply.github.com> Date: Thu, 23 Apr 2020 14:30:31 -0500 Subject: [PATCH 077/281] Modify Circle config to use Go 1.14 (#7980) Signed-off-by: Josh Dolitsky <393494+jdolitsky@users.noreply.github.com> --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ef19b8ee7..e6ce2e242 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,7 +5,7 @@ jobs: build: working_directory: ~/helm.sh/helm docker: - - image: circleci/golang:1.13 + - image: circleci/golang:1.14 environment: GOCACHE: "/tmp/go/cache" From 54e5088a500bde62a84eeba89d39abd6d7f28daa Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Thu, 23 Apr 2020 19:49:43 -0400 Subject: [PATCH 078/281] Fixing docs from version to appVersion (#7975) In the created chart from `helm create` is notes a tag overrides version. It actually overrides appVersion. Updating the docs to reflect reality. Signed-off-by: Matt Farina --- pkg/chartutil/create.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/chartutil/create.go b/pkg/chartutil/create.go index 28fb28e00..0e87c7b47 100644 --- a/pkg/chartutil/create.go +++ b/pkg/chartutil/create.go @@ -99,7 +99,7 @@ replicaCount: 1 image: repository: nginx pullPolicy: IfNotPresent - # Overrides the image tag whose default is the chart version. + # Overrides the image tag whose default is the chart appVersion. tag: "" imagePullSecrets: [] From c422e51ca13b9f213d5f7ed42c187d401ffec245 Mon Sep 17 00:00:00 2001 From: Thomas FREYSS Date: Fri, 24 Apr 2020 11:09:27 +0200 Subject: [PATCH 079/281] test: add test for bom test data integrity Signed-off-by: Thomas FREYSS --- pkg/chart/loader/load_test.go | 49 +++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/pkg/chart/loader/load_test.go b/pkg/chart/loader/load_test.go index 3fa6bba0d..40b86dec2 100644 --- a/pkg/chart/loader/load_test.go +++ b/pkg/chart/loader/load_test.go @@ -20,6 +20,7 @@ import ( "archive/tar" "bytes" "compress/gzip" + "io" "io/ioutil" "os" "path/filepath" @@ -85,6 +86,54 @@ func TestLoadDirWithSymlink(t *testing.T) { verifyDependenciesLock(t, c) } +func TestBomTestData(t *testing.T) { + testFiles := []string{"frobnitz_with_bom/.helmignore", "frobnitz_with_bom/templates/template.tpl", "frobnitz_with_bom/Chart.yaml"} + for _, file := range testFiles { + data, err := ioutil.ReadFile("testdata/" + file) + if err != nil || !bytes.HasPrefix(data, utf8bom) { + t.Errorf("Test file has no BOM or is invalid: testdata/%s", file) + } + } + + archive, err := ioutil.ReadFile("testdata/frobnitz_with_bom.tgz") + if err != nil { + t.Fatalf("Error reading archive frobnitz_with_bom.tgz: %s", err) + } + unzipped, err := gzip.NewReader(bytes.NewReader(archive)) + if err != nil { + t.Fatalf("Error reading archive frobnitz_with_bom.tgz: %s", err) + } + defer unzipped.Close() + for _, testFile := range testFiles { + data := make([]byte, 3) + err := unzipped.Reset(bytes.NewReader(archive)) + if err != nil { + t.Fatalf("Error reading archive frobnitz_with_bom.tgz: %s", err) + } + tr := tar.NewReader(unzipped) + for { + file, err := tr.Next() + if err == io.EOF { + break + } + if err != nil { + t.Fatalf("Error reading archive frobnitz_with_bom.tgz: %s", err) + } + if file != nil && strings.EqualFold(file.Name, testFile) { + _, err := tr.Read(data) + if err != nil { + t.Fatalf("Error reading archive frobnitz_with_bom.tgz: %s", err) + } else { + break + } + } + } + if !bytes.Equal(data, utf8bom) { + t.Fatalf("Test file has no BOM or is invalid: frobnitz_with_bom.tgz/%s", testFile) + } + } +} + func TestLoadDirWithUTFBOM(t *testing.T) { l, err := Loader("testdata/frobnitz_with_bom") if err != nil { From 984d2ac7676874ae78a7617f7417513a7a9b5ef2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl?= Date: Fri, 24 Apr 2020 23:03:47 +0200 Subject: [PATCH 080/281] fix: write index.yaml file atomically (#7954) * fix: write index.yaml file atomically This refactors the already-existing `AtomicWriteFile` utility to a central location and uses it to write index files atomically. This is done to avoid having half-written index files break client requests. Drive-bys: - Add test for AtomicWriteFile. - Add test IndexFile.WriteFile. Signed-off-by: rabadin * Review fix: use RenameWithFallback instead of os.Rename Signed-off-by: rabadin Co-authored-by: rabadin --- internal/fileutil/fileutil.go | 51 ++++++++++++++++++++++++ internal/fileutil/fileutil_test.go | 62 ++++++++++++++++++++++++++++++ pkg/downloader/chart_downloader.go | 31 ++------------- pkg/repo/index.go | 4 +- pkg/repo/index_test.go | 20 ++++++++++ 5 files changed, 139 insertions(+), 29 deletions(-) create mode 100644 internal/fileutil/fileutil.go create mode 100644 internal/fileutil/fileutil_test.go diff --git a/internal/fileutil/fileutil.go b/internal/fileutil/fileutil.go new file mode 100644 index 000000000..739093f3b --- /dev/null +++ b/internal/fileutil/fileutil.go @@ -0,0 +1,51 @@ +/* +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 fileutil + +import ( + "io" + "io/ioutil" + "os" + "path/filepath" + + "helm.sh/helm/v3/internal/third_party/dep/fs" +) + +// AtomicWriteFile atomically (as atomic as os.Rename allows) writes a file to a +// disk. +func AtomicWriteFile(filename string, reader io.Reader, mode os.FileMode) error { + tempFile, err := ioutil.TempFile(filepath.Split(filename)) + if err != nil { + return err + } + tempName := tempFile.Name() + + if _, err := io.Copy(tempFile, reader); err != nil { + tempFile.Close() // return value is ignored as we are already on error path + return err + } + + if err := tempFile.Close(); err != nil { + return err + } + + if err := os.Chmod(tempName, mode); err != nil { + return err + } + + return fs.RenameWithFallback(tempName, filename) +} diff --git a/internal/fileutil/fileutil_test.go b/internal/fileutil/fileutil_test.go new file mode 100644 index 000000000..9a4bc32c9 --- /dev/null +++ b/internal/fileutil/fileutil_test.go @@ -0,0 +1,62 @@ +/* +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 fileutil + +import ( + "bytes" + "io/ioutil" + "os" + "path/filepath" + "testing" +) + +func TestAtomicWriteFile(t *testing.T) { + dir, err := ioutil.TempDir("", "helm-tmp") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + testpath := filepath.Join(dir, "test") + stringContent := "Test content" + reader := bytes.NewReader([]byte(stringContent)) + mode := os.FileMode(0644) + + err = AtomicWriteFile(testpath, reader, mode) + if err != nil { + t.Errorf("AtomicWriteFile error: %s", err) + } + + got, err := ioutil.ReadFile(testpath) + if err != nil { + t.Fatal(err) + } + + if stringContent != string(got) { + t.Fatalf("expected: %s, got: %s", stringContent, string(got)) + } + + gotinfo, err := os.Stat(testpath) + if err != nil { + t.Fatal(err) + } + + if mode != gotinfo.Mode() { + t.Fatalf("expected %s: to be the same mode as %s", + mode, gotinfo.Mode()) + } +} diff --git a/pkg/downloader/chart_downloader.go b/pkg/downloader/chart_downloader.go index 0013dbdf0..ef26f3348 100644 --- a/pkg/downloader/chart_downloader.go +++ b/pkg/downloader/chart_downloader.go @@ -18,7 +18,6 @@ package downloader import ( "fmt" "io" - "io/ioutil" "net/url" "os" "path/filepath" @@ -26,6 +25,7 @@ import ( "github.com/pkg/errors" + "helm.sh/helm/v3/internal/fileutil" "helm.sh/helm/v3/internal/urlutil" "helm.sh/helm/v3/pkg/getter" "helm.sh/helm/v3/pkg/helmpath" @@ -72,31 +72,6 @@ type ChartDownloader struct { RepositoryCache string } -// atomicWriteFile atomically (as atomic as os.Rename allows) writes a file to a -// disk. -func atomicWriteFile(filename string, body io.Reader, mode os.FileMode) error { - tempFile, err := ioutil.TempFile(filepath.Split(filename)) - if err != nil { - return err - } - tempName := tempFile.Name() - - if _, err := io.Copy(tempFile, body); err != nil { - tempFile.Close() // return value is ignored as we are already on error path - return err - } - - if err := tempFile.Close(); err != nil { - return err - } - - if err := os.Chmod(tempName, mode); err != nil { - return err - } - - return os.Rename(tempName, filename) -} - // DownloadTo retrieves a chart. Depending on the settings, it may also download a provenance file. // // If Verify is set to VerifyNever, the verification will be nil. @@ -126,7 +101,7 @@ func (c *ChartDownloader) DownloadTo(ref, version, dest string) (string, *proven name := filepath.Base(u.Path) destfile := filepath.Join(dest, name) - if err := atomicWriteFile(destfile, data, 0644); err != nil { + if err := fileutil.AtomicWriteFile(destfile, data, 0644); err != nil { return destfile, nil, err } @@ -142,7 +117,7 @@ func (c *ChartDownloader) DownloadTo(ref, version, dest string) (string, *proven return destfile, ver, nil } provfile := destfile + ".prov" - if err := atomicWriteFile(provfile, body, 0644); err != nil { + if err := fileutil.AtomicWriteFile(provfile, body, 0644); err != nil { return destfile, nil, err } diff --git a/pkg/repo/index.go b/pkg/repo/index.go index 36386665e..6ef2cf8b5 100644 --- a/pkg/repo/index.go +++ b/pkg/repo/index.go @@ -17,6 +17,7 @@ limitations under the License. package repo import ( + "bytes" "io/ioutil" "os" "path" @@ -29,6 +30,7 @@ import ( "github.com/pkg/errors" "sigs.k8s.io/yaml" + "helm.sh/helm/v3/internal/fileutil" "helm.sh/helm/v3/internal/urlutil" "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/chart/loader" @@ -197,7 +199,7 @@ func (i IndexFile) WriteFile(dest string, mode os.FileMode) error { if err != nil { return err } - return ioutil.WriteFile(dest, b, mode) + return fileutil.AtomicWriteFile(dest, bytes.NewReader(b), mode) } // Merge merges the given index file into this index. diff --git a/pkg/repo/index_test.go b/pkg/repo/index_test.go index 5dbd5e551..466a2c306 100644 --- a/pkg/repo/index_test.go +++ b/pkg/repo/index_test.go @@ -428,3 +428,23 @@ func TestIndexAdd(t *testing.T) { t.Errorf("Expected http://example.com/charts/deis-0.1.0.tgz, got %s", i.Entries["deis"][0].URLs[0]) } } + +func TestIndexWrite(t *testing.T) { + i := NewIndexFile() + i.Add(&chart.Metadata{Name: "clipper", Version: "0.1.0"}, "clipper-0.1.0.tgz", "http://example.com/charts", "sha256:1234567890") + dir, err := ioutil.TempDir("", "helm-tmp") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + testpath := filepath.Join(dir, "test") + i.WriteFile(testpath, 0600) + + got, err := ioutil.ReadFile(testpath) + if err != nil { + t.Fatal(err) + } + if !strings.Contains(string(got), "clipper-0.1.0.tgz") { + t.Fatal("Index files doesn't contain expected content") + } +} From 6bc4a948be0ef1e32f6a605b655cc8cd5f0c2f06 Mon Sep 17 00:00:00 2001 From: Hu Shuai Date: Mon, 27 Apr 2020 14:19:02 +0800 Subject: [PATCH 081/281] Add unit test for pkg/chart/chart.go Signed-off-by: Hu Shuai --- pkg/chart/chart_test.go | 50 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/pkg/chart/chart_test.go b/pkg/chart/chart_test.go index 1b8669ac8..ef8cec3ad 100644 --- a/pkg/chart/chart_test.go +++ b/pkg/chart/chart_test.go @@ -159,3 +159,53 @@ func TestChartFullPath(t *testing.T) { is.Equal("foo/charts/", chrt1.ChartFullPath()) is.Equal("foo", chrt2.ChartFullPath()) } + +func TestCRDObjects(t *testing.T) { + chrt := Chart{ + Files: []*File{ + { + Name: "crds/foo.yaml", + Data: []byte("hello"), + }, + { + Name: "bar.yaml", + Data: []byte("hello"), + }, + { + Name: "crds/foo/bar/baz.yaml", + Data: []byte("hello"), + }, + { + Name: "crdsfoo/bar/baz.yaml", + Data: []byte("hello"), + }, + { + Name: "crds/README.md", + Data: []byte("# hello"), + }, + }, + } + + expected := []CRD{ + { + Name: "crds/foo.yaml", + Filename: "crds/foo.yaml", + File: &File{ + Name: "crds/foo.yaml", + Data: []byte("hello"), + }, + }, + { + Name: "crds/foo/bar/baz.yaml", + Filename: "crds/foo/bar/baz.yaml", + File: &File{ + Name: "crds/foo/bar/baz.yaml", + Data: []byte("hello"), + }, + }, + } + + is := assert.New(t) + crds := chrt.CRDObjects() + is.Equal(expected, crds) +} From f7d1bfd0063a0d43aaef0764222a819fe3472056 Mon Sep 17 00:00:00 2001 From: Bridget Kromhout Date: Mon, 27 Apr 2020 09:02:14 -0500 Subject: [PATCH 082/281] Adding PR template from dev-v2 branch Signed-off-by: Bridget Kromhout --- .github/pull_request_template.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..595b50218 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,12 @@ + + +**What this PR does / why we need it**: + +**Special notes for your reviewer**: + +**If applicable**: +- [ ] this PR contains documentation +- [ ] this PR contains unit tests +- [ ] this PR has been tested for backwards compatibility From 0a318beba32626997abb6af915ebad9399430103 Mon Sep 17 00:00:00 2001 From: Bridget Kromhout Date: Mon, 27 Apr 2020 09:17:09 -0500 Subject: [PATCH 083/281] Updating CONTRIBUTING to match current practice Signed-off-by: Bridget Kromhout --- CONTRIBUTING.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 63780365e..a637f9255 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -184,9 +184,9 @@ contributing to Helm. All issue types follow the same general lifecycle. Differe ## How to Contribute a Patch -1. If you haven't already done so, sign a Contributor License Agreement (see details above). -2. Fork the desired repo, develop and test your code changes. -3. Submit a pull request. +1. Identify or create the related issue. +2. Fork the desired repo; develop and test your code changes. +3. Submit a pull request, making sure to sign your work and link the related issue. Coding conventions and standards are explained in the [official developer docs](https://helm.sh/docs/developers/). From cd50d0c3621ad91b3848f14b7ef3a8d6aa29d2c9 Mon Sep 17 00:00:00 2001 From: Anshul Verma Date: Tue, 28 Apr 2020 01:41:47 +0530 Subject: [PATCH 084/281] Fix : Prints empty list in json/yaml is no repositories are present (#7949) * Prints empty repolist in json/yaml if there are no repos and output format is given as json/yaml rather that printing the error msg "no repositories to show" Signed-off-by: Anshul Verma * Prints empty repolist in json/yaml if there are no repos and output format is given as json/yaml rather that printing the error msg "no repositories to show" Signed-off-by: Anshul Verma --- cmd/helm/repo_list.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/helm/repo_list.go b/cmd/helm/repo_list.go index 51b4f0d58..ed1c9573c 100644 --- a/cmd/helm/repo_list.go +++ b/cmd/helm/repo_list.go @@ -38,7 +38,7 @@ func newRepoListCmd(out io.Writer) *cobra.Command { Args: require.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { f, err := repo.LoadFile(settings.RepositoryConfig) - if isNotExist(err) || len(f.Repositories) == 0 { + if isNotExist(err) || (len(f.Repositories) == 0 && !(outfmt == output.JSON || outfmt == output.YAML)) { return errors.New("no repositories to show") } From 2334195a01a6e47d597940890890a1369f8bcba4 Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Thu, 23 Apr 2020 14:50:31 -0400 Subject: [PATCH 085/281] Adding Helm env vars where XDG exposed Helm had been exposing XDG based variables to end users. This lead to confusion. For example, if a user wanted to change the cache location Helm used should they change the XDG variable? Since this would be like changing the HOME environment variable the answer is no. This change adds HELM_*_HOME environment variables to be used in addition to XDG ones of the same name. Helm will now look for the Helm specific variable. If not set, Helm will fall back to XDG locations. If those are not set a default location will be used. This keeps XDG in use as a default when present, provides users with the ability to set the location, and removes XDG from being exposed to end users to avoid confusion. Closes #7919 Signed-off-by: Matt Farina --- cmd/helm/root.go | 14 +++++++------- cmd/helm/root_test.go | 18 ++++++++++++++++++ internal/test/ensure/ensure.go | 4 ++++ pkg/cli/environment.go | 3 +++ pkg/helmpath/lazypath.go | 33 ++++++++++++++++++++++++++++----- 5 files changed, 60 insertions(+), 12 deletions(-) diff --git a/cmd/helm/root.go b/cmd/helm/root.go index 2c66d3a09..e9bc26fe4 100644 --- a/cmd/helm/root.go +++ b/cmd/helm/root.go @@ -46,20 +46,20 @@ Environment variables: +------------------+--------------------------------------------------------------------------------------------------------+ | Name | Description | +------------------+--------------------------------------------------------------------------------------------------------+ -| $XDG_CACHE_HOME | set an alternative location for storing cached files. | -| $XDG_CONFIG_HOME | set an alternative location for storing Helm configuration. | -| $XDG_DATA_HOME | set an alternative location for storing Helm data. | +| $HELM_CACHE_HOME | set an alternative location for storing cached files. | +| $HELM_CONFIG_HOME | set an alternative location for storing Helm configuration. | +| $HELM_DATA_HOME | set an alternative location for storing Helm data. | | $HELM_DRIVER | set the backend storage driver. Values are: configmap, secret, memory, postgres | | $HELM_DRIVER_SQL_CONNECTION_STRING | set the connection string the SQL storage driver should use. | | $HELM_NO_PLUGINS | disable plugins. Set HELM_NO_PLUGINS=1 to disable plugins. | | $KUBECONFIG | set an alternative Kubernetes configuration file (default "~/.kube/config") | +------------------+--------------------------------------------------------------------------------------------------------+ -Helm stores configuration based on the XDG base directory specification, so +Helm stores cache, configuration, and data based on the following configuration order: -- cached files are stored in $XDG_CACHE_HOME/helm -- configuration is stored in $XDG_CONFIG_HOME/helm -- data is stored in $XDG_DATA_HOME/helm +- If a HELM_*_HOME environment variable is set, it will be used +- Otherwise, on systems supporting the XDG base directory specification, the XDG variables will be used +- When no other location is set a default location will be used based on the operating system By default, the default directories depend on the Operating System. The defaults are listed below: diff --git a/cmd/helm/root_test.go b/cmd/helm/root_test.go index e1fa1fc27..891bb698e 100644 --- a/cmd/helm/root_test.go +++ b/cmd/helm/root_test.go @@ -55,6 +55,24 @@ func TestRootCmd(t *testing.T) { envvars: map[string]string{xdg.DataHomeEnvVar: "/bar"}, dataPath: "/bar/helm", }, + { + name: "with $HELM_CACHE_HOME set", + args: "env", + envvars: map[string]string{helmpath.CacheHomeEnvVar: "/foo/helm"}, + cachePath: "/foo/helm", + }, + { + name: "with $HELM_CONFIG_HOME set", + args: "env", + envvars: map[string]string{helmpath.ConfigHomeEnvVar: "/foo/helm"}, + configPath: "/foo/helm", + }, + { + name: "with $HELM_DATA_HOME set", + args: "env", + envvars: map[string]string{helmpath.DataHomeEnvVar: "/foo/helm"}, + dataPath: "/foo/helm", + }, } for _, tt := range tests { diff --git a/internal/test/ensure/ensure.go b/internal/test/ensure/ensure.go index b4775df80..6219ad626 100644 --- a/internal/test/ensure/ensure.go +++ b/internal/test/ensure/ensure.go @@ -21,6 +21,7 @@ import ( "os" "testing" + "helm.sh/helm/v3/pkg/helmpath" "helm.sh/helm/v3/pkg/helmpath/xdg" ) @@ -31,6 +32,9 @@ func HelmHome(t *testing.T) func() { os.Setenv(xdg.CacheHomeEnvVar, base) os.Setenv(xdg.ConfigHomeEnvVar, base) os.Setenv(xdg.DataHomeEnvVar, base) + os.Setenv(helmpath.CacheHomeEnvVar, "") + os.Setenv(helmpath.ConfigHomeEnvVar, "") + os.Setenv(helmpath.DataHomeEnvVar, "") return func() { os.RemoveAll(base) } diff --git a/pkg/cli/environment.go b/pkg/cli/environment.go index e279331b0..8e8f4ce8d 100644 --- a/pkg/cli/environment.go +++ b/pkg/cli/environment.go @@ -101,6 +101,9 @@ func envOr(name, def string) string { func (s *EnvSettings) EnvVars() map[string]string { envvars := map[string]string{ "HELM_BIN": os.Args[0], + "HELM_CACHE_HOME": helmpath.CachePath(""), + "HELM_CONFIG_HOME": helmpath.ConfigPath(""), + "HELM_DATA_HOME": helmpath.DataPath(""), "HELM_DEBUG": fmt.Sprint(s.Debug), "HELM_PLUGINS": s.PluginsDirectory, "HELM_REGISTRY_CONFIG": s.RegistryConfig, diff --git a/pkg/helmpath/lazypath.go b/pkg/helmpath/lazypath.go index 0b9068671..a8a64bfab 100644 --- a/pkg/helmpath/lazypath.go +++ b/pkg/helmpath/lazypath.go @@ -20,11 +20,34 @@ import ( "helm.sh/helm/v3/pkg/helmpath/xdg" ) +const ( + // CacheHomeEnvVar is the environment variable used by Helm + // for the cache directory. When no value is set a default is used. + CacheHomeEnvVar = "HELM_CACHE_HOME" + + // ConfigHomeEnvVar is the environment variable used by Helm + // for the config directory. When no value is set a default is used. + ConfigHomeEnvVar = "HELM_CONFIG_HOME" + + // DataHomeEnvVar is the environment variable used by Helm + // for the data directory. When no value is set a default is used. + DataHomeEnvVar = "HELM_DATA_HOME" +) + // lazypath is an lazy-loaded path buffer for the XDG base directory specification. type lazypath string -func (l lazypath) path(envVar string, defaultFn func() string, elem ...string) string { - base := os.Getenv(envVar) +func (l lazypath) path(helmEnvVar, XDGEnvVar string, defaultFn func() string, elem ...string) string { + + // There is an order to checking for a path. + // 1. See if a Helm specific environment variable has been set. + // 2. Check if an XDG environment variable is set + // 3. Fall back to a default + base := os.Getenv(helmEnvVar) + if base != "" { + return filepath.Join(base, filepath.Join(elem...)) + } + base = os.Getenv(XDGEnvVar) if base == "" { base = defaultFn() } @@ -34,16 +57,16 @@ func (l lazypath) path(envVar string, defaultFn func() string, elem ...string) s // cachePath defines the base directory relative to which user specific non-essential data files // should be stored. func (l lazypath) cachePath(elem ...string) string { - return l.path(xdg.CacheHomeEnvVar, cacheHome, filepath.Join(elem...)) + return l.path(CacheHomeEnvVar, xdg.CacheHomeEnvVar, cacheHome, filepath.Join(elem...)) } // configPath defines the base directory relative to which user specific configuration files should // be stored. func (l lazypath) configPath(elem ...string) string { - return l.path(xdg.ConfigHomeEnvVar, configHome, filepath.Join(elem...)) + return l.path(ConfigHomeEnvVar, xdg.ConfigHomeEnvVar, configHome, filepath.Join(elem...)) } // dataPath defines the base directory relative to which user specific data files should be stored. func (l lazypath) dataPath(elem ...string) string { - return l.path(xdg.DataHomeEnvVar, dataHome, filepath.Join(elem...)) + return l.path(DataHomeEnvVar, xdg.DataHomeEnvVar, dataHome, filepath.Join(elem...)) } From 6fc93530569813580c1b6554a7154f4ec0fda0b4 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Tue, 28 Apr 2020 17:12:14 -0600 Subject: [PATCH 086/281] feat: lint the names of templated resources (#8011) Signed-off-by: Matt Butcher --- pkg/lint/lint_test.go | 8 ++- pkg/lint/rules/template.go | 53 ++++++++++++++----- pkg/lint/rules/template_test.go | 29 ++++++++++ .../testdata/goodone/templates/goodone.yaml | 2 +- pkg/lint/rules/testdata/goodone/values.yaml | 2 +- 5 files changed, 78 insertions(+), 16 deletions(-) diff --git a/pkg/lint/lint_test.go b/pkg/lint/lint_test.go index 2c110009d..e7ff4cd7a 100644 --- a/pkg/lint/lint_test.go +++ b/pkg/lint/lint_test.go @@ -104,7 +104,10 @@ func TestBadValues(t *testing.T) { func TestGoodChart(t *testing.T) { m := All(goodChartDir, values, namespace, strict).Messages if len(m) != 0 { - t.Errorf("All failed but shouldn't have: %#v", m) + t.Error("All returned linter messages when it shouldn't have") + for i, msg := range m { + t.Logf("Message %d: %s", i, msg) + } } } @@ -130,6 +133,9 @@ func TestHelmCreateChart(t *testing.T) { m := All(createdChart, values, namespace, true).Messages if ll := len(m); ll != 1 { t.Errorf("All should have had exactly 1 error. Got %d", ll) + for i, msg := range m { + t.Logf("Message %d: %s", i, msg.Error()) + } } else if msg := m[0].Err.Error(); !strings.Contains(msg, "icon is recommended") { t.Errorf("Unexpected lint error: %s", msg) } diff --git a/pkg/lint/rules/template.go b/pkg/lint/rules/template.go index 3d388f81b..e27c6a345 100644 --- a/pkg/lint/rules/template.go +++ b/pkg/lint/rules/template.go @@ -17,9 +17,11 @@ limitations under the License. package rules import ( + "fmt" "os" "path/filepath" "regexp" + "strings" "github.com/pkg/errors" "sigs.k8s.io/yaml" @@ -35,6 +37,14 @@ var ( releaseTimeSearch = regexp.MustCompile(`\.Release\.Time`) ) +// validName is a regular expression for names. +// +// This is different than action.ValidName. It conforms to the regular expression +// `kubectl` says it uses, plus it disallows empty names. +// +// For details, see https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names +var validName = regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`) + // Templates lints the templates in the Linter. func Templates(linter *support.Linter, values map[string]interface{}, namespace string, strict bool) { path := "templates/" @@ -57,7 +67,7 @@ func Templates(linter *support.Linter, values map[string]interface{}, namespace } options := chartutil.ReleaseOptions{ - Name: "testRelease", + Name: "test-release", Namespace: namespace, } @@ -111,14 +121,17 @@ func Templates(linter *support.Linter, values map[string]interface{}, namespace // linter.RunLinterRule(support.WarningSev, path, validateQuotes(string(preExecutedTemplate))) renderedContent := renderedContentMap[filepath.Join(chart.Name(), fileName)] - var yamlStruct K8sYamlStruct - // Even though K8sYamlStruct only defines Metadata namespace, an error in any other - // key will be raised as well - err := yaml.Unmarshal([]byte(renderedContent), &yamlStruct) - - // If YAML linting fails, we sill progress. So we don't capture the returned state - // on this linter run. - linter.RunLinterRule(support.ErrorSev, path, validateYamlContent(err)) + if strings.TrimSpace(renderedContent) != "" { + var yamlStruct K8sYamlStruct + // Even though K8sYamlStruct only defines a few fields, an error in any other + // key will be raised as well + err := yaml.Unmarshal([]byte(renderedContent), &yamlStruct) + + // If YAML linting fails, we sill progress. So we don't capture the returned state + // on this linter run. + linter.RunLinterRule(support.ErrorSev, path, validateYamlContent(err)) + linter.RunLinterRule(support.ErrorSev, path, validateMetadataName(&yamlStruct)) + } } } @@ -149,6 +162,15 @@ func validateYamlContent(err error) error { return errors.Wrap(err, "unable to parse YAML") } +func validateMetadataName(obj *K8sYamlStruct) error { + // This will return an error if the characters do not abide by the standard OR if the + // name is left empty. + if validName.MatchString(obj.Metadata.Name) { + return nil + } + return fmt.Errorf("object name does not conform to Kubernetes naming requirements: %q", obj.Metadata.Name) +} + func validateNoCRDHooks(manifest []byte) error { if crdHookSearch.Match(manifest) { return errors.New("manifest is a crd-install hook. This hook is no longer supported in v3 and all CRDs should also exist the crds/ directory at the top level of the chart") @@ -164,9 +186,14 @@ func validateNoReleaseTime(manifest []byte) error { } // K8sYamlStruct stubs a Kubernetes YAML file. -// Need to access for now to Namespace only +// +// DEPRECATED: In Helm 4, this will be made a private type, as it is for use only within +// the rules package. type K8sYamlStruct struct { - Metadata struct { - Namespace string - } + Metadata k8sYamlMetadata +} + +type k8sYamlMetadata struct { + Namespace string + Name string } diff --git a/pkg/lint/rules/template_test.go b/pkg/lint/rules/template_test.go index ddb46aba0..c924de0e7 100644 --- a/pkg/lint/rules/template_test.go +++ b/pkg/lint/rules/template_test.go @@ -101,3 +101,32 @@ func TestV3Fail(t *testing.T) { t.Errorf("Unexpected error: %s", res[2].Err) } } + +func TestValidateMetadataName(t *testing.T) { + names := map[string]bool{ + "": false, + "foo": true, + "foo.bar1234baz.seventyone": true, + "FOO": false, + "123baz": true, + "foo.BAR.baz": false, + "one-two": true, + "-two": false, + "one_two": false, + "a..b": false, + "%^&#$%*@^*@&#^": false, + } + for input, expectPass := range names { + obj := K8sYamlStruct{Metadata: k8sYamlMetadata{Name: input}} + if err := validateMetadataName(&obj); (err == nil) != expectPass { + st := "fail" + if expectPass { + st = "succeed" + } + t.Errorf("Expected %q to %s", input, st) + if err != nil { + t.Log(err) + } + } + } +} diff --git a/pkg/lint/rules/testdata/goodone/templates/goodone.yaml b/pkg/lint/rules/testdata/goodone/templates/goodone.yaml index 0e77f46f2..cd46f62c7 100644 --- a/pkg/lint/rules/testdata/goodone/templates/goodone.yaml +++ b/pkg/lint/rules/testdata/goodone/templates/goodone.yaml @@ -1,2 +1,2 @@ metadata: - name: {{.Values.name | default "foo" | title}} + name: {{ .Values.name | default "foo" | lower }} diff --git a/pkg/lint/rules/testdata/goodone/values.yaml b/pkg/lint/rules/testdata/goodone/values.yaml index fe9abd983..92c3d9bb9 100644 --- a/pkg/lint/rules/testdata/goodone/values.yaml +++ b/pkg/lint/rules/testdata/goodone/values.yaml @@ -1 +1 @@ -name: "goodone here" +name: "goodone-here" From fb829c2c843df01ad1dd5ffd13c4e923be4ab9e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=BCchinger=20Dominic?= Date: Wed, 29 Apr 2020 09:14:22 +0200 Subject: [PATCH 087/281] Fix markdown table in helm command doc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tables aren't rendered correctly because the syntax was wrong. Fixed it by applying the https://www.markdownguide.org/extended-syntax/#tables table syntax. See https://helm.sh/docs/helm/helm/#synopsis for the wrong rendering. ![Broken table in html](https://imgur.com/bniKxO6) Fix for helm/helm-www#575 Signed-off-by: Lüchinger Dominic --- cmd/helm/root.go | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/cmd/helm/root.go b/cmd/helm/root.go index 2c66d3a09..ea66a061a 100644 --- a/cmd/helm/root.go +++ b/cmd/helm/root.go @@ -43,17 +43,15 @@ Common actions for Helm: Environment variables: -+------------------+--------------------------------------------------------------------------------------------------------+ -| Name | Description | -+------------------+--------------------------------------------------------------------------------------------------------+ -| $XDG_CACHE_HOME | set an alternative location for storing cached files. | -| $XDG_CONFIG_HOME | set an alternative location for storing Helm configuration. | -| $XDG_DATA_HOME | set an alternative location for storing Helm data. | -| $HELM_DRIVER | set the backend storage driver. Values are: configmap, secret, memory, postgres | -| $HELM_DRIVER_SQL_CONNECTION_STRING | set the connection string the SQL storage driver should use. | -| $HELM_NO_PLUGINS | disable plugins. Set HELM_NO_PLUGINS=1 to disable plugins. | -| $KUBECONFIG | set an alternative Kubernetes configuration file (default "~/.kube/config") | -+------------------+--------------------------------------------------------------------------------------------------------+ +| Name | Description | +|------------------------------------|-----------------------------------------------------------------------------------| +| $XDG_CACHE_HOME | set an alternative location for storing cached files. | +| $XDG_CONFIG_HOME | set an alternative location for storing Helm configuration. | +| $XDG_DATA_HOME | set an alternative location for storing Helm data. | +| $HELM_DRIVER | set the backend storage driver. Values are: configmap, secret, memory, postgres | +| $HELM_DRIVER_SQL_CONNECTION_STRING | set the connection string the SQL storage driver should use. | +| $HELM_NO_PLUGINS | disable plugins. Set HELM_NO_PLUGINS=1 to disable plugins. | +| $KUBECONFIG | set an alternative Kubernetes configuration file (default "~/.kube/config") | Helm stores configuration based on the XDG base directory specification, so @@ -63,13 +61,11 @@ Helm stores configuration based on the XDG base directory specification, so By default, the default directories depend on the Operating System. The defaults are listed below: -+------------------+---------------------------+--------------------------------+-------------------------+ | Operating System | Cache Path | Configuration Path | Data Path | -+------------------+---------------------------+--------------------------------+-------------------------+ +|------------------|---------------------------|--------------------------------|-------------------------| | Linux | $HOME/.cache/helm | $HOME/.config/helm | $HOME/.local/share/helm | | macOS | $HOME/Library/Caches/helm | $HOME/Library/Preferences/helm | $HOME/Library/helm | | Windows | %TEMP%\helm | %APPDATA%\helm | %APPDATA%\helm | -+------------------+---------------------------+--------------------------------+-------------------------+ ` func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string) *cobra.Command { From ff3ed53b7c0c07aab2bc8cc59344d18063e8a719 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Thu, 30 Apr 2020 17:31:27 +0800 Subject: [PATCH 088/281] polish to keep the same log style Signed-off-by: Liu Ming --- pkg/kube/client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/kube/client.go b/pkg/kube/client.go index 8a4831ffb..c1de2b299 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -439,7 +439,7 @@ func updateResource(c *Client, target *resource.Info, currentObj runtime.Object, if err != nil { return errors.Wrap(err, "failed to replace object") } - c.Log("Replaced %q with kind %s for kind %s\n", target.Name, currentObj.GetObjectKind().GroupVersionKind().Kind, kind) + c.Log("Replaced %q with kind %s for kind %s", target.Name, currentObj.GetObjectKind().GroupVersionKind().Kind, kind) } else { // send patch to server obj, err = helper.Patch(target.Namespace, target.Name, patchType, patch, nil) From 918deeef181f20ea640bc562cef69d209493cbab Mon Sep 17 00:00:00 2001 From: Matthias Riegler Date: Fri, 1 May 2020 13:21:44 +0200 Subject: [PATCH 089/281] added option --insecure-skip-tls-verify for helm pull, addresses #7875 Signed-off-by: Matthias Riegler --- cmd/helm/flags.go | 1 + pkg/action/install.go | 19 ++++++++++--------- pkg/action/pull.go | 1 + 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/cmd/helm/flags.go b/cmd/helm/flags.go index 246cb0dd5..d35a05fec 100644 --- a/cmd/helm/flags.go +++ b/cmd/helm/flags.go @@ -49,6 +49,7 @@ func addChartPathOptionsFlags(f *pflag.FlagSet, c *action.ChartPathOptions) { f.StringVar(&c.Password, "password", "", "chart repository password where to locate the requested chart") f.StringVar(&c.CertFile, "cert-file", "", "identify HTTPS client using this SSL certificate file") f.StringVar(&c.KeyFile, "key-file", "", "identify HTTPS client using this SSL key file") + f.BoolVar(&c.InsecureSkipTLSverify, "insecure-skip-tls-verify", false, "skip tls certificate checks for the chart download") f.StringVar(&c.CaFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle") } diff --git a/pkg/action/install.go b/pkg/action/install.go index 10a9644dd..cb5318965 100644 --- a/pkg/action/install.go +++ b/pkg/action/install.go @@ -104,15 +104,16 @@ type Install struct { // ChartPathOptions captures common options used for controlling chart paths type ChartPathOptions struct { - CaFile string // --ca-file - CertFile string // --cert-file - KeyFile string // --key-file - Keyring string // --keyring - Password string // --password - RepoURL string // --repo - Username string // --username - Verify bool // --verify - Version string // --version + CaFile string // --ca-file + CertFile string // --cert-file + KeyFile string // --key-file + InsecureSkipTLSverify bool // --insecure-skip-verify + Keyring string // --keyring + Password string // --password + RepoURL string // --repo + Username string // --username + Verify bool // --verify + Version string // --version } // NewInstall creates a new Install object with the given configuration. diff --git a/pkg/action/pull.go b/pkg/action/pull.go index ee20bbe83..a46e98bae 100644 --- a/pkg/action/pull.go +++ b/pkg/action/pull.go @@ -64,6 +64,7 @@ func (p *Pull) Run(chartRef string) (string, error) { Options: []getter.Option{ getter.WithBasicAuth(p.Username, p.Password), getter.WithTLSClientConfig(p.CertFile, p.KeyFile, p.CaFile), + getter.WithInsecureSkipVerifyTLS(p.InsecureSkipTLSverify), }, RepositoryConfig: p.Settings.RepositoryConfig, RepositoryCache: p.Settings.RepositoryCache, From 8ff7801e0c3d902a5fc4129aaaf60aa97c7ba4fd Mon Sep 17 00:00:00 2001 From: Matthias Riegler Date: Fri, 1 May 2020 14:57:36 +0200 Subject: [PATCH 090/281] added option --insecure-skip-tls-verify for helm install, addresses #7875 Signed-off-by: Matthias Riegler --- pkg/action/install.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/action/install.go b/pkg/action/install.go index cb5318965..4a8bf1835 100644 --- a/pkg/action/install.go +++ b/pkg/action/install.go @@ -641,6 +641,7 @@ func (c *ChartPathOptions) LocateChart(name string, settings *cli.EnvSettings) ( Options: []getter.Option{ getter.WithBasicAuth(c.Username, c.Password), getter.WithTLSClientConfig(c.CertFile, c.KeyFile, c.CaFile), + getter.WithInsecureSkipVerifyTLS(c.InsecureSkipTLSverify), }, RepositoryConfig: settings.RepositoryConfig, RepositoryCache: settings.RepositoryCache, From bf9d629dc02e759a1ba09ff8f6784dd0fb585cf3 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Fri, 1 May 2020 14:01:15 -0600 Subject: [PATCH 091/281] feat: implement deprecation warnings in helm lint (#7986) * feat: implement deprecation warnings in helm lint Signed-off-by: Matt Butcher * added more deprecated APIs Signed-off-by: Matt Butcher --- pkg/chartutil/save.go | 3 ++ pkg/lint/rules/deprecations.go | 64 +++++++++++++++++++++++++++++ pkg/lint/rules/deprecations_test.go | 42 +++++++++++++++++++ pkg/lint/rules/template.go | 5 ++- pkg/lint/rules/template_test.go | 44 ++++++++++++++++++++ 5 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 pkg/lint/rules/deprecations.go create mode 100644 pkg/lint/rules/deprecations_test.go diff --git a/pkg/chartutil/save.go b/pkg/chartutil/save.go index be5d151d7..1011436b5 100644 --- a/pkg/chartutil/save.go +++ b/pkg/chartutil/save.go @@ -34,6 +34,9 @@ import ( var headerBytes = []byte("+aHR0cHM6Ly95b3V0dS5iZS96OVV6MWljandyTQo=") // SaveDir saves a chart as files in a directory. +// +// This takes the chart name, and creates a new subdirectory inside of the given dest +// directory, writing the chart's contents to that subdirectory. func SaveDir(c *chart.Chart, dest string) error { // Create the chart directory outdir := filepath.Join(dest, c.Name()) diff --git a/pkg/lint/rules/deprecations.go b/pkg/lint/rules/deprecations.go new file mode 100644 index 000000000..c14fedec6 --- /dev/null +++ b/pkg/lint/rules/deprecations.go @@ -0,0 +1,64 @@ +/* +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 rules // import "helm.sh/helm/v3/pkg/lint/rules" + +import "fmt" + +// deprecatedAPIs lists APIs that are deprecated (left) with suggested alternatives (right). +// +// An empty rvalue indicates that the API is completely deprecated. +var deprecatedAPIs = map[string]string{ + "extensions/v1 Deployment": "apps/v1 Deployment", + "extensions/v1 DaemonSet": "apps/v1 DaemonSet", + "extensions/v1 ReplicaSet": "apps/v1 ReplicaSet", + "extensions/v1beta1 PodSecurityPolicy": "policy/v1beta1 PodSecurityPolicy", + "extensions/v1beta1 NetworkPolicy": "networking.k8s.io/v1beta1 NetworkPolicy", + "extensions/v1beta1 Ingress": "networking.k8s.io/v1beta1 Ingress", + "apps/v1beta1 Deployment": "apps/v1 Deployment", + "apps/v1beta1 StatefulSet": "apps/v1 StatefulSet", + "apps/v1beta1 DaemonSet": "apps/v1 DaemonSet", + "apps/v1beta1 ReplicaSet": "apps/v1 ReplicaSet", + "apps/v1beta2 Deployment": "apps/v1 Deployment", + "apps/v1beta2 StatefulSet": "apps/v1 StatefulSet", + "apps/v1beta2 DaemonSet": "apps/v1 DaemonSet", + "apps/v1beta2 ReplicaSet": "apps/v1 ReplicaSet", +} + +// deprecatedAPIError indicates than an API is deprecated in Kubernetes +type deprecatedAPIError struct { + Deprecated string + Alternative string +} + +func (e deprecatedAPIError) Error() string { + msg := fmt.Sprintf("the kind %q is deprecated", e.Deprecated) + if e.Alternative != "" { + msg += fmt.Sprintf(" in favor of %q", e.Alternative) + } + return msg +} + +func validateNoDeprecations(resource *K8sYamlStruct) error { + gvk := fmt.Sprintf("%s %s", resource.APIVersion, resource.Kind) + if alt, ok := deprecatedAPIs[gvk]; ok { + return deprecatedAPIError{ + Deprecated: gvk, + Alternative: alt, + } + } + return nil +} diff --git a/pkg/lint/rules/deprecations_test.go b/pkg/lint/rules/deprecations_test.go new file mode 100644 index 000000000..f85d58a0c --- /dev/null +++ b/pkg/lint/rules/deprecations_test.go @@ -0,0 +1,42 @@ +/* +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 rules // import "helm.sh/helm/v3/pkg/lint/rules" + +import "testing" + +func TestValidateNoDeprecations(t *testing.T) { + deprecated := &K8sYamlStruct{ + APIVersion: "extensions/v1", + Kind: "Deployment", + } + err := validateNoDeprecations(deprecated) + if err == nil { + t.Fatal("Expected deprecated extension to be flagged") + } + + depErr := err.(deprecatedAPIError) + if depErr.Alternative != "apps/v1 Deployment" { + t.Errorf("Expected %q to be replaced by %q", depErr.Deprecated, depErr.Alternative) + } + + if err := validateNoDeprecations(&K8sYamlStruct{ + APIVersion: "v1", + Kind: "Pod", + }); err != nil { + t.Errorf("Expected a v1 Pod to not be deprecated") + } +} diff --git a/pkg/lint/rules/template.go b/pkg/lint/rules/template.go index e27c6a345..b76e4260a 100644 --- a/pkg/lint/rules/template.go +++ b/pkg/lint/rules/template.go @@ -131,6 +131,7 @@ func Templates(linter *support.Linter, values map[string]interface{}, namespace // on this linter run. linter.RunLinterRule(support.ErrorSev, path, validateYamlContent(err)) linter.RunLinterRule(support.ErrorSev, path, validateMetadataName(&yamlStruct)) + linter.RunLinterRule(support.ErrorSev, path, validateNoDeprecations(&yamlStruct)) } } } @@ -190,7 +191,9 @@ func validateNoReleaseTime(manifest []byte) error { // DEPRECATED: In Helm 4, this will be made a private type, as it is for use only within // the rules package. type K8sYamlStruct struct { - Metadata k8sYamlMetadata + APIVersion string `json:"apiVersion"` + Kind string + Metadata k8sYamlMetadata } type k8sYamlMetadata struct { diff --git a/pkg/lint/rules/template_test.go b/pkg/lint/rules/template_test.go index c924de0e7..1a047edf2 100644 --- a/pkg/lint/rules/template_test.go +++ b/pkg/lint/rules/template_test.go @@ -22,6 +22,9 @@ import ( "strings" "testing" + "helm.sh/helm/v3/internal/test/ensure" + "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/chartutil" "helm.sh/helm/v3/pkg/lint/support" ) @@ -130,3 +133,44 @@ func TestValidateMetadataName(t *testing.T) { } } } + +func TestDeprecatedAPIFails(t *testing.T) { + mychart := chart.Chart{ + Metadata: &chart.Metadata{ + APIVersion: "v2", + Name: "failapi", + Version: "0.1.0", + Icon: "satisfy-the-linting-gods.gif", + }, + Templates: []*chart.File{ + { + Name: "templates/baddeployment.yaml", + Data: []byte("apiVersion: apps/v1beta1\nkind: Deployment\nmetadata:\n name: baddep"), + }, + { + Name: "templates/goodsecret.yaml", + Data: []byte("apiVersion: v1\nkind: Secret\nmetadata:\n name: goodsecret"), + }, + }, + } + tmpdir := ensure.TempDir(t) + defer os.RemoveAll(tmpdir) + + if err := chartutil.SaveDir(&mychart, tmpdir); err != nil { + t.Fatal(err) + } + + linter := support.Linter{ChartDir: filepath.Join(tmpdir, mychart.Name())} + Templates(&linter, values, namespace, strict) + if l := len(linter.Messages); l != 1 { + for i, msg := range linter.Messages { + t.Logf("Message %d: %s", i, msg) + } + t.Fatalf("Expected 1 lint error, got %d", l) + } + + err := linter.Messages[0].Err.(deprecatedAPIError) + if err.Deprecated != "apps/v1beta1 Deployment" { + t.Errorf("Surprised to learn that %q is deprecated", err.Deprecated) + } +} From 524150c662f9c030d2caa9ad8f79d2ff9521c431 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Fri, 1 May 2020 14:02:47 -0600 Subject: [PATCH 092/281] fix: use correct regular expression for Kubernetes names (#8013) Signed-off-by: Matt Butcher --- pkg/action/action.go | 13 +++++++------ pkg/action/action_test.go | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/pkg/action/action.go b/pkg/action/action.go index a8437d729..bb9ef5f71 100644 --- a/pkg/action/action.go +++ b/pkg/action/action.go @@ -62,16 +62,17 @@ var ( errInvalidName = errors.New("invalid release name, must match regex ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])+$ and the length must not longer than 53") ) -// ValidName is a regular expression for names. +// ValidName is a regular expression for resource names. // // According to the Kubernetes help text, the regular expression it uses is: // -// (([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])? +// [a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)* // -// We modified that. First, we added start and end delimiters. Second, we changed -// the final ? to + to require that the pattern match at least once. This modification -// prevents an empty string from matching. -var ValidName = regexp.MustCompile("^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])+$") +// This follows the above regular expression (but requires a full string match, not partial). +// +// The Kubernetes documentation is here, though it is not entirely correct: +// https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names +var ValidName = regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`) // Configuration injects the dependencies that all actions share. type Configuration struct { diff --git a/pkg/action/action_test.go b/pkg/action/action_test.go index 36ef261a3..0cbdb162b 100644 --- a/pkg/action/action_test.go +++ b/pkg/action/action_test.go @@ -316,3 +316,40 @@ func TestGetVersionSet(t *testing.T) { t.Error("Non-existent version is reported found.") } } + +// TestValidName is a regression test for ValidName +// +// Kubernetes has strict naming conventions for resource names. This test represents +// those conventions. +// +// See https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names +// +// NOTE: At the time of this writing, the docs above say that names cannot begin with +// digits. However, `kubectl`'s regular expression explicit allows this, and +// Kubernetes (at least as of 1.18) also accepts resources whose names begin with digits. +func TestValidName(t *testing.T) { + names := map[string]bool{ + "": false, + "foo": true, + "foo.bar1234baz.seventyone": true, + "FOO": false, + "123baz": true, + "foo.BAR.baz": false, + "one-two": true, + "-two": false, + "one_two": false, + "a..b": false, + "%^&#$%*@^*@&#^": false, + "example:com": false, + "example%%com": false, + } + for input, expectPass := range names { + if ValidName.MatchString(input) != expectPass { + st := "fail" + if expectPass { + st = "succeed" + } + t.Errorf("Expected %q to %s", input, st) + } + } +} From 47abe66fd29162a71627ee4c9d26a9c3712e24e1 Mon Sep 17 00:00:00 2001 From: Vibhav Bobade Date: Thu, 16 Apr 2020 03:51:55 +0530 Subject: [PATCH 093/281] Add checking of length of resourceList before creating of deleting A chart being installed which only contains CRDs and not any templates tries to install the resources by default. The resourceList which is used in this case does not check if there are resources present in it or not. This commit adds checks to those particular places where we need to check if the size of resourceList > 0 during installation and deletion. Signed-off-by: Vibhav Bobade --- cmd/helm/install_test.go | 5 +++ .../chart-with-only-crds/.helmignore | 23 ++++++++++++ .../chart-with-only-crds/Chart.yaml | 21 +++++++++++ .../chart-with-only-crds/crds/test-crd.yaml | 19 ++++++++++ pkg/action/install.go | 36 ++++++++++--------- pkg/action/uninstall.go | 7 ++-- 6 files changed, 93 insertions(+), 18 deletions(-) create mode 100644 cmd/helm/testdata/testcharts/chart-with-only-crds/.helmignore create mode 100644 cmd/helm/testdata/testcharts/chart-with-only-crds/Chart.yaml create mode 100644 cmd/helm/testdata/testcharts/chart-with-only-crds/crds/test-crd.yaml diff --git a/cmd/helm/install_test.go b/cmd/helm/install_test.go index 57972024f..4e1584e90 100644 --- a/cmd/helm/install_test.go +++ b/cmd/helm/install_test.go @@ -189,6 +189,11 @@ func TestInstall(t *testing.T) { cmd: "install aeneas testdata/testcharts/deprecated --namespace default", golden: "output/deprecated-chart.txt", }, + // Install chart with only crds + { + name: "install chart with only crds", + cmd: "install crd-test testdata/testcharts/chart-with-only-crds --namespace default", + }, } runTestActionCmd(t, tests) diff --git a/cmd/helm/testdata/testcharts/chart-with-only-crds/.helmignore b/cmd/helm/testdata/testcharts/chart-with-only-crds/.helmignore new file mode 100644 index 000000000..0e8a0eb36 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-only-crds/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/cmd/helm/testdata/testcharts/chart-with-only-crds/Chart.yaml b/cmd/helm/testdata/testcharts/chart-with-only-crds/Chart.yaml new file mode 100644 index 000000000..a8b4c2022 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-only-crds/Chart.yaml @@ -0,0 +1,21 @@ +apiVersion: v2 +name: crd-test +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. +appVersion: 1.16.0 diff --git a/cmd/helm/testdata/testcharts/chart-with-only-crds/crds/test-crd.yaml b/cmd/helm/testdata/testcharts/chart-with-only-crds/crds/test-crd.yaml new file mode 100644 index 000000000..1d7350f1d --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-only-crds/crds/test-crd.yaml @@ -0,0 +1,19 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: tests.test.io +spec: + group: test.io + names: + kind: Test + listKind: TestList + plural: tests + singular: test + scope: Namespaced + versions: + - name : v1alpha2 + served: true + storage: true + - name : v1alpha1 + served: true + storage: false diff --git a/pkg/action/install.go b/pkg/action/install.go index 4b4dd9214..38e86d4cc 100644 --- a/pkg/action/install.go +++ b/pkg/action/install.go @@ -145,20 +145,24 @@ func (i *Install) installCRDs(crds []chart.CRD) error { } totalItems = append(totalItems, res...) } - // Invalidate the local cache, since it will not have the new CRDs - // present. - discoveryClient, err := i.cfg.RESTClientGetter.ToDiscoveryClient() - if err != nil { - return err - } - i.cfg.Log("Clearing discovery cache") - discoveryClient.Invalidate() - // Give time for the CRD to be recognized. - if err := i.cfg.KubeClient.Wait(totalItems, 60*time.Second); err != nil { - return err + if len(totalItems) > 0 { + // Invalidate the local cache, since it will not have the new CRDs + // present. + discoveryClient, err := i.cfg.RESTClientGetter.ToDiscoveryClient() + if err != nil { + return err + } + i.cfg.Log("Clearing discovery cache") + discoveryClient.Invalidate() + // Give time for the CRD to be recognized. + + if err := i.cfg.KubeClient.Wait(totalItems, 60*time.Second); err != nil { + return err + } + + // Make sure to force a rebuild of the cache. + discoveryClient.ServerGroups() } - // Make sure to force a rebuild of the cache. - discoveryClient.ServerGroups() return nil } @@ -265,7 +269,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 && !isUpgrade { + if !i.ClientOnly && !isUpgrade && len(resources) > 0 { toBeAdopted, err = existingResourceConflict(resources, rel.Name, rel.Namespace) if err != nil { return nil, errors.Wrap(err, "rendered manifests contain a resource that already exists. Unable to continue with install") @@ -330,11 +334,11 @@ func (i *Install) Run(chrt *chart.Chart, vals map[string]interface{}) (*release. // At this point, we can do the install. Note that before we were detecting whether to // do an update, but it's not clear whether we WANT to do an update if the re-use is set // to true, since that is basically an upgrade operation. - if len(toBeAdopted) == 0 { + if len(toBeAdopted) == 0 && len(resources) > 0 { if _, err := i.cfg.KubeClient.Create(resources); err != nil { return i.failRelease(rel, err) } - } else { + } else if len(resources) > 0 { if _, err := i.cfg.KubeClient.Update(toBeAdopted, resources, false); err != nil { return i.failRelease(rel, err) } diff --git a/pkg/action/uninstall.go b/pkg/action/uninstall.go index dfaa98472..a51a283d6 100644 --- a/pkg/action/uninstall.go +++ b/pkg/action/uninstall.go @@ -169,6 +169,7 @@ func joinErrors(errs []error) string { // deleteRelease deletes the release and returns manifests that were kept in the deletion process func (u *Uninstall) deleteRelease(rel *release.Release) (string, []error) { + var errs []error caps, err := u.cfg.getCapabilities() if err != nil { return rel.Manifest, []error{errors.Wrap(err, "could not get apiVersions from Kubernetes")} @@ -194,11 +195,13 @@ func (u *Uninstall) deleteRelease(rel *release.Release) (string, []error) { for _, file := range filesToDelete { builder.WriteString("\n---\n" + file.Content) } + resources, err := u.cfg.KubeClient.Build(strings.NewReader(builder.String()), false) if err != nil { return "", []error{errors.Wrap(err, "unable to build kubernetes objects for delete")} } - - _, errs := u.cfg.KubeClient.Delete(resources) + if len(resources) > 0 { + _, errs = u.cfg.KubeClient.Delete(resources) + } return kept, errs } From 08e546f169ff3d5694863f0766c3132da2f095b7 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Mon, 4 May 2020 13:55:42 -0600 Subject: [PATCH 094/281] fix: removed strict template errors in linter (#8017) Signed-off-by: Matt Butcher --- pkg/lint/rules/template.go | 1 - pkg/lint/rules/template_test.go | 52 +++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/pkg/lint/rules/template.go b/pkg/lint/rules/template.go index b76e4260a..787c5b26a 100644 --- a/pkg/lint/rules/template.go +++ b/pkg/lint/rules/template.go @@ -81,7 +81,6 @@ func Templates(linter *support.Linter, values map[string]interface{}, namespace return } var e engine.Engine - e.Strict = strict e.LintMode = true renderedContentMap, err := e.Render(chart, valuesToRender) diff --git a/pkg/lint/rules/template_test.go b/pkg/lint/rules/template_test.go index 1a047edf2..991c6c2f6 100644 --- a/pkg/lint/rules/template_test.go +++ b/pkg/lint/rules/template_test.go @@ -174,3 +174,55 @@ func TestDeprecatedAPIFails(t *testing.T) { t.Errorf("Surprised to learn that %q is deprecated", err.Deprecated) } } + +const manifest = `apiVersion: v1 +kind: ConfigMap +metadata: + name: foo +data: + myval1: {{default "val" .Values.mymap.key1 }} + myval2: {{default "val" .Values.mymap.key2 }} +` + +// TestSTrictTemplatePrasingMapError is a regression test. +// +// The template engine should not produce an error when a map in values.yaml does +// not contain all possible keys. +// +// See https://github.com/helm/helm/issues/7483 +func TestStrictTemplateParsingMapError(t *testing.T) { + + ch := chart.Chart{ + Metadata: &chart.Metadata{ + Name: "regression7483", + APIVersion: "v2", + Version: "0.1.0", + }, + Values: map[string]interface{}{ + "mymap": map[string]string{ + "key1": "val1", + }, + }, + Templates: []*chart.File{ + { + Name: "templates/configmap.yaml", + Data: []byte(manifest), + }, + }, + } + dir := ensure.TempDir(t) + defer os.RemoveAll(dir) + if err := chartutil.SaveDir(&ch, dir); err != nil { + t.Fatal(err) + } + linter := &support.Linter{ + ChartDir: filepath.Join(dir, ch.Metadata.Name), + } + Templates(linter, ch.Values, namespace, strict) + if len(linter.Messages) != 0 { + t.Errorf("expected zero messages, got %d", len(linter.Messages)) + for i, msg := range linter.Messages { + t.Logf("Message %d: %q", i, msg) + } + } +} From 8316a403ed16c76c35ce673e163c0ee30b1e8871 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Thu, 16 Apr 2020 15:51:31 -0600 Subject: [PATCH 095/281] bump version to v3.2 Signed-off-by: Matt Butcher (cherry picked from commit 7bffac813db894e06d17bac91d14ea819b5c2310) --- cmd/helm/testdata/output/version-client-shorthand.txt | 2 +- cmd/helm/testdata/output/version-client.txt | 2 +- cmd/helm/testdata/output/version-short.txt | 2 +- cmd/helm/testdata/output/version-template.txt | 2 +- cmd/helm/testdata/output/version.txt | 2 +- internal/version/version.go | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd/helm/testdata/output/version-client-shorthand.txt b/cmd/helm/testdata/output/version-client-shorthand.txt index 8f9ed6136..d613309fe 100644 --- a/cmd/helm/testdata/output/version-client-shorthand.txt +++ b/cmd/helm/testdata/output/version-client-shorthand.txt @@ -1 +1 @@ -version.BuildInfo{Version:"v3.1", GitCommit:"", GitTreeState:"", GoVersion:""} +version.BuildInfo{Version:"v3.2", GitCommit:"", GitTreeState:"", GoVersion:""} diff --git a/cmd/helm/testdata/output/version-client.txt b/cmd/helm/testdata/output/version-client.txt index 8f9ed6136..d613309fe 100644 --- a/cmd/helm/testdata/output/version-client.txt +++ b/cmd/helm/testdata/output/version-client.txt @@ -1 +1 @@ -version.BuildInfo{Version:"v3.1", GitCommit:"", GitTreeState:"", GoVersion:""} +version.BuildInfo{Version:"v3.2", GitCommit:"", GitTreeState:"", GoVersion:""} diff --git a/cmd/helm/testdata/output/version-short.txt b/cmd/helm/testdata/output/version-short.txt index 861668947..4d5034cea 100644 --- a/cmd/helm/testdata/output/version-short.txt +++ b/cmd/helm/testdata/output/version-short.txt @@ -1 +1 @@ -v3.1 +v3.2 diff --git a/cmd/helm/testdata/output/version-template.txt b/cmd/helm/testdata/output/version-template.txt index e5a779bbf..7c09e8d57 100644 --- a/cmd/helm/testdata/output/version-template.txt +++ b/cmd/helm/testdata/output/version-template.txt @@ -1 +1 @@ -Version: v3.1 \ No newline at end of file +Version: v3.2 \ No newline at end of file diff --git a/cmd/helm/testdata/output/version.txt b/cmd/helm/testdata/output/version.txt index 8f9ed6136..d613309fe 100644 --- a/cmd/helm/testdata/output/version.txt +++ b/cmd/helm/testdata/output/version.txt @@ -1 +1 @@ -version.BuildInfo{Version:"v3.1", GitCommit:"", GitTreeState:"", GoVersion:""} +version.BuildInfo{Version:"v3.2", GitCommit:"", GitTreeState:"", GoVersion:""} diff --git a/internal/version/version.go b/internal/version/version.go index fd0616920..baa65a028 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -30,7 +30,7 @@ var ( // Increment major number for new feature additions and behavioral changes. // Increment minor number for bug fixes and performance enhancements. // Increment patch number for critical fixes to existing releases. - version = "v3.1" + version = "v3.2" // metadata is extra build time data metadata = "" From bc515991f8fb7c3294e7f7809778fd360751d5cb Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Tue, 5 May 2020 14:01:21 +0800 Subject: [PATCH 096/281] docs: fix capitalization in a few help messages 1. fixed capitalization in a few help messages 2. use no thrid-person verb. ref #7898 Signed-off-by: Liu Ming --- cmd/helm/docs.go | 2 +- cmd/helm/lint.go | 2 +- cmd/helm/show.go | 8 ++++---- cmd/helm/status.go | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cmd/helm/docs.go b/cmd/helm/docs.go index 2c9020fb9..c974d4014 100644 --- a/cmd/helm/docs.go +++ b/cmd/helm/docs.go @@ -48,7 +48,7 @@ func newDocsCmd(out io.Writer) *cobra.Command { cmd := &cobra.Command{ Use: "docs", - Short: "Generate documentation as markdown or man pages", + Short: "generate documentation as markdown or man pages", Long: docsDesc, Hidden: true, Args: require.NoArgs, diff --git a/cmd/helm/lint.go b/cmd/helm/lint.go index fe39a5741..a7aac172a 100644 --- a/cmd/helm/lint.go +++ b/cmd/helm/lint.go @@ -46,7 +46,7 @@ func newLintCmd(out io.Writer) *cobra.Command { cmd := &cobra.Command{ Use: "lint PATH", - Short: "examines a chart for possible issues", + Short: "examine a chart for possible issues", Long: longLintHelp, RunE: func(cmd *cobra.Command, args []string) error { paths := []string{"."} diff --git a/cmd/helm/show.go b/cmd/helm/show.go index ac38ed5af..b335c5f76 100644 --- a/cmd/helm/show.go +++ b/cmd/helm/show.go @@ -72,7 +72,7 @@ func newShowCmd(out io.Writer) *cobra.Command { all := &cobra.Command{ Use: "all [CHART]", - Short: "shows all information of the chart", + Short: "show all information of the chart", Long: showAllDesc, Args: require.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { @@ -88,7 +88,7 @@ func newShowCmd(out io.Writer) *cobra.Command { valuesSubCmd := &cobra.Command{ Use: "values [CHART]", - Short: "shows the chart's values", + Short: "show the chart's values", Long: showValuesDesc, Args: require.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { @@ -104,7 +104,7 @@ func newShowCmd(out io.Writer) *cobra.Command { chartSubCmd := &cobra.Command{ Use: "chart [CHART]", - Short: "shows the chart's definition", + Short: "show the chart's definition", Long: showChartDesc, Args: require.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { @@ -120,7 +120,7 @@ func newShowCmd(out io.Writer) *cobra.Command { readmeSubCmd := &cobra.Command{ Use: "readme [CHART]", - Short: "shows the chart's README", + Short: "show the chart's README", Long: readmeChartDesc, Args: require.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { diff --git a/cmd/helm/status.go b/cmd/helm/status.go index 34543c6cb..6313b3975 100644 --- a/cmd/helm/status.go +++ b/cmd/helm/status.go @@ -50,7 +50,7 @@ func newStatusCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { cmd := &cobra.Command{ Use: "status RELEASE_NAME", - Short: "displays the status of the named release", + Short: "display the status of the named release", Long: statusHelp, Args: require.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { From be38084eb4d6e0bfb79566083833413947f8bfc8 Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Tue, 5 May 2020 10:27:47 -0400 Subject: [PATCH 097/281] Fixing argument to be lower case Signed-off-by: Matt Farina --- pkg/helmpath/lazypath.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/helmpath/lazypath.go b/pkg/helmpath/lazypath.go index a8a64bfab..22d7bf0a1 100644 --- a/pkg/helmpath/lazypath.go +++ b/pkg/helmpath/lazypath.go @@ -37,7 +37,7 @@ const ( // lazypath is an lazy-loaded path buffer for the XDG base directory specification. type lazypath string -func (l lazypath) path(helmEnvVar, XDGEnvVar string, defaultFn func() string, elem ...string) string { +func (l lazypath) path(helmEnvVar, xdgEnvVar string, defaultFn func() string, elem ...string) string { // There is an order to checking for a path. // 1. See if a Helm specific environment variable has been set. @@ -47,7 +47,7 @@ func (l lazypath) path(helmEnvVar, XDGEnvVar string, defaultFn func() string, el if base != "" { return filepath.Join(base, filepath.Join(elem...)) } - base = os.Getenv(XDGEnvVar) + base = os.Getenv(xdgEnvVar) if base == "" { base = defaultFn() } From e1aaf995a6c238f04eb8449f67feb5f2cb95028f Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Tue, 5 May 2020 17:44:47 +0800 Subject: [PATCH 098/281] refactor: alter constant `pluginFileName` to `PluginFileName` in order to reuse the "plugin.yaml" value in installer package and avoid magic value in installer.go Signed-off-by: Liu Ming --- pkg/plugin/installer/installer.go | 4 +++- pkg/plugin/plugin.go | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/pkg/plugin/installer/installer.go b/pkg/plugin/installer/installer.go index 65c61cd7d..61b49ab3b 100644 --- a/pkg/plugin/installer/installer.go +++ b/pkg/plugin/installer/installer.go @@ -23,6 +23,8 @@ import ( "strings" "github.com/pkg/errors" + + "helm.sh/helm/v3/pkg/plugin" ) // ErrMissingMetadata indicates that plugin.yaml is missing. @@ -100,7 +102,7 @@ func isRemoteHTTPArchive(source string) bool { // isPlugin checks if the directory contains a plugin.yaml file. func isPlugin(dirname string) bool { - _, err := os.Stat(filepath.Join(dirname, "plugin.yaml")) + _, err := os.Stat(filepath.Join(dirname, plugin.PluginFileName)) return err == nil } diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go index 2eb354fca..caa34fbd3 100644 --- a/pkg/plugin/plugin.go +++ b/pkg/plugin/plugin.go @@ -28,7 +28,7 @@ import ( "helm.sh/helm/v3/pkg/cli" ) -const pluginFileName = "plugin.yaml" +const PluginFileName = "plugin.yaml" // Downloaders represents the plugins capability if it can retrieve // charts from special sources @@ -159,7 +159,7 @@ func (p *Plugin) PrepareCommand(extraArgs []string) (string, []string, error) { // LoadDir loads a plugin from the given directory. func LoadDir(dirname string) (*Plugin, error) { - data, err := ioutil.ReadFile(filepath.Join(dirname, pluginFileName)) + data, err := ioutil.ReadFile(filepath.Join(dirname, PluginFileName)) if err != nil { return nil, err } @@ -177,7 +177,7 @@ func LoadDir(dirname string) (*Plugin, error) { func LoadAll(basedir string) ([]*Plugin, error) { plugins := []*Plugin{} // We want basedir/*/plugin.yaml - scanpath := filepath.Join(basedir, "*", pluginFileName) + scanpath := filepath.Join(basedir, "*", PluginFileName) matches, err := filepath.Glob(scanpath) if err != nil { return plugins, err From e4768e646095204c16a124b8176c2c6542561a08 Mon Sep 17 00:00:00 2001 From: Martin Hickey Date: Tue, 5 May 2020 21:04:14 +0000 Subject: [PATCH 099/281] Update lint deprecation list Add api group: - apiextensions.k8s.io/v1beta1 - rbac.authorization.k8s.io/v1alpha1 Also, some kinds moved from extensions/v1 to extensions/v1beta1 Signed-off-by: Martin Hickey --- pkg/lint/rules/deprecations.go | 44 ++++++++++++++++++++--------- pkg/lint/rules/deprecations_test.go | 2 +- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/pkg/lint/rules/deprecations.go b/pkg/lint/rules/deprecations.go index c14fedec6..88921408d 100644 --- a/pkg/lint/rules/deprecations.go +++ b/pkg/lint/rules/deprecations.go @@ -22,20 +22,36 @@ import "fmt" // // An empty rvalue indicates that the API is completely deprecated. var deprecatedAPIs = map[string]string{ - "extensions/v1 Deployment": "apps/v1 Deployment", - "extensions/v1 DaemonSet": "apps/v1 DaemonSet", - "extensions/v1 ReplicaSet": "apps/v1 ReplicaSet", - "extensions/v1beta1 PodSecurityPolicy": "policy/v1beta1 PodSecurityPolicy", - "extensions/v1beta1 NetworkPolicy": "networking.k8s.io/v1beta1 NetworkPolicy", - "extensions/v1beta1 Ingress": "networking.k8s.io/v1beta1 Ingress", - "apps/v1beta1 Deployment": "apps/v1 Deployment", - "apps/v1beta1 StatefulSet": "apps/v1 StatefulSet", - "apps/v1beta1 DaemonSet": "apps/v1 DaemonSet", - "apps/v1beta1 ReplicaSet": "apps/v1 ReplicaSet", - "apps/v1beta2 Deployment": "apps/v1 Deployment", - "apps/v1beta2 StatefulSet": "apps/v1 StatefulSet", - "apps/v1beta2 DaemonSet": "apps/v1 DaemonSet", - "apps/v1beta2 ReplicaSet": "apps/v1 ReplicaSet", + "extensions/v1beta1 Deployment": "apps/v1 Deployment", + "extensions/v1beta1 DaemonSet": "apps/v1 DaemonSet", + "extensions/v1beta1 ReplicaSet": "apps/v1 ReplicaSet", + "extensions/v1beta1 PodSecurityPolicy": "policy/v1beta1 PodSecurityPolicy", + "extensions/v1beta1 NetworkPolicy": "networking.k8s.io/v1beta1 NetworkPolicy", + "extensions/v1beta1 Ingress": "networking.k8s.io/v1beta1 Ingress", + "apps/v1beta1 Deployment": "apps/v1 Deployment", + "apps/v1beta1 StatefulSet": "apps/v1 StatefulSet", + "apps/v1beta1 ReplicaSet": "apps/v1 ReplicaSet", + "apps/v1beta2 Deployment": "apps/v1 Deployment", + "apps/v1beta2 StatefulSet": "apps/v1 StatefulSet", + "apps/v1beta2 DaemonSet": "apps/v1 DaemonSet", + "apps/v1beta2 ReplicaSet": "apps/v1 ReplicaSet", + "apiextensions.k8s.io/v1beta1 CustomResourceDefinition": "apiextensions.k8s.io/v1 CustomResourceDefinition", + "rbac.authorization.k8s.io/v1alpha1 ClusterRole": "rbac.authorization.k8s.io/v1 ClusterRole", + "rbac.authorization.k8s.io/v1alpha1 ClusterRoleList": "rbac.authorization.k8s.io/v1 ClusterRoleList", + "rbac.authorization.k8s.io/v1alpha1 ClusterRoleBinding": "rbac.authorization.k8s.io/v1 ClusterRoleBinding", + "rbac.authorization.k8s.io/v1alpha1 ClusterRoleBindingList": "rbac.authorization.k8s.io/v1 ClusterRoleBindingList", + "rbac.authorization.k8s.io/v1alpha1 Role": "rbac.authorization.k8s.io/v1 Role", + "rbac.authorization.k8s.io/v1alpha1 RoleList": "rbac.authorization.k8s.io/v1 RoleList", + "rbac.authorization.k8s.io/v1alpha1 RoleBinding": "rbac.authorization.k8s.io/v1 RoleBinding", + "rbac.authorization.k8s.io/v1alpha1 RoleBindingList": "rbac.authorization.k8s.io/v1 RoleBindingList", + "rbac.authorization.k8s.io/v1beta1 ClusterRole": "rbac.authorization.k8s.io/v1 ClusterRole", + "rbac.authorization.k8s.io/v1beta1 ClusterRoleList": "rbac.authorization.k8s.io/v1 ClusterRoleList", + "rbac.authorization.k8s.io/v1beta1 ClusterRoleBinding": "rbac.authorization.k8s.io/v1 ClusterRoleBinding", + "rbac.authorization.k8s.io/v1beta1 ClusterRoleBindingList": "rbac.authorization.k8s.io/v1 ClusterRoleBindingList", + "rbac.authorization.k8s.io/v1beta1 Role": "rbac.authorization.k8s.io/v1 Role", + "rbac.authorization.k8s.io/v1beta1 RoleList": "rbac.authorization.k8s.io/v1 RoleList", + "rbac.authorization.k8s.io/v1beta1 RoleBinding": "rbac.authorization.k8s.io/v1 RoleBinding", + "rbac.authorization.k8s.io/v1beta1 RoleBindingList": "rbac.authorization.k8s.io/v1 RoleBindingList", } // deprecatedAPIError indicates than an API is deprecated in Kubernetes diff --git a/pkg/lint/rules/deprecations_test.go b/pkg/lint/rules/deprecations_test.go index f85d58a0c..1e8d34702 100644 --- a/pkg/lint/rules/deprecations_test.go +++ b/pkg/lint/rules/deprecations_test.go @@ -20,7 +20,7 @@ import "testing" func TestValidateNoDeprecations(t *testing.T) { deprecated := &K8sYamlStruct{ - APIVersion: "extensions/v1", + APIVersion: "extensions/v1beta1", Kind: "Deployment", } err := validateNoDeprecations(deprecated) From 93b0eee0dff2849081f16b47f4693fd8b25115bc Mon Sep 17 00:00:00 2001 From: Idan Elhalwani Date: Thu, 7 May 2020 12:11:24 +0300 Subject: [PATCH 100/281] Set DisableCompression to true to disable unwanted httpclient transformation Signed-off-by: Idan Elhalwani --- pkg/getter/httpgetter.go | 29 +++++++++------------ pkg/getter/httpgetter_test.go | 39 ++++++++++++++++++++++++++++ pkg/getter/testdata/empty-0.0.1.tgz | Bin 0 -> 130 bytes 3 files changed, 51 insertions(+), 17 deletions(-) create mode 100644 pkg/getter/testdata/empty-0.0.1.tgz diff --git a/pkg/getter/httpgetter.go b/pkg/getter/httpgetter.go index 695a87743..46c817a9c 100644 --- a/pkg/getter/httpgetter.go +++ b/pkg/getter/httpgetter.go @@ -90,6 +90,10 @@ func NewHTTPGetter(options ...Option) (Getter, error) { } func (g *HTTPGetter) httpClient() (*http.Client, error) { + transport := &http.Transport{ + DisableCompression: true, + Proxy: http.ProxyFromEnvironment, + } 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 { @@ -103,28 +107,19 @@ func (g *HTTPGetter) httpClient() (*http.Client, error) { } tlsConf.ServerName = sni - client := &http.Client{ - Transport: &http.Transport{ - TLSClientConfig: tlsConf, - Proxy: http.ProxyFromEnvironment, - }, - } - - return client, nil + transport.TLSClientConfig = tlsConf } if g.opts.insecureSkipVerifyTLS { - client := &http.Client{ - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: true, - }, - Proxy: http.ProxyFromEnvironment, - }, + transport.TLSClientConfig = &tls.Config{ + InsecureSkipVerify: true, } - return client, nil } - return http.DefaultClient, nil + client := &http.Client{ + Transport: transport, + } + + return client, nil } diff --git a/pkg/getter/httpgetter_test.go b/pkg/getter/httpgetter_test.go index 4d7ada852..0e157eccd 100644 --- a/pkg/getter/httpgetter_test.go +++ b/pkg/getter/httpgetter_test.go @@ -17,10 +17,13 @@ package getter import ( "fmt" + "io" "net/http" "net/http/httptest" "net/url" + "os" "path/filepath" + "strconv" "strings" "testing" @@ -248,3 +251,39 @@ func TestDownloadInsecureSkipTLSVerify(t *testing.T) { } } + +func TestHTTPGetterTarDownload(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + f, _ := os.Open("testdata/empty-0.0.1.tgz") + defer f.Close() + + b := make([]byte, 512) + f.Read(b) + //Get the file size + FileStat, _ := f.Stat() + FileSize := strconv.FormatInt(FileStat.Size(), 10) + + //Simulating improper header values from bitbucket + w.Header().Set("Content-Type", "application/x-tar") + w.Header().Set("Content-Encoding", "gzip") + w.Header().Set("Content-Length", FileSize) + + f.Seek(0, 0) + io.Copy(w, f) + })) + + defer srv.Close() + + g, err := NewHTTPGetter(WithURL(srv.URL)) + if err != nil { + t.Fatal(err) + } + + data, _ := g.Get(srv.URL) + mimeType := http.DetectContentType(data.Bytes()) + + expectedMimeType := "application/x-gzip" + if mimeType != expectedMimeType { + t.Fatalf("Expected response with MIME type %s, but got %s", expectedMimeType, mimeType) + } +} \ No newline at end of file diff --git a/pkg/getter/testdata/empty-0.0.1.tgz b/pkg/getter/testdata/empty-0.0.1.tgz new file mode 100644 index 0000000000000000000000000000000000000000..6c4c3d20597f344826ec27c18aeca1ae585392b7 GIT binary patch literal 130 zcmb2|=3v-$Wpf+@^V Date: Sat, 9 May 2020 16:57:16 +0530 Subject: [PATCH 101/281] Fixes repo parsing Signed-off-by: Juned Memon --- internal/experimental/registry/reference.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/internal/experimental/registry/reference.go b/internal/experimental/registry/reference.go index ced6cf33a..73c409ba2 100644 --- a/internal/experimental/registry/reference.go +++ b/internal/experimental/registry/reference.go @@ -34,6 +34,10 @@ var ( referenceDelimiter = regexp.MustCompile(`[:]`) errEmptyRepo = errors.New("parsed repo was empty") errTooManyColons = errors.New("ref may only contain a single colon character (:) unless specifying a port number") + // ErrInvalid is returned when there is an invalid reference + ErrInvalid = errors.New("invalid reference") + // ErrHostnameRequired is returned when the hostname is required + ErrHostnameRequired = errors.New("hostname required") ) type ( @@ -86,6 +90,7 @@ func (ref *Reference) FullName() string { // validate makes sure the ref meets our criteria func (ref *Reference) validate() error { + err := ref.validateRepo() if err != nil { return err @@ -100,7 +105,15 @@ func (ref *Reference) validateRepo() error { } // Makes sure the repo results in a parsable URL (similar to what is done // with containerd reference parsing) - _, err := url.Parse(ref.Repo) + u, err := url.Parse("dummy://" + ref.Repo) + + if u.Scheme != "dummy" { + return ErrInvalid + } + + if u.Host == "" { + return ErrHostnameRequired + } return err } From 8cb9ab7095c885e1f8d9bea3a28229f58ba59a5e Mon Sep 17 00:00:00 2001 From: Juned Memon Date: Sat, 9 May 2020 18:05:28 +0530 Subject: [PATCH 102/281] Fixes repo parsing Signed-off-by: Juned Memon --- internal/experimental/registry/reference.go | 14 +------------- internal/experimental/registry/reference_test.go | 7 +++++++ 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/internal/experimental/registry/reference.go b/internal/experimental/registry/reference.go index 73c409ba2..13d3d046f 100644 --- a/internal/experimental/registry/reference.go +++ b/internal/experimental/registry/reference.go @@ -34,10 +34,6 @@ var ( referenceDelimiter = regexp.MustCompile(`[:]`) errEmptyRepo = errors.New("parsed repo was empty") errTooManyColons = errors.New("ref may only contain a single colon character (:) unless specifying a port number") - // ErrInvalid is returned when there is an invalid reference - ErrInvalid = errors.New("invalid reference") - // ErrHostnameRequired is returned when the hostname is required - ErrHostnameRequired = errors.New("hostname required") ) type ( @@ -105,15 +101,7 @@ func (ref *Reference) validateRepo() error { } // Makes sure the repo results in a parsable URL (similar to what is done // with containerd reference parsing) - u, err := url.Parse("dummy://" + ref.Repo) - - if u.Scheme != "dummy" { - return ErrInvalid - } - - if u.Host == "" { - return ErrHostnameRequired - } + _, err := url.Parse("dummy://" + ref.Repo) return err } diff --git a/internal/experimental/registry/reference_test.go b/internal/experimental/registry/reference_test.go index bb53ebab8..aae03ad99 100644 --- a/internal/experimental/registry/reference_test.go +++ b/internal/experimental/registry/reference_test.go @@ -81,6 +81,13 @@ func TestParseReference(t *testing.T) { is.Equal("1.5.0", ref.Tag) is.Equal("myrepo:5001/mychart:1.5.0", ref.FullName()) + s = "127.0.0.1:5001/mychart:1.5.0" + ref, err = ParseReference(s) + is.NoError(err) + is.Equal("127.0.0.1:5001/mychart", ref.Repo) + is.Equal("1.5.0", ref.Tag) + is.Equal("127.0.0.1:5001/mychart:1.5.0", ref.FullName()) + s = "localhost:5000/mychart:latest" ref, err = ParseReference(s) is.NoError(err) From 95e9e18ab585ce3e67c373841efae378886189ee Mon Sep 17 00:00:00 2001 From: Juned Memon Date: Mon, 11 May 2020 07:51:45 +0530 Subject: [PATCH 103/281] Removed scheme Signed-off-by: Juned Memon --- internal/experimental/registry/reference.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/experimental/registry/reference.go b/internal/experimental/registry/reference.go index 13d3d046f..ebab29a7e 100644 --- a/internal/experimental/registry/reference.go +++ b/internal/experimental/registry/reference.go @@ -101,7 +101,7 @@ func (ref *Reference) validateRepo() error { } // Makes sure the repo results in a parsable URL (similar to what is done // with containerd reference parsing) - _, err := url.Parse("dummy://" + ref.Repo) + _, err := url.Parse("//" + ref.Repo) return err } From 59eed4e81f840bfa8a5aa69fc6c92471413609eb Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Tue, 12 May 2020 14:23:25 -0600 Subject: [PATCH 104/281] feat: make the linter coalesce the passed-in values before running values tests (#7984) * fix: make the linter coalesce the passed-in values before running values tests Signed-off-by: Matt Butcher * fixed typo Signed-off-by: Matt Butcher --- internal/test/ensure/ensure.go | 19 +++++++ pkg/lint/lint.go | 2 +- pkg/lint/rules/values.go | 23 +++++++- pkg/lint/rules/values_test.go | 97 ++++++++++++++++++++++++++++++++-- 4 files changed, 135 insertions(+), 6 deletions(-) diff --git a/internal/test/ensure/ensure.go b/internal/test/ensure/ensure.go index 6219ad626..3c0e4575c 100644 --- a/internal/test/ensure/ensure.go +++ b/internal/test/ensure/ensure.go @@ -19,6 +19,7 @@ package ensure import ( "io/ioutil" "os" + "path/filepath" "testing" "helm.sh/helm/v3/pkg/helmpath" @@ -49,3 +50,21 @@ func TempDir(t *testing.T) string { } return d } + +// TempFile ensures a temp file for unit testing purposes. +// +// It returns the path to the directory (to which you will still need to join the filename) +// +// You must clean up the directory that is returned. +// +// tempdir := TempFile(t, "foo", []byte("bar")) +// defer os.RemoveAll(tempdir) +// filename := filepath.Join(tempdir, "foo") +func TempFile(t *testing.T, name string, data []byte) string { + path := TempDir(t) + filename := filepath.Join(path, name) + if err := ioutil.WriteFile(filename, data, 0755); err != nil { + t.Fatal(err) + } + return path +} diff --git a/pkg/lint/lint.go b/pkg/lint/lint.go index d47951671..223ead75a 100644 --- a/pkg/lint/lint.go +++ b/pkg/lint/lint.go @@ -30,7 +30,7 @@ func All(basedir string, values map[string]interface{}, namespace string, strict linter := support.Linter{ChartDir: chartDir} rules.Chartfile(&linter) - rules.Values(&linter) + rules.ValuesWithOverrides(&linter, values) rules.Templates(&linter, values, namespace, strict) return linter } diff --git a/pkg/lint/rules/values.go b/pkg/lint/rules/values.go index 0f202f475..c596687c5 100644 --- a/pkg/lint/rules/values.go +++ b/pkg/lint/rules/values.go @@ -28,7 +28,19 @@ import ( ) // Values lints a chart's values.yaml file. +// +// This function is deprecated and will be removed in Helm 4. func Values(linter *support.Linter) { + ValuesWithOverrides(linter, map[string]interface{}{}) +} + +// ValuesWithOverrides tests the values.yaml file. +// +// If a schema is present in the chart, values are tested against that. Otherwise, +// they are only tested for well-formedness. +// +// If additional values are supplied, they are coalesced into the values in values.yaml. +func ValuesWithOverrides(linter *support.Linter, values map[string]interface{}) { file := "values.yaml" vf := filepath.Join(linter.ChartDir, file) fileExists := linter.RunLinterRule(support.InfoSev, file, validateValuesFileExistence(vf)) @@ -37,7 +49,7 @@ func Values(linter *support.Linter) { return } - linter.RunLinterRule(support.ErrorSev, file, validateValuesFile(vf)) + linter.RunLinterRule(support.ErrorSev, file, validateValuesFile(vf, values)) } func validateValuesFileExistence(valuesPath string) error { @@ -48,12 +60,19 @@ func validateValuesFileExistence(valuesPath string) error { return nil } -func validateValuesFile(valuesPath string) error { +func validateValuesFile(valuesPath string, overrides map[string]interface{}) error { values, err := chartutil.ReadValuesFile(valuesPath) if err != nil { return errors.Wrap(err, "unable to parse YAML") } + // Helm 3.0.0 carried over the values linting from Helm 2.x, which only tests the top + // level values against the top-level expectations. Subchart values are not linted. + // We could change that. For now, though, we retain that strategy, and thus can + // coalesce tables (like reuse-values does) instead of doing the full chart + // CoalesceValues. + values = chartutil.CoalesceTables(values, overrides) + ext := filepath.Ext(valuesPath) schemaPath := valuesPath[:len(valuesPath)-len(ext)] + ".schema.json" schema, err := ioutil.ReadFile(schemaPath) diff --git a/pkg/lint/rules/values_test.go b/pkg/lint/rules/values_test.go index 901a739fd..6d93d630e 100644 --- a/pkg/lint/rules/values_test.go +++ b/pkg/lint/rules/values_test.go @@ -17,15 +17,41 @@ limitations under the License. package rules import ( + "io/ioutil" "os" "path/filepath" "testing" -) -var ( - nonExistingValuesFilePath = filepath.Join("/fake/dir", "values.yaml") + "github.com/stretchr/testify/assert" + + "helm.sh/helm/v3/internal/test/ensure" ) +var nonExistingValuesFilePath = filepath.Join("/fake/dir", "values.yaml") + +const testSchema = ` +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "helm values test schema", + "type": "object", + "additionalProperties": false, + "required": [ + "username", + "password" + ], + "properties": { + "username": { + "description": "Your username", + "type": "string" + }, + "password": { + "description": "Your password", + "type": "string" + } + } +} +` + func TestValidateValuesYamlNotDirectory(t *testing.T) { _ = os.Mkdir(nonExistingValuesFilePath, os.ModePerm) defer os.Remove(nonExistingValuesFilePath) @@ -35,3 +61,68 @@ func TestValidateValuesYamlNotDirectory(t *testing.T) { t.Errorf("validateValuesFileExistence to return a linter error, got no error") } } + +func TestValidateValuesFileWellFormed(t *testing.T) { + badYaml := ` + not:well[]{}formed + ` + tmpdir := ensure.TempFile(t, "values.yaml", []byte(badYaml)) + defer os.RemoveAll(tmpdir) + valfile := filepath.Join(tmpdir, "values.yaml") + if err := validateValuesFile(valfile, map[string]interface{}{}); err == nil { + t.Fatal("expected values file to fail parsing") + } +} + +func TestValidateValuesFileSchema(t *testing.T) { + yaml := "username: admin\npassword: swordfish" + tmpdir := ensure.TempFile(t, "values.yaml", []byte(yaml)) + defer os.RemoveAll(tmpdir) + createTestingSchema(t, tmpdir) + + valfile := filepath.Join(tmpdir, "values.yaml") + if err := validateValuesFile(valfile, map[string]interface{}{}); err != nil { + t.Fatalf("Failed validation with %s", err) + } +} + +func TestValidateValuesFileSchemaFailure(t *testing.T) { + // 1234 is an int, not a string. This should fail. + yaml := "username: 1234\npassword: swordfish" + tmpdir := ensure.TempFile(t, "values.yaml", []byte(yaml)) + defer os.RemoveAll(tmpdir) + createTestingSchema(t, tmpdir) + + valfile := filepath.Join(tmpdir, "values.yaml") + + err := validateValuesFile(valfile, map[string]interface{}{}) + if err == nil { + t.Fatal("expected values file to fail parsing") + } + + assert.Contains(t, err.Error(), "Expected: string, given: integer", "integer should be caught by schema") +} + +func TestValidateValuesFileSchemaOverrides(t *testing.T) { + yaml := "username: admin" + overrides := map[string]interface{}{ + "password": "swordfish", + } + tmpdir := ensure.TempFile(t, "values.yaml", []byte(yaml)) + defer os.RemoveAll(tmpdir) + createTestingSchema(t, tmpdir) + + valfile := filepath.Join(tmpdir, "values.yaml") + if err := validateValuesFile(valfile, overrides); err != nil { + t.Fatalf("Failed validation with %s", err) + } +} + +func createTestingSchema(t *testing.T, dir string) string { + t.Helper() + schemafile := filepath.Join(dir, "values.schema.json") + if err := ioutil.WriteFile(schemafile, []byte(testSchema), 0700); err != nil { + t.Fatalf("Failed to write schema to tmpdir: %s", err) + } + return schemafile +} From bf12ae39344aef012927ab391eb26ddd3a30ae30 Mon Sep 17 00:00:00 2001 From: Niels de Vos Date: Wed, 13 May 2020 01:08:53 +0200 Subject: [PATCH 105/281] scripts: do not use optional 'which' command in get-helm installation (#8048) When installing Helm, the following warning gets printed on the console: Downloading https://get.helm.sh/helm-v2.16.6-linux-amd64.tar.gz Preparing to install helm and tiller into /usr/local/bin helm installed into /usr/local/bin/helm tiller installed into /usr/local/bin/tiller main: line 178: which: command not found Run 'helm init' to configure helm. The 'which' command is optional, and not always installed on all environments (like a Fedora container). Instead, use 'command -v' to detect if the executable is in the $PATH. Signed-off-by: Niels de Vos --- scripts/get | 2 +- scripts/get-helm-3 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/get b/scripts/get index afa02bbb1..094892346 100755 --- a/scripts/get +++ b/scripts/get @@ -175,7 +175,7 @@ fail_trap() { # testVersion tests the installed client to make sure it is working. testVersion() { set +e - HELM="$(which $PROJECT_NAME)" + HELM="$(command -v $PROJECT_NAME)" if [ "$?" = "1" ]; then echo "$PROJECT_NAME not found. Is $HELM_INSTALL_DIR on your "'$PATH?' exit 1 diff --git a/scripts/get-helm-3 b/scripts/get-helm-3 index 201065717..1d0b3502e 100755 --- a/scripts/get-helm-3 +++ b/scripts/get-helm-3 @@ -165,7 +165,7 @@ fail_trap() { # testVersion tests the installed client to make sure it is working. testVersion() { set +e - HELM="$(which $BINARY_NAME)" + HELM="$(command -v $BINARY_NAME)" if [ "$?" = "1" ]; then echo "$BINARY_NAME not found. Is $HELM_INSTALL_DIR on your "'$PATH?' exit 1 From 75aa425bfcfdffdbdd285a03fb0fceab8e28c778 Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Wed, 13 May 2020 08:36:47 -0700 Subject: [PATCH 106/281] bump to kubernetes 1.18.2 Signed-off-by: Matthew Fisher --- go.mod | 12 ++++++------ go.sum | 43 +++++++++++++++++-------------------------- 2 files changed, 23 insertions(+), 32 deletions(-) diff --git a/go.mod b/go.mod index 64ebfe307..f23d152a2 100644 --- a/go.mod +++ b/go.mod @@ -34,13 +34,13 @@ require ( github.com/stretchr/testify v1.5.1 github.com/xeipuuv/gojsonschema v1.1.0 golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 - k8s.io/api v0.18.0 - k8s.io/apiextensions-apiserver v0.18.0 - k8s.io/apimachinery v0.18.0 - k8s.io/cli-runtime v0.18.0 - k8s.io/client-go v0.18.0 + k8s.io/api v0.18.2 + k8s.io/apiextensions-apiserver v0.18.2 + k8s.io/apimachinery v0.18.2 + k8s.io/cli-runtime v0.18.2 + k8s.io/client-go v0.18.2 k8s.io/klog v1.0.0 - k8s.io/kubectl v0.18.0 + k8s.io/kubectl v0.18.2 sigs.k8s.io/yaml v1.2.0 ) diff --git a/go.sum b/go.sum index 0a51a72e8..2908f3eb8 100644 --- a/go.sum +++ b/go.sum @@ -29,8 +29,6 @@ github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd h1:sjQovDkwrZp github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver/v3 v3.0.3 h1:znjIyLfpXEDQjOIEWh+ehwpTU14UzUPub3c3sm36u14= -github.com/Masterminds/semver/v3 v3.0.3/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/semver/v3 v3.1.0 h1:Y2lUDsFKVRSYGojLJ1yLxSXdMmMYTYls0rCvoqmMUQk= github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/sprig/v3 v3.1.0 h1:j7GpgZ7PdFqNsmncycTHsLmVPf5/3wJtlgW9TNDYD9Y= @@ -155,8 +153,6 @@ github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QL github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e h1:p1yVGRW3nmb85p1Sh1ZJSDm4A4iKLS5QNbvUHMgGu/M= -github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= @@ -321,13 +317,9 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0= -github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI= -github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= @@ -587,7 +579,6 @@ golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d h1:9FCpayM9Egr1baVnV1SX0H87m+XB0B8S0hAMi99X/3U= golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo= @@ -737,20 +728,20 @@ gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -k8s.io/api v0.18.0 h1:lwYk8Vt7rsVTwjRU6pzEsa9YNhThbmbocQlKvNBB4EQ= -k8s.io/api v0.18.0/go.mod h1:q2HRQkfDzHMBZL9l/y9rH63PkQl4vae0xRT+8prbrK8= -k8s.io/apiextensions-apiserver v0.18.0 h1:HN4/P8vpGZFvB5SOMuPPH2Wt9Y/ryX+KRvIyAkchu1Q= -k8s.io/apiextensions-apiserver v0.18.0/go.mod h1:18Cwn1Xws4xnWQNC00FLq1E350b9lUF+aOdIWDOZxgo= -k8s.io/apimachinery v0.18.0 h1:fuPfYpk3cs1Okp/515pAf0dNhL66+8zk8RLbSX+EgAE= -k8s.io/apimachinery v0.18.0/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= -k8s.io/apiserver v0.18.0/go.mod h1:3S2O6FeBBd6XTo0njUrLxiqk8GNy6wWOftjhJcXYnjw= -k8s.io/cli-runtime v0.18.0 h1:jG8XpSqQ5TrV0N+EZ3PFz6+gqlCk71dkggWCCq9Mq34= -k8s.io/cli-runtime v0.18.0/go.mod h1:1eXfmBsIJosjn9LjEBUd2WVPoPAY9XGTqTFcPMIBsUQ= -k8s.io/client-go v0.18.0 h1:yqKw4cTUQraZK3fcVCMeSa+lqKwcjZ5wtcOIPnxQno4= -k8s.io/client-go v0.18.0/go.mod h1:uQSYDYs4WhVZ9i6AIoEZuwUggLVEF64HOD37boKAtF8= -k8s.io/code-generator v0.18.0/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc= -k8s.io/component-base v0.18.0 h1:I+lP0fNfsEdTDpHaL61bCAqTZLoiWjEEP304Mo5ZQgE= -k8s.io/component-base v0.18.0/go.mod h1:u3BCg0z1uskkzrnAKFzulmYaEpZF7XC9Pf/uFyb1v2c= +k8s.io/api v0.18.2 h1:wG5g5ZmSVgm5B+eHMIbI9EGATS2L8Z72rda19RIEgY8= +k8s.io/api v0.18.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78= +k8s.io/apiextensions-apiserver v0.18.2 h1:I4v3/jAuQC+89L3Z7dDgAiN4EOjN6sbm6iBqQwHTah8= +k8s.io/apiextensions-apiserver v0.18.2/go.mod h1:q3faSnRGmYimiocj6cHQ1I3WpLqmDgJFlKL37fC4ZvY= +k8s.io/apimachinery v0.18.2 h1:44CmtbmkzVDAhCpRVSiP2R5PPrC2RtlIv/MoB8xpdRA= +k8s.io/apimachinery v0.18.2/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= +k8s.io/apiserver v0.18.2/go.mod h1:Xbh066NqrZO8cbsoenCwyDJ1OSi8Ag8I2lezeHxzwzw= +k8s.io/cli-runtime v0.18.2 h1:JiTN5RgkFNTiMxHBRyrl6n26yKWAuNRlei1ZJALUmC8= +k8s.io/cli-runtime v0.18.2/go.mod h1:yfFR2sQQzDsV0VEKGZtrJwEy4hLZ2oj4ZIfodgxAHWQ= +k8s.io/client-go v0.18.2 h1:aLB0iaD4nmwh7arT2wIn+lMnAq7OswjaejkQ8p9bBYE= +k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU= +k8s.io/code-generator v0.18.2/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc= +k8s.io/component-base v0.18.2 h1:SJweNZAGcUvsypLGNPNGeJ9UgPZQ6+bW+gEHe8uyh/Y= +k8s.io/component-base v0.18.2/go.mod h1:kqLlMuhJNHQ9lz8Z7V5bxUUtjFZnrypArGl58gmDfUM= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= @@ -759,10 +750,10 @@ k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c h1:/KUFqjjqAcY4Us6luF5RDNZ16KJtb49HfR3ZHB9qYXM= k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= -k8s.io/kubectl v0.18.0 h1:hu52Ndq/d099YW+3sS3VARxFz61Wheiq8K9S7oa82Dk= -k8s.io/kubectl v0.18.0/go.mod h1:LOkWx9Z5DXMEg5KtOjHhRiC1fqJPLyCr3KtQgEolCkU= +k8s.io/kubectl v0.18.2 h1:9jnGSOC2DDVZmMUTMi0D1aed438mfQcgqa5TAzVjA1k= +k8s.io/kubectl v0.18.2/go.mod h1:OdgFa3AlsPKRpFFYE7ICTwulXOcMGXHTc+UKhHKvrb4= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= -k8s.io/metrics v0.18.0/go.mod h1:8aYTW18koXqjLVKL7Ds05RPMX9ipJZI3mywYvBOxXd4= +k8s.io/metrics v0.18.2/go.mod h1:qga8E7QfYNR9Q89cSCAjinC9pTZ7yv1XSVGUB0vJypg= k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89 h1:d4vVOjXm687F1iLSP2q3lyPPuyvTUt3aVoBpi2DqRsU= k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0= From bfb39c0c68b989eea6a0b648f36aa5c7f930a515 Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Wed, 13 May 2020 11:10:47 -0700 Subject: [PATCH 107/281] bump DefaultCapabilities to 1.18 Signed-off-by: Matthew Fisher --- .../testdata/output/template-name-template.txt | 4 ++-- cmd/helm/testdata/output/template-set.txt | 4 ++-- .../output/template-show-only-multiple.txt | 4 ++-- .../testdata/output/template-show-only-one.txt | 4 ++-- cmd/helm/testdata/output/template-values-files.txt | 4 ++-- .../testdata/output/template-with-api-version.txt | 4 ++-- cmd/helm/testdata/output/template-with-crds.txt | 4 ++-- cmd/helm/testdata/output/template.txt | 4 ++-- pkg/chartutil/capabilities.go | 4 ++-- pkg/chartutil/capabilities_test.go | 14 +++++++------- 10 files changed, 25 insertions(+), 25 deletions(-) diff --git a/cmd/helm/testdata/output/template-name-template.txt b/cmd/helm/testdata/output/template-name-template.txt index 0130a2a92..24f2bb616 100644 --- a/cmd/helm/testdata/output/template-name-template.txt +++ b/cmd/helm/testdata/output/template-name-template.txt @@ -71,8 +71,8 @@ metadata: helm.sh/chart: "subchart1-0.1.0" app.kubernetes.io/instance: "foobar-YWJj-baz" kube-version/major: "1" - kube-version/minor: "16" - kube-version/version: "v1.16.0" + kube-version/minor: "18" + kube-version/version: "v1.18.0" spec: type: ClusterIP ports: diff --git a/cmd/helm/testdata/output/template-set.txt b/cmd/helm/testdata/output/template-set.txt index ddaa8886b..4c7f1f626 100644 --- a/cmd/helm/testdata/output/template-set.txt +++ b/cmd/helm/testdata/output/template-set.txt @@ -71,8 +71,8 @@ metadata: 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-version/minor: "18" + kube-version/version: "v1.18.0" spec: type: ClusterIP ports: diff --git a/cmd/helm/testdata/output/template-show-only-multiple.txt b/cmd/helm/testdata/output/template-show-only-multiple.txt index abb9a2e10..75b590e20 100644 --- a/cmd/helm/testdata/output/template-show-only-multiple.txt +++ b/cmd/helm/testdata/output/template-show-only-multiple.txt @@ -8,8 +8,8 @@ metadata: 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-version/minor: "18" + kube-version/version: "v1.18.0" kube-api-version/test: v1 spec: type: ClusterIP diff --git a/cmd/helm/testdata/output/template-show-only-one.txt b/cmd/helm/testdata/output/template-show-only-one.txt index f0dd0834e..ce61f481f 100644 --- a/cmd/helm/testdata/output/template-show-only-one.txt +++ b/cmd/helm/testdata/output/template-show-only-one.txt @@ -8,8 +8,8 @@ metadata: 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-version/minor: "18" + kube-version/version: "v1.18.0" kube-api-version/test: v1 spec: type: ClusterIP diff --git a/cmd/helm/testdata/output/template-values-files.txt b/cmd/helm/testdata/output/template-values-files.txt index ddaa8886b..4c7f1f626 100644 --- a/cmd/helm/testdata/output/template-values-files.txt +++ b/cmd/helm/testdata/output/template-values-files.txt @@ -71,8 +71,8 @@ metadata: 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-version/minor: "18" + kube-version/version: "v1.18.0" spec: type: ClusterIP ports: diff --git a/cmd/helm/testdata/output/template-with-api-version.txt b/cmd/helm/testdata/output/template-with-api-version.txt index 7a2a4d5bf..210dcc503 100644 --- a/cmd/helm/testdata/output/template-with-api-version.txt +++ b/cmd/helm/testdata/output/template-with-api-version.txt @@ -71,8 +71,8 @@ metadata: 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-version/minor: "18" + kube-version/version: "v1.18.0" kube-api-version/test: v1 spec: type: ClusterIP diff --git a/cmd/helm/testdata/output/template-with-crds.txt b/cmd/helm/testdata/output/template-with-crds.txt index b04a4d5d2..289eec291 100644 --- a/cmd/helm/testdata/output/template-with-crds.txt +++ b/cmd/helm/testdata/output/template-with-crds.txt @@ -87,8 +87,8 @@ metadata: 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-version/minor: "18" + kube-version/version: "v1.18.0" kube-api-version/test: v1 spec: type: ClusterIP diff --git a/cmd/helm/testdata/output/template.txt b/cmd/helm/testdata/output/template.txt index 8301c2231..82a21c5a1 100644 --- a/cmd/helm/testdata/output/template.txt +++ b/cmd/helm/testdata/output/template.txt @@ -71,8 +71,8 @@ metadata: 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-version/minor: "18" + kube-version/version: "v1.18.0" spec: type: ClusterIP ports: diff --git a/pkg/chartutil/capabilities.go b/pkg/chartutil/capabilities.go index f46350bb1..ce968c5d7 100644 --- a/pkg/chartutil/capabilities.go +++ b/pkg/chartutil/capabilities.go @@ -29,9 +29,9 @@ var ( // DefaultCapabilities is the default set of capabilities. DefaultCapabilities = &Capabilities{ KubeVersion: KubeVersion{ - Version: "v1.16.0", + Version: "v1.18.0", Major: "1", - Minor: "16", + Minor: "18", }, APIVersions: DefaultVersionSet, } diff --git a/pkg/chartutil/capabilities_test.go b/pkg/chartutil/capabilities_test.go index 3eeac76e2..416eea06d 100644 --- a/pkg/chartutil/capabilities_test.go +++ b/pkg/chartutil/capabilities_test.go @@ -42,19 +42,19 @@ func TestDefaultVersionSet(t *testing.T) { func TestDefaultCapabilities(t *testing.T) { kv := DefaultCapabilities.KubeVersion - if kv.String() != "v1.16.0" { - t.Errorf("Expected default KubeVersion.String() to be v1.16.0, got %q", kv.String()) + if kv.String() != "v1.18.0" { + t.Errorf("Expected default KubeVersion.String() to be v1.18.0, got %q", kv.String()) } - if kv.Version != "v1.16.0" { - t.Errorf("Expected default KubeVersion.Version to be v1.16.0, got %q", kv.Version) + if kv.Version != "v1.18.0" { + t.Errorf("Expected default KubeVersion.Version to be v1.18.0, got %q", kv.Version) } - if kv.GitVersion() != "v1.16.0" { - t.Errorf("Expected default KubeVersion.GitVersion() to be v1.16.0, got %q", kv.Version) + if kv.GitVersion() != "v1.18.0" { + t.Errorf("Expected default KubeVersion.GitVersion() to be v1.18.0, got %q", kv.Version) } if kv.Major != "1" { t.Errorf("Expected default KubeVersion.Major to be 1, got %q", kv.Major) } - if kv.Minor != "16" { + if kv.Minor != "18" { t.Errorf("Expected default KubeVersion.Minor to be 16, got %q", kv.Minor) } } From 2f39854d3f5da2f13cd749ccb08d61982cafef2f Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Wed, 13 May 2020 12:03:12 -0700 Subject: [PATCH 108/281] fix security mailing list address Signed-off-by: Matthew Fisher --- CONTRIBUTING.md | 206 +++++++++++++++++++++++++++--------------------- 1 file changed, 115 insertions(+), 91 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a637f9255..e27fa7d19 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,21 +1,22 @@ # Contributing Guidelines -The Helm project accepts contributions via GitHub pull requests. This document outlines the process to help get your contribution accepted. +The Helm project accepts contributions via GitHub pull requests. This document outlines the process +to help get your contribution accepted. ## Reporting a Security Issue -Most of the time, when you find a bug in Helm, it should be reported -using [GitHub issues](https://github.com/helm/helm/issues). However, if -you are reporting a _security vulnerability_, please email a report to -[cncf-kubernetes-helm-security@lists.cncf.io](mailto:cncf-kubernetes-helm-security@lists.cncf.io). This will give -us a chance to try to fix the issue before it is exploited in the wild. +Most of the time, when you find a bug in Helm, it should be reported using [GitHub +issues](https://github.com/helm/helm/issues). However, if you are reporting a _security +vulnerability_, please email a report to +[cncf-helm-security@lists.cncf.io](mailto:cncf-helm-security@lists.cncf.io). This will give us a +chance to try to fix the issue before it is exploited in the wild. ## Sign Your Work -The sign-off is a simple line at the end of the explanation for a commit. All -commits needs to be signed. Your signature certifies that you wrote the patch or -otherwise have the right to contribute the material. The rules are pretty simple, -if you can certify the below (from [developercertificate.org](https://developercertificate.org/)): +The sign-off is a simple line at the end of the explanation for a commit. All commits needs to be +signed. Your signature certifies that you wrote the patch or otherwise have the right to contribute +the material. The rules are pretty simple, if you can certify the below (from +[developercertificate.org](https://developercertificate.org/)): ``` Developer Certificate of Origin @@ -62,11 +63,11 @@ Then you just add a line to every git commit message: Use your real name (sorry, no pseudonyms or anonymous contributions.) -If you set your `user.name` and `user.email` git configs, you can sign your -commit automatically with `git commit -s`. +If you set your `user.name` and `user.email` git configs, you can sign your commit automatically +with `git commit -s`. -Note: If your git config information is set properly then viewing the - `git log` information for your commit will look something like this: +Note: If your git config information is set properly then viewing the `git log` information for your + commit will look something like this: ``` Author: Joe Smith @@ -77,8 +78,8 @@ Date: Thu Feb 2 11:41:15 2018 -0800 Signed-off-by: Joe Smith ``` -Notice the `Author` and `Signed-off-by` lines match. If they don't -your PR will be rejected by the automated DCO check. +Notice the `Author` and `Signed-off-by` lines match. If they don't your PR will be rejected by the +automated DCO check. ## Support Channels @@ -89,49 +90,69 @@ Whether you are a user or contributor, official support channels include: - User: [#helm-users](https://kubernetes.slack.com/messages/C0NH30761/details/) - Contributor: [#helm-dev](https://kubernetes.slack.com/messages/C51E88VDG/) -Before opening a new issue or submitting a new pull request, it's helpful to search the project - it's likely that another user has already reported the issue you're facing, or it's a known issue that we're already aware of. It is also worth asking on the Slack channels. +Before opening a new issue or submitting a new pull request, it's helpful to search the project - +it's likely that another user has already reported the issue you're facing, or it's a known issue +that we're already aware of. It is also worth asking on the Slack channels. ## Milestones -We use milestones to track progress of releases. There are also 2 special milestones -used for helping us keep work organized: `Upcoming - Minor` and `Upcoming - Major` +We use milestones to track progress of releases. There are also 2 special milestones used for +helping us keep work organized: `Upcoming - Minor` and `Upcoming - Major` -`Upcoming - Minor` is used for keeping track of issues that aren't assigned to a specific -release but could easily be addressed in a minor release. `Upcoming - Major` keeps track -of issues that will need to be addressed in a major release. For example, if the current -version is `3.2.0` an issue/PR could fall in to one of 4 different active milestones: -`3.2.1`, `3.3.0`, `Upcoming - Minor`, or `Upcoming - Major`. If an issue pertains to a -specific upcoming bug or minor release, it would go into `3.2.1` or `3.3.0`. If the issue/PR -does not have a specific milestone yet, but it is likely that it will land in a `3.X` release, -it should go into `Upcoming - Minor`. If the issue/PR is a large functionality add or change -and/or it breaks compatibility, then it should be added to the `Upcoming - Major` milestone. -An issue that we are not sure we will be doing will not be added to any milestone. +`Upcoming - Minor` is used for keeping track of issues that aren't assigned to a specific release +but could easily be addressed in a minor release. `Upcoming - Major` keeps track of issues that will +need to be addressed in a major release. For example, if the current version is `3.2.0` an issue/PR +could fall in to one of 4 different active milestones: `3.2.1`, `3.3.0`, `Upcoming - Minor`, or +`Upcoming - Major`. If an issue pertains to a specific upcoming bug or minor release, it would go +into `3.2.1` or `3.3.0`. If the issue/PR does not have a specific milestone yet, but it is likely +that it will land in a `3.X` release, it should go into `Upcoming - Minor`. If the issue/PR is a +large functionality add or change and/or it breaks compatibility, then it should be added to the +`Upcoming - Major` milestone. An issue that we are not sure we will be doing will not be added to +any milestone. -A milestone (and hence release) is considered done when all outstanding issues/PRs have been closed or moved to another milestone. +A milestone (and hence release) is considered done when all outstanding issues/PRs have been closed +or moved to another milestone. ## Semantic Versioning -Helm maintains a strong commitment to backward compatibility. All of our changes to protocols and formats are backward compatible from one major release to the next. No features, flags, or commands are removed or substantially modified (unless we need to fix a security issue). +Helm maintains a strong commitment to backward compatibility. All of our changes to protocols and +formats are backward compatible from one major release to the next. No features, flags, or commands +are removed or substantially modified (unless we need to fix a security issue). -We also try very hard to not change publicly accessible Go library definitions inside of the `pkg/` directory of our source code. +We also try very hard to not change publicly accessible Go library definitions inside of the `pkg/` +directory of our source code. For a quick summary of our backward compatibility guidelines for releases between 3.0 and 4.0: - Command line commands, flags, and arguments MUST be backward compatible - File formats (such as Chart.yaml) MUST be backward compatible -- Any chart that worked on a previous version of Helm 3 MUST work on a new version of Helm 3 (barring the cases where (a) Kubernetes itself changed, and (b) the chart worked because it exploited a bug) +- Any chart that worked on a previous version of Helm 3 MUST work on a new version of Helm 3 + (barring the cases where (a) Kubernetes itself changed, and (b) the chart worked because it + exploited a bug) - Chart repository functionality MUST be backward compatible -- Go libraries inside of `pkg/` SHOULD remain backward compatible, though code inside of `cmd/` and `internal/` may be changed from release to release without notice. +- Go libraries inside of `pkg/` SHOULD remain backward compatible, though code inside of `cmd/` and + `internal/` may be changed from release to release without notice. ## Support Contract for Helm 2 -With Helm 2's current release schedule, we want to take into account any migration issues for users due to the upcoming holiday shopping season and tax season. We also want to clarify what actions may occur after the support contract ends for Helm 2, so that users will not be surprised or caught off guard. +With Helm 2's current release schedule, we want to take into account any migration issues for users +due to the upcoming holiday shopping season and tax season. We also want to clarify what actions may +occur after the support contract ends for Helm 2, so that users will not be surprised or caught off +guard. -After Helm 2.15.0 is released, Helm 2 will go into "maintenance mode". We will continue to accept bug fixes and fix any security issues that arise, but no new features will be accepted for Helm 2. All feature development will be moved over to Helm 3. +After Helm 2.15.0 is released, Helm 2 will go into "maintenance mode". We will continue to accept +bug fixes and fix any security issues that arise, but no new features will be accepted for Helm 2. +All feature development will be moved over to Helm 3. -6 months after Helm 3.0.0's public release, Helm 2 will stop accepting bug fixes. Only security issues will be accepted. +6 months after Helm 3.0.0's public release, Helm 2 will stop accepting bug fixes. Only security +issues will be accepted. -12 months after Helm 3.0.0's public release, support for Helm 2 will formally end. Download links for the Helm 2 client through Google Cloud Storage, the Docker image for Tiller stored in Google Container Registry, and the Google Cloud buckets for the stable and incubator chart repositories may no longer work at any point. Client downloads through `get.helm.sh` will continue to work, and we will distribute a Tiller image that will be made available at an alternative location which can be updated with `helm init --tiller-image`. +12 months after Helm 3.0.0's public release, support for Helm 2 will formally end. Download links +for the Helm 2 client through Google Cloud Storage, the Docker image for Tiller stored in Google +Container Registry, and the Google Cloud buckets for the stable and incubator chart repositories may +no longer work at any point. Client downloads through `get.helm.sh` will continue to work, and we +will distribute a Tiller image that will be made available at an alternative location which can be +updated with `helm init --tiller-image`. ## Issues @@ -141,45 +162,46 @@ Issues are used as the primary method for tracking anything to do with the Helm There are 5 types of issues (each with their own corresponding [label](#labels)): -- `question/support`: These are support or functionality inquiries that we want to have a record of for -future reference. Generally these are questions that are too complex or large to store in the -Slack channel or have particular interest to the community as a whole. Depending on the discussion, -these can turn into `feature` or `bug` issues. +- `question/support`: These are support or functionality inquiries that we want to have a record of + for future reference. Generally these are questions that are too complex or large to store in the + Slack channel or have particular interest to the community as a whole. Depending on the + discussion, these can turn into `feature` or `bug` issues. - `proposal`: Used for items (like this one) that propose a new ideas or functionality that require -a larger community discussion. This allows for feedback from others in the community before a -feature is actually developed. This is not needed for small additions. Final word on whether or -not a feature needs a proposal is up to the core maintainers. All issues that are proposals should -both have a label and an issue title of "Proposal: [the rest of the title]." A proposal can become -a `feature` and does not require a milestone. -- `feature`: These track specific feature requests and ideas until they are complete. They can evolve -from a `proposal` or can be submitted individually depending on the size. + a larger community discussion. This allows for feedback from others in the community before a + feature is actually developed. This is not needed for small additions. Final word on whether or + not a feature needs a proposal is up to the core maintainers. All issues that are proposals should + both have a label and an issue title of "Proposal: [the rest of the title]." A proposal can become + a `feature` and does not require a milestone. +- `feature`: These track specific feature requests and ideas until they are complete. They can + evolve from a `proposal` or can be submitted individually depending on the size. - `bug`: These track bugs with the code - `docs`: These track problems with the documentation (i.e. missing or incomplete) ### Issue Lifecycle The issue lifecycle is mainly driven by the core maintainers, but is good information for those -contributing to Helm. All issue types follow the same general lifecycle. Differences are noted below. +contributing to Helm. All issue types follow the same general lifecycle. Differences are noted +below. 1. Issue creation 2. Triage - - The maintainer in charge of triaging will apply the proper labels for the issue. This - includes labels for priority, type, and metadata (such as `good first issue`). The only issue - priority we will be tracking is whether or not the issue is "critical." If additional - levels are needed in the future, we will add them. - - (If needed) Clean up the title to succinctly and clearly state the issue. Also ensure - that proposals are prefaced with "Proposal: [the rest of the title]". - - Add the issue to the correct milestone. If any questions come up, don't worry about - adding the issue to a milestone until the questions are answered. + - The maintainer in charge of triaging will apply the proper labels for the issue. This includes + labels for priority, type, and metadata (such as `good first issue`). The only issue priority + we will be tracking is whether or not the issue is "critical." If additional levels are needed + in the future, we will add them. + - (If needed) Clean up the title to succinctly and clearly state the issue. Also ensure that + proposals are prefaced with "Proposal: [the rest of the title]". + - Add the issue to the correct milestone. If any questions come up, don't worry about adding the + issue to a milestone until the questions are answered. - We attempt to do this process at least once per work day. 3. Discussion - issues that are labeled as `feature` or `bug` should be connected to the PR that resolves it. - - Whoever is working on a `feature` or `bug` issue (whether a maintainer or someone from - the community), should either assign the issue to themself or make a comment in the issue - saying that they are taking it. - - `proposal` and `support/question` issues should stay open until resolved or if they have not been - active for more than 30 days. This will help keep the issue queue to a manageable size and - reduce noise. Should the issue need to stay open, the `keep open` label can be added. + - Whoever is working on a `feature` or `bug` issue (whether a maintainer or someone from the + community), should either assign the issue to themself or make a comment in the issue saying + that they are taking it. + - `proposal` and `support/question` issues should stay open until resolved or if they have not + been active for more than 30 days. This will help keep the issue queue to a manageable size + and reduce noise. Should the issue need to stay open, the `keep open` label can be added. 4. Issue closure ## How to Contribute a Patch @@ -188,7 +210,8 @@ contributing to Helm. All issue types follow the same general lifecycle. Differe 2. Fork the desired repo; develop and test your code changes. 3. Submit a pull request, making sure to sign your work and link the related issue. -Coding conventions and standards are explained in the [official developer docs](https://helm.sh/docs/developers/). +Coding conventions and standards are explained in the [official developer +docs](https://helm.sh/docs/developers/). ## Pull Requests @@ -199,41 +222,42 @@ Like any good open source project, we use Pull Requests (PRs) to track code chan 1. PR creation - PRs are usually created to fix or else be a subset of other PRs that fix a particular issue. - We more than welcome PRs that are currently in progress. They are a great way to keep track of - important work that is in-flight, but useful for others to see. If a PR is a work in progress, - it **must** be prefaced with "WIP: [title]". Once the PR is ready for review, remove "WIP" from - the title. + important work that is in-flight, but useful for others to see. If a PR is a work in progress, + it **must** be prefaced with "WIP: [title]". Once the PR is ready for review, remove "WIP" + from the title. - It is preferred, but not required, to have a PR tied to a specific issue. There can be - circumstances where if it is a quick fix then an issue might be overkill. The details provided - in the PR description would suffice in this case. + circumstances where if it is a quick fix then an issue might be overkill. The details provided + in the PR description would suffice in this case. 2. Triage - The maintainer in charge of triaging will apply the proper labels for the issue. This should - include at least a size label, `bug` or `feature`, and `awaiting review` once all labels are applied. - See the [Labels section](#labels) for full details on the definitions of labels. + include at least a size label, `bug` or `feature`, and `awaiting review` once all labels are + applied. See the [Labels section](#labels) for full details on the definitions of labels. - Add the PR to the correct milestone. This should be the same as the issue the PR closes. 3. Assigning reviews - - Once a review has the `awaiting review` label, maintainers will review them as schedule permits. - The maintainer who takes the issue should self-request a review. - - Any PR with the `size/large` label requires 2 review approvals from maintainers before it can be - merged. Those with `size/medium` or `size/small` are per the judgement of the maintainers. + - Once a review has the `awaiting review` label, maintainers will review them as schedule + permits. The maintainer who takes the issue should self-request a review. + - Any PR with the `size/large` label requires 2 review approvals from maintainers before it can + be merged. Those with `size/medium` or `size/small` are per the judgement of the maintainers. 4. Reviewing/Discussion - All reviews will be completed using Github review tool. - A "Comment" review should be used when there are questions about the code that should be - answered, but that don't involve code changes. This type of review does not count as approval. - - A "Changes Requested" review indicates that changes to the code need to be made before they will be merged. + answered, but that don't involve code changes. This type of review does not count as approval. + - A "Changes Requested" review indicates that changes to the code need to be made before they + will be merged. - Reviewers should update labels as needed (such as `needs rebase`) 5. Address comments by answering questions or changing code 6. LGTM (Looks good to me) - - Once a Reviewer has completed a review and the code looks ready to merge, an "Approve" review is used - to signal to the contributor and to other maintainers that you have reviewed the code and feel that it is - ready to be merged. + - Once a Reviewer has completed a review and the code looks ready to merge, an "Approve" review + is used to signal to the contributor and to other maintainers that you have reviewed the code + and feel that it is ready to be merged. 7. Merge or close - - PRs should stay open until merged or if they have not been active for more than 30 days. - This will help keep the PR queue to a manageable size and reduce noise. Should the PR need - to stay open (like in the case of a WIP), the `keep open` label can be added. + - PRs should stay open until merged or if they have not been active for more than 30 days. This + will help keep the PR queue to a manageable size and reduce noise. Should the PR need to stay + open (like in the case of a WIP), the `keep open` label can be added. - Before merging a PR, refer to the topic on [Size Labels](#size-labels) below to determine if the PR requires more than one LGTM to merge. - If the owner of the PR is listed in the `OWNERS` file, that user **must** merge their own PRs - or explicitly request another OWNER do that for them. + or explicitly request another OWNER do that for them. - If the owner of a PR is _not_ listed in `OWNERS`, any core maintainer may merge the PR. #### Documentation PRs @@ -286,12 +310,12 @@ The following tables define all label types used for Helm. It is split up by cat #### Size labels -Size labels are used to indicate how "dangerous" a PR is. The guidelines below are used to assign the -labels, but ultimately this can be changed by the maintainers. For example, even if a PR only makes -30 lines of changes in 1 file, but it changes key functionality, it will likely be labeled as `size/L` -because it requires sign off from multiple people. Conversely, a PR that adds a small feature, but requires -another 150 lines of tests to cover all cases, could be labeled as `size/S` even though the number of -lines is greater than defined below. +Size labels are used to indicate how "dangerous" a PR is. The guidelines below are used to assign +the labels, but ultimately this can be changed by the maintainers. For example, even if a PR only +makes 30 lines of changes in 1 file, but it changes key functionality, it will likely be labeled as +`size/L` because it requires sign off from multiple people. Conversely, a PR that adds a small +feature, but requires another 150 lines of tests to cover all cases, could be labeled as `size/S` +even though the number of lines is greater than defined below. PRs submitted by a core maintainer, regardless of size, only requires approval from one additional maintainer. This ensures there are at least two maintainers who are aware of any significant PRs From ae738d7d87bbd5db7203db7441fb11e32b02df40 Mon Sep 17 00:00:00 2001 From: Hidde Beydals Date: Wed, 13 May 2020 23:51:32 +0200 Subject: [PATCH 109/281] feat(getter): add timeout option (#7950) To allow finer grain control over the request when utilizing `getter` as a package. Signed-off-by: Hidde Beydals --- pkg/getter/getter.go | 9 +++++++++ pkg/getter/httpgetter.go | 1 + pkg/getter/httpgetter_test.go | 7 +++++++ 3 files changed, 17 insertions(+) diff --git a/pkg/getter/getter.go b/pkg/getter/getter.go index 4ccc74834..8ee08cb7f 100644 --- a/pkg/getter/getter.go +++ b/pkg/getter/getter.go @@ -18,6 +18,7 @@ package getter import ( "bytes" + "time" "github.com/pkg/errors" @@ -36,6 +37,7 @@ type options struct { username string password string userAgent string + timeout time.Duration } // Option allows specifying various settings configurable by the user for overriding the defaults @@ -81,6 +83,13 @@ func WithTLSClientConfig(certFile, keyFile, caFile string) Option { } } +// WithTimeout sets the timeout for requests +func WithTimeout(timeout time.Duration) Option { + return func(opts *options) { + opts.timeout = timeout + } +} + // Getter is an interface to support GET to the specified URL. type Getter interface { // Get file content by url string diff --git a/pkg/getter/httpgetter.go b/pkg/getter/httpgetter.go index 46c817a9c..858af91ac 100644 --- a/pkg/getter/httpgetter.go +++ b/pkg/getter/httpgetter.go @@ -119,6 +119,7 @@ func (g *HTTPGetter) httpClient() (*http.Client, error) { client := &http.Client{ Transport: transport, + Timeout: g.opts.timeout, } return client, nil diff --git a/pkg/getter/httpgetter_test.go b/pkg/getter/httpgetter_test.go index 0e157eccd..7973b5f85 100644 --- a/pkg/getter/httpgetter_test.go +++ b/pkg/getter/httpgetter_test.go @@ -26,6 +26,7 @@ import ( "strconv" "strings" "testing" + "time" "github.com/pkg/errors" @@ -48,6 +49,7 @@ func TestHTTPGetter(t *testing.T) { join := filepath.Join ca, pub, priv := join(cd, "rootca.crt"), join(cd, "crt.pem"), join(cd, "key.pem") insecure := false + timeout := time.Second * 5 // Test with options g, err = NewHTTPGetter( @@ -55,6 +57,7 @@ func TestHTTPGetter(t *testing.T) { WithUserAgent("Groot"), WithTLSClientConfig(pub, priv, ca), WithInsecureSkipVerifyTLS(insecure), + WithTimeout(timeout), ) if err != nil { t.Fatal(err) @@ -93,6 +96,10 @@ func TestHTTPGetter(t *testing.T) { t.Errorf("Expected NewHTTPGetter to contain %t as InsecureSkipVerifyTLs flag, got %t", false, hg.opts.insecureSkipVerifyTLS) } + if hg.opts.timeout != timeout { + t.Errorf("Expected NewHTTPGetter to contain %s as Timeout flag, got %s", timeout, hg.opts.timeout) + } + // Test if setting insecureSkipVerifyTLS is being passed to the ops insecure = true From decab8ea2e6ea4b31560aff50abb2676a67ec8ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=98=8E=E5=90=8C=E5=AD=A6?= <11614773+liuming-dev@users.noreply.github.com> Date: Thu, 14 May 2020 05:53:37 +0800 Subject: [PATCH 110/281] fix: upgrade using --force shoud not run patch logic (#8000) fix helm/helm#7999 Signed-off-by: Liu Ming --- pkg/kube/client.go | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/pkg/kube/client.go b/pkg/kube/client.go index c1de2b299..4683d8033 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -418,29 +418,29 @@ func updateResource(c *Client, target *resource.Info, currentObj runtime.Object, kind = target.Mapping.GroupVersionKind.Kind ) - patch, patchType, err := createPatch(target, currentObj) - if err != nil { - return errors.Wrap(err, "failed to create patch") - } - - if patch == nil || string(patch) == "{}" { - c.Log("Looks like there are no changes for %s %q", target.Mapping.GroupVersionKind.Kind, target.Name) - // This needs to happen to make sure that tiller has the latest info from the API - // Otherwise there will be no labels and other functions that use labels will panic - if err := target.Get(); err != nil { - return errors.Wrap(err, "failed to refresh resource information") - } - return nil - } - // if --force is applied, attempt to replace the existing resource with the new object. if force { + var err error obj, err = helper.Replace(target.Namespace, target.Name, true, target.Object) if err != nil { return errors.Wrap(err, "failed to replace object") } c.Log("Replaced %q with kind %s for kind %s", target.Name, currentObj.GetObjectKind().GroupVersionKind().Kind, kind) } else { + patch, patchType, err := createPatch(target, currentObj) + if err != nil { + return errors.Wrap(err, "failed to create patch") + } + + if patch == nil || string(patch) == "{}" { + c.Log("Looks like there are no changes for %s %q", target.Mapping.GroupVersionKind.Kind, target.Name) + // This needs to happen to make sure that tiller has the latest info from the API + // Otherwise there will be no labels and other functions that use labels will panic + if err := target.Get(); err != nil { + return errors.Wrap(err, "failed to refresh resource information") + } + return nil + } // send patch to server obj, err = helper.Patch(target.Namespace, target.Name, patchType, patch, nil) if err != nil { From 512544b9abbc75418348b76037fee3156ea1d3bd Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Wed, 13 May 2020 18:09:27 -0400 Subject: [PATCH 111/281] Fixing PAX Header handling (#8086) * Fixing issue with PAX headers in plugin archive PAX Headers can be added by some systems that create archives. Helm should ignore them when extracting. There are two PAX headers. One is global and the other is not. Both are ignored. The test adds only the PAX global header because the Go tar package is unable to write the header that is not global. Closes #8084 Signed-off-by: Matt Farina * Removing the PAX header test as it is not working The PAX header test was making a WriteHeader call and ignoring the error. When writing the type TypeXHeader it was causing an error that was being silently ignored. The Go tar package cannot write this type and produces an error when one tries to. The error reads "cannot manually encode TypeXHeader, TypeGNULongName, or TypeGNULongLink headers" Signed-off-by: Matt Farina * Adding check of returned error in test Adding a check for the returned error to make sure a non-nil value is not returned. Signed-off-by: Matt Farina --- pkg/chart/loader/archive_test.go | 34 +++++---------------- pkg/plugin/installer/http_installer.go | 3 ++ pkg/plugin/installer/http_installer_test.go | 13 ++++++++ 3 files changed, 23 insertions(+), 27 deletions(-) diff --git a/pkg/chart/loader/archive_test.go b/pkg/chart/loader/archive_test.go index 7d8c8b51e..41b0af1aa 100644 --- a/pkg/chart/loader/archive_test.go +++ b/pkg/chart/loader/archive_test.go @@ -43,46 +43,26 @@ func TestLoadArchiveFiles(t *testing.T) { 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{ + err := 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) + t.Fatal(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{ + err = w.WriteHeader(&tar.Header{ Typeflag: tar.TypeReg, Name: "dir/empty", }) + if err != nil { + t.Fatal(err) + } }, 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) + t.Fatalf(`got unwanted error [%#v] for tar file with pax_global_header content`, err) } if len(files) != 1 { diff --git a/pkg/plugin/installer/http_installer.go b/pkg/plugin/installer/http_installer.go index 629bbec39..c07cad80a 100644 --- a/pkg/plugin/installer/http_installer.go +++ b/pkg/plugin/installer/http_installer.go @@ -188,6 +188,9 @@ func (g *TarGzExtractor) Extract(buffer *bytes.Buffer, targetDir string) error { return err } outFile.Close() + // We don't want to process these extension header files. + case tar.TypeXGlobalHeader, tar.TypeXHeader: + continue default: return errors.Errorf("unknown type: %b in %s", header.Typeflag, header.Name) } diff --git a/pkg/plugin/installer/http_installer_test.go b/pkg/plugin/installer/http_installer_test.go index b496a1b01..99470ace6 100644 --- a/pkg/plugin/installer/http_installer_test.go +++ b/pkg/plugin/installer/http_installer_test.go @@ -222,6 +222,19 @@ func TestExtract(t *testing.T) { t.Fatal(err) } } + + // Add pax global headers. This should be ignored. + // Note the PAX header that isn't global cannot be written using WriteHeader. + // Details are in the internal Go function for the tar packaged named + // allowedFormats. For a TypeXHeader it will return a message stating + // "cannot manually encode TypeXHeader, TypeGNULongName, or TypeGNULongLink headers" + if err := tw.WriteHeader(&tar.Header{ + Name: "pax_global_header", + Typeflag: tar.TypeXGlobalHeader, + }); err != nil { + t.Fatal(err) + } + if err := tw.Close(); err != nil { t.Fatal(err) } From b18e7e201e55ba38fdecbcf81e4da512e55444b4 Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Thu, 14 May 2020 08:06:18 -0400 Subject: [PATCH 112/281] chore(*): Fix formatting Signed-off-by: Marc Khouzam --- pkg/getter/httpgetter.go | 2 +- pkg/getter/httpgetter_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/getter/httpgetter.go b/pkg/getter/httpgetter.go index 858af91ac..c100b2cc0 100644 --- a/pkg/getter/httpgetter.go +++ b/pkg/getter/httpgetter.go @@ -92,7 +92,7 @@ func NewHTTPGetter(options ...Option) (Getter, error) { func (g *HTTPGetter) httpClient() (*http.Client, error) { transport := &http.Transport{ DisableCompression: true, - Proxy: http.ProxyFromEnvironment, + Proxy: http.ProxyFromEnvironment, } if (g.opts.certFile != "" && g.opts.keyFile != "") || g.opts.caFile != "" { tlsConf, err := tlsutil.NewClientTLS(g.opts.certFile, g.opts.keyFile, g.opts.caFile) diff --git a/pkg/getter/httpgetter_test.go b/pkg/getter/httpgetter_test.go index 7973b5f85..90578f7b7 100644 --- a/pkg/getter/httpgetter_test.go +++ b/pkg/getter/httpgetter_test.go @@ -293,4 +293,4 @@ func TestHTTPGetterTarDownload(t *testing.T) { if mimeType != expectedMimeType { t.Fatalf("Expected response with MIME type %s, but got %s", expectedMimeType, mimeType) } -} \ No newline at end of file +} From 88f6991ffff04c48dd8fd257d238aca48aef80df Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Thu, 14 May 2020 15:00:41 -0400 Subject: [PATCH 113/281] feat(test): Update golangci-lint to 1.27.0 Since we've moved to Go 1.14, golangci-lint has been silently failing. This commit updates to a compatible version. Signed-off-by: Marc Khouzam --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index e6ce2e242..bf3b78179 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,7 +9,7 @@ jobs: environment: GOCACHE: "/tmp/go/cache" - GOLANGCI_LINT_VERSION: "1.21.0" + GOLANGCI_LINT_VERSION: "1.27.0" steps: - checkout From 0366f9970f0ff5c0b61b02521d139445bfaeba8f Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Fri, 10 Apr 2020 11:47:31 -0400 Subject: [PATCH 114/281] fix(comp): Prepare plugin completion for Cobra 1.0 Cobra 1.0.0 introduces support for Custom Go Completions. However, there is a small difference compared to helm's existing solution. The difference is that Cobra will not complete sub-commands if there are any dynamic completion functions registered. The way helm implemented plugin completions had the dynamic completion function associated with the root plugin command (e.g. 2to3); with Cobra, this prevents any of the sub-commands of the plugin to be completed (i.e., 2to3 will not show the static sub-commands). The solution is to only register the dynamic completion function for a plugin command that doesn't have any further sub-commands. This allows all sub-commands to be completed properly, and when there are no more levels of sub-commands, the dynamic completion is called. This commit had to make two changes to achieve this: 1- refactor the load_plugins.go file to extract the logic to call the plugin.complete executable (for dynamic completions), so that it could be added to each final sub-command. 2- load the static completion not only for the "completion" command like before, but also for the "__complete" command, so that when the __complete command is called for dynamic completion of a sub-command, it is aware of the sub-command in question. The second change is also valuable for the future support of completion for the Fish shell. Completion for the fish shell uses the __complete command not only for dynamic completions but also for basic command and flag completion. This implies that the __complete command must be aware of the plugins sub-commands, and therefore that the plugin's static completion be loaded. Signed-off-by: Marc Khouzam --- cmd/helm/load_plugins.go | 263 +++++++++++++++++++++------------------ 1 file changed, 142 insertions(+), 121 deletions(-) diff --git a/cmd/helm/load_plugins.go b/cmd/helm/load_plugins.go index a23a067fb..36de50135 100644 --- a/cmd/helm/load_plugins.go +++ b/cmd/helm/load_plugins.go @@ -64,21 +64,6 @@ func loadPlugins(baseCmd *cobra.Command, out io.Writer) { return } - processParent := func(cmd *cobra.Command, args []string) ([]string, error) { - k, u := manuallyProcessArgs(args) - if err := cmd.Parent().ParseFlags(k); err != nil { - return nil, err - } - return u, nil - } - - // If we are dealing with the completion command, we try to load more details about the plugins - // if available, so as to allow for command and flag completion - if subCmd, _, err := baseCmd.Find(os.Args[1:]); err == nil && subCmd.Name() == "completion" { - loadPluginsForCompletion(baseCmd, found) - return - } - // Now we create commands for all of these. for _, plug := range found { plug := plug @@ -87,33 +72,6 @@ func loadPlugins(baseCmd *cobra.Command, out io.Writer) { md.Usage = fmt.Sprintf("the %q plugin", md.Name) } - // This function is used to setup the environment for the plugin and then - // call the executable specified by the parameter 'main' - callPluginExecutable := func(cmd *cobra.Command, main string, argv []string, out io.Writer) error { - env := os.Environ() - for k, v := range settings.EnvVars() { - env = append(env, fmt.Sprintf("%s=%s", k, v)) - } - - prog := exec.Command(main, argv...) - prog.Env = env - prog.Stdin = os.Stdin - prog.Stdout = out - prog.Stderr = os.Stderr - if err := prog.Run(); err != nil { - if eerr, ok := err.(*exec.ExitError); ok { - os.Stderr.Write(eerr.Stderr) - status := eerr.Sys().(syscall.WaitStatus) - return pluginError{ - error: errors.Errorf("plugin %q exited with error", md.Name), - code: status.ExitStatus(), - } - } - return err - } - return nil - } - c := &cobra.Command{ Use: md.Name, Short: md.Usage, @@ -134,62 +92,59 @@ func loadPlugins(baseCmd *cobra.Command, out io.Writer) { return errors.Errorf("plugin %q exited with error", md.Name) } - return callPluginExecutable(cmd, main, argv, out) + return callPluginExecutable(md.Name, main, argv, out) }, // This passes all the flags to the subcommand. DisableFlagParsing: true, } + // TODO: Make sure a command with this name does not already exist. + baseCmd.AddCommand(c) - // Setup dynamic completion for the plugin - completion.RegisterValidArgsFunc(c, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - u, err := processParent(cmd, args) - if err != nil { - return nil, completion.BashCompDirectiveError - } - - // We will call the dynamic completion script of the plugin - main := strings.Join([]string{plug.Dir, pluginDynamicCompletionExecutable}, string(filepath.Separator)) - - argv := []string{} - if !md.IgnoreFlags { - argv = append(argv, u...) - argv = append(argv, toComplete) - } - plugin.SetupPluginEnv(settings, md.Name, plug.Dir) + // For completion, we try to load more details about the plugins so as to allow for command and + // flag completion of the plugin itself. + // We only do this when necessary (for the "completion" and "__complete" commands) to avoid the + // risk of a rogue plugin affecting Helm's normal behavior. + subCmd, _, err := baseCmd.Find(os.Args[1:]) + if (err == nil && (subCmd.Name() == "completion" || subCmd.Name() == completion.CompRequestCmd)) || + /* for the tests */ subCmd == baseCmd.Root() { + loadCompletionForPlugin(c, plug) + } + } +} - completion.CompDebugln(fmt.Sprintf("calling %s with args %v", main, argv)) - buf := new(bytes.Buffer) - if err := callPluginExecutable(cmd, main, argv, buf); err != nil { - return nil, completion.BashCompDirectiveError - } +func processParent(cmd *cobra.Command, args []string) ([]string, error) { + k, u := manuallyProcessArgs(args) + if err := cmd.Parent().ParseFlags(k); err != nil { + return nil, err + } + return u, nil +} - var completions []string - for _, comp := range strings.Split(buf.String(), "\n") { - // Remove any empty lines - if len(comp) > 0 { - completions = append(completions, comp) - } - } +// This function is used to setup the environment for the plugin and then +// call the executable specified by the parameter 'main' +func callPluginExecutable(pluginName string, main string, argv []string, out io.Writer) error { + env := os.Environ() + for k, v := range settings.EnvVars() { + env = append(env, fmt.Sprintf("%s=%s", k, v)) + } - // Check if the last line of output is of the form :, which - // indicates the BashCompletionDirective. - directive := completion.BashCompDirectiveDefault - if len(completions) > 0 { - lastLine := completions[len(completions)-1] - if len(lastLine) > 1 && lastLine[0] == ':' { - if strInt, err := strconv.Atoi(lastLine[1:]); err == nil { - directive = completion.BashCompDirective(strInt) - completions = completions[:len(completions)-1] - } - } + prog := exec.Command(main, argv...) + prog.Env = env + prog.Stdin = os.Stdin + prog.Stdout = out + prog.Stderr = os.Stderr + if err := prog.Run(); err != nil { + if eerr, ok := err.(*exec.ExitError); ok { + os.Stderr.Write(eerr.Stderr) + status := eerr.Sys().(syscall.WaitStatus) + return pluginError{ + error: errors.Errorf("plugin %q exited with error", pluginName), + code: status.ExitStatus(), } - - return completions, directive - }) - - // TODO: Make sure a command with this name does not already exist. - baseCmd.AddCommand(c) + } + return err } + return nil } // manuallyProcessArgs processes an arg array, removing special args. @@ -246,35 +201,31 @@ type pluginCommand struct { Commands []pluginCommand `json:"commands"` } -// loadPluginsForCompletion will load and parse any completion.yaml provided by the plugins -func loadPluginsForCompletion(baseCmd *cobra.Command, plugins []*plugin.Plugin) { - for _, plug := range plugins { - // Parse the yaml file providing the plugin's subcmds and flags - cmds, err := loadFile(strings.Join( - []string{plug.Dir, pluginStaticCompletionFile}, string(filepath.Separator))) - - if err != nil { - // The file could be missing or invalid. Either way, we at least create the command - // for the plugin name. - if settings.Debug { - log.Output(2, fmt.Sprintf("[info] %s\n", err.Error())) - } - cmds = &pluginCommand{Name: plug.Metadata.Name} +// loadCompletionForPlugin will load and parse any completion.yaml provided by the plugin +// and add the dynamic completion hook to call the optional plugin.complete +func loadCompletionForPlugin(pluginCmd *cobra.Command, plugin *plugin.Plugin) { + // Parse the yaml file providing the plugin's sub-commands and flags + cmds, err := loadFile(strings.Join( + []string{plugin.Dir, pluginStaticCompletionFile}, string(filepath.Separator))) + + if err != nil { + // The file could be missing or invalid. No static completion for this plugin. + if settings.Debug { + log.Output(2, fmt.Sprintf("[info] %s\n", err.Error())) } + // Continue to setup dynamic completion. + cmds = &pluginCommand{} + } - // We know what the plugin name must be. - // Let's set it in case the Name field was not specified correctly in the file. - // This insures that we will at least get the plugin name to complete, even if - // there is a problem with the completion.yaml file - cmds.Name = plug.Metadata.Name + // Preserve the Usage string specified for the plugin + cmds.Name = pluginCmd.Use - addPluginCommands(baseCmd, cmds) - } + addPluginCommands(plugin, pluginCmd, cmds) } -// addPluginCommands is a recursive method that adds the different levels -// of sub-commands and flags for the plugins that provide such information -func addPluginCommands(baseCmd *cobra.Command, cmds *pluginCommand) { +// addPluginCommands is a recursive method that adds each different level +// of sub-commands and flags for the plugins that have provided such information +func addPluginCommands(plugin *plugin.Plugin, baseCmd *cobra.Command, cmds *pluginCommand) { if cmds == nil { return } @@ -287,14 +238,19 @@ func addPluginCommands(baseCmd *cobra.Command, cmds *pluginCommand) { return } - // Create a fake command just so the completion script will include it - c := &cobra.Command{ - Use: cmds.Name, - ValidArgs: cmds.ValidArgs, - // A Run is required for it to be a valid command without subcommands - Run: func(cmd *cobra.Command, args []string) {}, + baseCmd.Use = cmds.Name + baseCmd.ValidArgs = cmds.ValidArgs + // Setup the same dynamic completion for each plugin sub-command. + // This is because if dynamic completion is triggered, there is a single executable + // to call (plugin.complete), so every sub-commands calls it in the same fashion. + if cmds.Commands == nil { + // Only setup dynamic completion if there are no sub-commands. This avoids + // calling plugin.complete at every completion, which greatly simplifies + // development of plugin.complete for plugin developers. + completion.RegisterValidArgsFunc(baseCmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { + return pluginDynamicComp(plugin, cmd, args, toComplete) + }) } - baseCmd.AddCommand(c) // Create fake flags. if len(cmds.Flags) > 0 { @@ -314,7 +270,7 @@ func addPluginCommands(baseCmd *cobra.Command, cmds *pluginCommand) { } } - f := c.Flags() + f := baseCmd.Flags() if len(longs) >= len(shorts) { for i := range longs { if i < len(shorts) { @@ -338,7 +294,16 @@ func addPluginCommands(baseCmd *cobra.Command, cmds *pluginCommand) { // Recursively add any sub-commands for _, cmd := range cmds.Commands { - addPluginCommands(c, &cmd) + // Create a fake command so that completion can be done for the sub-commands of the plugin + subCmd := &cobra.Command{ + // This prevents Cobra from removing the flags. We want to keep the flags to pass them + // to the dynamic completion script of the plugin. + DisableFlagParsing: true, + // A Run is required for it to be a valid command without subcommands + Run: func(cmd *cobra.Command, args []string) {}, + } + baseCmd.AddCommand(subCmd) + addPluginCommands(plugin, subCmd, &cmd) } } @@ -353,3 +318,59 @@ func loadFile(path string) (*pluginCommand, error) { err = yaml.Unmarshal(b, cmds) return cmds, err } + +// pluginDynamicComp call the plugin.complete script of the plugin (if available) +// to obtain the dynamic completion choices. It must pass all the flags and sub-commands +// specified in the command-line to the plugin.complete executable (except helm's global flags) +func pluginDynamicComp(plug *plugin.Plugin, cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { + md := plug.Metadata + + u, err := processParent(cmd, args) + if err != nil { + return nil, completion.BashCompDirectiveError + } + + // We will call the dynamic completion script of the plugin + main := strings.Join([]string{plug.Dir, pluginDynamicCompletionExecutable}, string(filepath.Separator)) + + // We must include all sub-commands passed on the command-line. + // To do that, we pass-in the entire CommandPath, except the first two elements + // which are 'helm' and 'pluginName'. + argv := strings.Split(cmd.CommandPath(), " ")[2:] + if !md.IgnoreFlags { + argv = append(argv, u...) + argv = append(argv, toComplete) + } + plugin.SetupPluginEnv(settings, md.Name, plug.Dir) + + completion.CompDebugln(fmt.Sprintf("calling %s with args %v", main, argv)) + buf := new(bytes.Buffer) + if err := callPluginExecutable(md.Name, main, argv, buf); err != nil { + // The dynamic completion file is optional for a plugin, so this error is ok. + completion.CompDebugln(fmt.Sprintf("Unable to call %s: %v", main, err.Error())) + return nil, completion.BashCompDirectiveDefault + } + + var completions []string + for _, comp := range strings.Split(buf.String(), "\n") { + // Remove any empty lines + if len(comp) > 0 { + completions = append(completions, comp) + } + } + + // Check if the last line of output is of the form :, which + // indicates the BashCompletionDirective. + directive := completion.BashCompDirectiveDefault + if len(completions) > 0 { + lastLine := completions[len(completions)-1] + if len(lastLine) > 1 && lastLine[0] == ':' { + if strInt, err := strconv.Atoi(lastLine[1:]); err == nil { + directive = completion.BashCompDirective(strInt) + completions = completions[:len(completions)-1] + } + } + } + + return completions, directive +} From e6069769151b91c91a107bd4d79ca3a941658518 Mon Sep 17 00:00:00 2001 From: Maksim Kochkin Date: Fri, 15 May 2020 16:38:55 +0300 Subject: [PATCH 115/281] Add new line to fix code formatting in doc Signed-off-by: Maksim Kochkin --- cmd/helm/install.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/helm/install.go b/cmd/helm/install.go index 21a41b9f9..44f7336c0 100644 --- a/cmd/helm/install.go +++ b/cmd/helm/install.go @@ -60,6 +60,7 @@ or $ helm install --set-string long_int=1234567890 myredis ./redis or + $ helm install --set-file my_script=dothings.sh myredis ./redis You can specify the '--values'/'-f' flag multiple times. The priority will be given to the From 118d46eb3e2c8425ecdbb9c8f8270413060eda78 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Fri, 15 May 2020 12:08:15 -0600 Subject: [PATCH 116/281] feat: Detect missing selector during lint Signed-off-by: Matt Butcher --- pkg/lint/rules/template.go | 14 ++++++ pkg/lint/rules/template_test.go | 81 ++++++++++++++++++++++++++++++++- 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/pkg/lint/rules/template.go b/pkg/lint/rules/template.go index 787c5b26a..0c8e872b7 100644 --- a/pkg/lint/rules/template.go +++ b/pkg/lint/rules/template.go @@ -131,6 +131,7 @@ func Templates(linter *support.Linter, values map[string]interface{}, namespace linter.RunLinterRule(support.ErrorSev, path, validateYamlContent(err)) linter.RunLinterRule(support.ErrorSev, path, validateMetadataName(&yamlStruct)) linter.RunLinterRule(support.ErrorSev, path, validateNoDeprecations(&yamlStruct)) + linter.RunLinterRule(support.ErrorSev, path, validateMatchSelector(&yamlStruct, renderedContent)) } } } @@ -185,6 +186,19 @@ func validateNoReleaseTime(manifest []byte) error { return nil } +// validateMatchSelector ensures that template specs have a selector declared. +// See https://github.com/helm/helm/issues/1990 +func validateMatchSelector(yamlStruct *K8sYamlStruct, manifest string) error { + switch yamlStruct.Kind { + case "Deployment", "ReplicaSet", "DaemonSet", "StatefulSet": + // verify that matchLabels or matchExpressions is present + if !(strings.Contains(manifest, "matchLabels") || strings.Contains(manifest, "matchExpressions")) { + return fmt.Errorf("a %s must contain matchLabels or matchExpressions, and %q does not", yamlStruct.Kind, yamlStruct.Metadata.Name) + } + } + return nil +} + // K8sYamlStruct stubs a Kubernetes YAML file. // // DEPRECATED: In Helm 4, this will be made a private type, as it is for use only within diff --git a/pkg/lint/rules/template_test.go b/pkg/lint/rules/template_test.go index 991c6c2f6..ae82c8922 100644 --- a/pkg/lint/rules/template_test.go +++ b/pkg/lint/rules/template_test.go @@ -145,7 +145,7 @@ func TestDeprecatedAPIFails(t *testing.T) { Templates: []*chart.File{ { Name: "templates/baddeployment.yaml", - Data: []byte("apiVersion: apps/v1beta1\nkind: Deployment\nmetadata:\n name: baddep"), + Data: []byte("apiVersion: apps/v1beta1\nkind: Deployment\nmetadata:\n name: baddep\nspec: {selector: {matchLabels: {foo: bar}}}"), }, { Name: "templates/goodsecret.yaml", @@ -226,3 +226,82 @@ func TestStrictTemplateParsingMapError(t *testing.T) { } } } + +func TestValidateMatchSelector(t *testing.T) { + md := &K8sYamlStruct{ + APIVersion: "apps/v1", + Kind: "Deployment", + Metadata: k8sYamlMetadata{ + Name: "mydeployment", + }, + } + manifest := ` + apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx-deployment + labels: + app: nginx +spec: + replicas: 3 + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: nginx + image: nginx:1.14.2 + ` + if err := validateMatchSelector(md, manifest); err != nil { + t.Error(err) + } + manifest = ` + apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx-deployment + labels: + app: nginx +spec: + replicas: 3 + selector: + matchExpressions: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: nginx + image: nginx:1.14.2 + ` + if err := validateMatchSelector(md, manifest); err != nil { + t.Error(err) + } + manifest = ` + apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx-deployment + labels: + app: nginx +spec: + replicas: 3 + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: nginx + image: nginx:1.14.2 + ` + if err := validateMatchSelector(md, manifest); err == nil { + t.Error("expected Deployment with no selector to fail") + } +} From 3364265e78274577be049763b54adffb9913d75d Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Fri, 15 May 2020 12:11:18 -0700 Subject: [PATCH 117/281] ref(pkg/chartutil): use minimal in-memory fixtures Signed-off-by: Adam Reese --- pkg/chartutil/coalesce_test.go | 43 +++++++++++++++++- pkg/chartutil/create_test.go | 2 +- .../frobnitz/charts/mariner-4.3.2.tgz | Bin 962 -> 0 bytes .../{ => frobnitz/charts}/mariner/Chart.yaml | 0 .../mariner/charts}/albatross/Chart.yaml | 0 .../mariner/charts}/albatross/values.yaml | 0 .../charts}/mariner/templates/placeholder.tpl | 0 .../{ => frobnitz/charts}/mariner/values.yaml | 0 .../testdata/frobnitz_backslash/.helmignore | 1 - .../testdata/frobnitz_backslash/Chart.lock | 8 ---- .../testdata/frobnitz_backslash/Chart.yaml | 27 ----------- .../testdata/frobnitz_backslash/INSTALL.txt | 1 - .../testdata/frobnitz_backslash/LICENSE | 1 - .../testdata/frobnitz_backslash/README.md | 11 ----- .../frobnitz_backslash/charts/_ignore_me | 1 - .../charts/alpine/Chart.yaml | 5 -- .../charts/alpine/README.md | 9 ---- .../charts/alpine/charts/mast1/Chart.yaml | 5 -- .../charts/alpine/charts/mast1/values.yaml | 4 -- .../charts/alpine/charts/mast2-0.1.0.tgz | Bin 252 -> 0 bytes .../charts/alpine/templates/alpine-pod.yaml | 14 ------ .../charts/alpine/values.yaml | 2 - .../charts/mariner-4.3.2.tgz | Bin 962 -> 0 bytes .../frobnitz_backslash/docs/README.md | 1 - .../testdata/frobnitz_backslash/icon.svg | 8 ---- .../testdata/frobnitz_backslash/ignore/me.txt | 0 .../frobnitz_backslash/templates/template.tpl | 1 - .../testdata/frobnitz_backslash/values.yaml | 6 --- .../mariner/charts/albatross-0.1.0.tgz | Bin 305 -> 0 bytes pkg/chartutil/testdata/moby/Chart.yaml | 4 -- .../testdata/moby/charts/pequod/Chart.yaml | 4 -- .../moby/charts/pequod/charts/ahab/Chart.yaml | 4 -- .../charts/pequod/charts/ahab/values.yaml | 6 --- .../testdata/moby/charts/pequod/values.yaml | 2 - .../testdata/moby/charts/spouter/Chart.yaml | 4 -- .../testdata/moby/charts/spouter/values.yaml | 1 - pkg/chartutil/testdata/moby/values.yaml | 11 ----- 37 files changed, 43 insertions(+), 143 deletions(-) delete mode 100644 pkg/chartutil/testdata/frobnitz/charts/mariner-4.3.2.tgz rename pkg/chartutil/testdata/{ => frobnitz/charts}/mariner/Chart.yaml (100%) rename pkg/chartutil/testdata/{ => frobnitz/charts/mariner/charts}/albatross/Chart.yaml (100%) rename pkg/chartutil/testdata/{ => frobnitz/charts/mariner/charts}/albatross/values.yaml (100%) rename pkg/chartutil/testdata/{ => frobnitz/charts}/mariner/templates/placeholder.tpl (100%) rename pkg/chartutil/testdata/{ => frobnitz/charts}/mariner/values.yaml (100%) delete mode 100755 pkg/chartutil/testdata/frobnitz_backslash/.helmignore delete mode 100755 pkg/chartutil/testdata/frobnitz_backslash/Chart.lock delete mode 100755 pkg/chartutil/testdata/frobnitz_backslash/Chart.yaml delete mode 100755 pkg/chartutil/testdata/frobnitz_backslash/INSTALL.txt delete mode 100755 pkg/chartutil/testdata/frobnitz_backslash/LICENSE delete mode 100755 pkg/chartutil/testdata/frobnitz_backslash/README.md delete mode 100755 pkg/chartutil/testdata/frobnitz_backslash/charts/_ignore_me delete mode 100755 pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/Chart.yaml delete mode 100755 pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/README.md delete mode 100755 pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/charts/mast1/Chart.yaml delete mode 100755 pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/charts/mast1/values.yaml delete mode 100755 pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/charts/mast2-0.1.0.tgz delete mode 100755 pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/templates/alpine-pod.yaml delete mode 100755 pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/values.yaml delete mode 100755 pkg/chartutil/testdata/frobnitz_backslash/charts/mariner-4.3.2.tgz delete mode 100755 pkg/chartutil/testdata/frobnitz_backslash/docs/README.md delete mode 100755 pkg/chartutil/testdata/frobnitz_backslash/icon.svg delete mode 100755 pkg/chartutil/testdata/frobnitz_backslash/ignore/me.txt delete mode 100755 pkg/chartutil/testdata/frobnitz_backslash/templates/template.tpl delete mode 100755 pkg/chartutil/testdata/frobnitz_backslash/values.yaml delete mode 100644 pkg/chartutil/testdata/mariner/charts/albatross-0.1.0.tgz delete mode 100644 pkg/chartutil/testdata/moby/Chart.yaml delete mode 100644 pkg/chartutil/testdata/moby/charts/pequod/Chart.yaml delete mode 100644 pkg/chartutil/testdata/moby/charts/pequod/charts/ahab/Chart.yaml delete mode 100644 pkg/chartutil/testdata/moby/charts/pequod/charts/ahab/values.yaml delete mode 100644 pkg/chartutil/testdata/moby/charts/pequod/values.yaml delete mode 100644 pkg/chartutil/testdata/moby/charts/spouter/Chart.yaml delete mode 100644 pkg/chartutil/testdata/moby/charts/spouter/values.yaml delete mode 100644 pkg/chartutil/testdata/moby/values.yaml diff --git a/pkg/chartutil/coalesce_test.go b/pkg/chartutil/coalesce_test.go index 2a3d848fa..5a4656d71 100644 --- a/pkg/chartutil/coalesce_test.go +++ b/pkg/chartutil/coalesce_test.go @@ -21,6 +21,8 @@ import ( "testing" "github.com/stretchr/testify/assert" + + "helm.sh/helm/v3/pkg/chart" ) // ref: http://www.yaml.org/spec/1.2/spec.html#id2803362 @@ -55,9 +57,48 @@ pequod: bar: null `) +func withDeps(c *chart.Chart, deps ...*chart.Chart) *chart.Chart { + c.AddDependency(deps...) + return c +} + func TestCoalesceValues(t *testing.T) { is := assert.New(t) - c := loadChart(t, "testdata/moby") + + c := withDeps(&chart.Chart{ + Metadata: &chart.Metadata{Name: "moby"}, + Values: map[string]interface{}{ + "back": "exists", + "bottom": "exists", + "front": "exists", + "left": "exists", + "name": "moby", + "nested": map[string]interface{}{"boat": true}, + "override": "bad", + "right": "exists", + "scope": "moby", + "top": "nope", + }, + }, + withDeps(&chart.Chart{ + Metadata: &chart.Metadata{Name: "pequod"}, + Values: map[string]interface{}{"name": "pequod", "scope": "pequod"}, + }, + &chart.Chart{ + Metadata: &chart.Metadata{Name: "ahab"}, + Values: map[string]interface{}{ + "scope": "ahab", + "name": "ahab", + "boat": true, + "nested": map[string]interface{}{"foo": false, "bar": true}, + }, + }, + ), + &chart.Chart{ + Metadata: &chart.Metadata{Name: "spouter"}, + Values: map[string]interface{}{"scope": "spouter"}, + }, + ) vals, err := ReadValues(testCoalesceValuesYaml) if err != nil { diff --git a/pkg/chartutil/create_test.go b/pkg/chartutil/create_test.go index d2a3b0a20..a11c45140 100644 --- a/pkg/chartutil/create_test.go +++ b/pkg/chartutil/create_test.go @@ -81,7 +81,7 @@ func TestCreateFrom(t *testing.T) { Name: "foo", Version: "0.1.0", } - srcdir := "./testdata/mariner" + srcdir := "./testdata/frobnitz/charts/mariner" if err := CreateFrom(cf, tdir, srcdir); err != nil { t.Fatal(err) diff --git a/pkg/chartutil/testdata/frobnitz/charts/mariner-4.3.2.tgz b/pkg/chartutil/testdata/frobnitz/charts/mariner-4.3.2.tgz deleted file mode 100644 index 5648f6f6daa6b69765cb792f5926add825926dfc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 962 zcmV;z13mm7iwFRyACz1G1MQbxXcR{rzz+gJ7NkO|K16!MMWkA?*}eTZb5%on8bXs; zTaH9)Ax`e*a*O-b*|}hhHsC|6sZ=bjZ6z;NC>li(eXv3+5jBV+3c(J{0;`~^c-MiWUe|G+#V?eB#2v_+Eh#&|dMFHNZs@^CB!ACg%JQ@AFNsXB}MnCmDYVPVjaYD3sW1UXzsPK3-$e2`08u9QH43b2tGM`lbzPM8Cr z5@Z!Xy$upnd+ zU|nrfVubP<8g>A& zK%0B-I8>fy(#K9Kg+V?Yx%#s`L%@42z;TmS=k^qI5(xu>Q;0PIFV55XpAIpUwDdT_ zJh5jY8%!7fGyzPF{~$d93M~GM!Q|+kYVHx2e>ux{!2iuBzLL?w2J?(Cr*y-$73a2>tSQ$kP|LA{gIn*HE?W5Y zj@_dmb8q&p(t}G#WNgj2CH3gle_U$pZ9TOh^8Oq9`|B4EpT8o0+_e+;eQ;;zVB^N+ z$2txU^Uw7cTl<#JE7?$F5gKYuCv`%?GO@gtx8{{77>Z=Pt`_vw26fW;k4pXkZ( z{vrR!j)!}izo`2$xN@`k-GcJVy=NaioKyW_+2THXY5THyHCt;=jt0lOhS#r-Jn;Rx zw_8j5hi~kyPLIEPH*@Dc zVQyr3R8em|NM&qo0PNJUs=_c72H?(liabH@pWIst-7YSIyL+rhEHrIN(t?QZE=F{y zgNRfS&$pa5Ly`mMk2OB%pV`*9knW7FlL-Joo@KED7*{C$d;N~z z37$S{+}wvSU9}|VtF|fRpv9Ve>8dWo|9?5B+RE}Y9CFh-x#(Bq8Vck^V=NUiPLCKa z8z5CF#JgK!4>;$4Fm+FUst4d+{(+nP|0&J+e}(;l^U4@w-{=?s0RR8vgVbLD3;+OM Cs&R<` diff --git a/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/templates/alpine-pod.yaml b/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/templates/alpine-pod.yaml deleted file mode 100755 index 5bbae10af..000000000 --- a/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/templates/alpine-pod.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: {{.Release.Name}}-{{.Chart.Name}} - labels: - app.kubernetes.io/managed-by: {{.Release.Service}} - chartName: {{.Chart.Name}} - chartVersion: {{.Chart.Version | quote}} -spec: - restartPolicy: {{default "Never" .restart_policy}} - containers: - - name: waiter - image: "alpine:3.3" - command: ["/bin/sleep","9000"] diff --git a/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/values.yaml b/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/values.yaml deleted file mode 100755 index 6c2aab7ba..000000000 --- a/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/values.yaml +++ /dev/null @@ -1,2 +0,0 @@ -# The pod name -name: "my-alpine" diff --git a/pkg/chartutil/testdata/frobnitz_backslash/charts/mariner-4.3.2.tgz b/pkg/chartutil/testdata/frobnitz_backslash/charts/mariner-4.3.2.tgz deleted file mode 100755 index 5648f6f6daa6b69765cb792f5926add825926dfc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 962 zcmV;z13mm7iwFRyACz1G1MQbxXcR{rzz+gJ7NkO|K16!MMWkA?*}eTZb5%on8bXs; zTaH9)Ax`e*a*O-b*|}hhHsC|6sZ=bjZ6z;NC>li(eXv3+5jBV+3c(J{0;`~^c-MiWUe|G+#V?eB#2v_+Eh#&|dMFHNZs@^CB!ACg%JQ@AFNsXB}MnCmDYVPVjaYD3sW1UXzsPK3-$e2`08u9QH43b2tGM`lbzPM8Cr z5@Z!Xy$upnd+ zU|nrfVubP<8g>A& zK%0B-I8>fy(#K9Kg+V?Yx%#s`L%@42z;TmS=k^qI5(xu>Q;0PIFV55XpAIpUwDdT_ zJh5jY8%!7fGyzPF{~$d93M~GM!Q|+kYVHx2e>ux{!2iuBzLL?w2J?(Cr*y-$73a2>tSQ$kP|LA{gIn*HE?W5Y zj@_dmb8q&p(t}G#WNgj2CH3gle_U$pZ9TOh^8Oq9`|B4EpT8o0+_e+;eQ;;zVB^N+ z$2txU^Uw7cTl<#JE7?$F5gKYuCv`%?GO@gtx8{{77>Z=Pt`_vw26fW;k4pXkZ( z{vrR!j)!}izo`2$xN@`k-GcJVy=NaioKyW_+2THXY5THyHCt;=jt0lOhS#r-Jn;Rx zw_8j5hi~kyPLIEPH*@ - - Example icon - - - diff --git a/pkg/chartutil/testdata/frobnitz_backslash/ignore/me.txt b/pkg/chartutil/testdata/frobnitz_backslash/ignore/me.txt deleted file mode 100755 index e69de29bb..000000000 diff --git a/pkg/chartutil/testdata/frobnitz_backslash/templates/template.tpl b/pkg/chartutil/testdata/frobnitz_backslash/templates/template.tpl deleted file mode 100755 index c651ee6a0..000000000 --- a/pkg/chartutil/testdata/frobnitz_backslash/templates/template.tpl +++ /dev/null @@ -1 +0,0 @@ -Hello {{.Name | default "world"}} diff --git a/pkg/chartutil/testdata/frobnitz_backslash/values.yaml b/pkg/chartutil/testdata/frobnitz_backslash/values.yaml deleted file mode 100755 index 61f501258..000000000 --- a/pkg/chartutil/testdata/frobnitz_backslash/values.yaml +++ /dev/null @@ -1,6 +0,0 @@ -# A values file contains configuration. - -name: "Some Name" - -section: - name: "Name in a section" diff --git a/pkg/chartutil/testdata/mariner/charts/albatross-0.1.0.tgz b/pkg/chartutil/testdata/mariner/charts/albatross-0.1.0.tgz deleted file mode 100644 index 22c1fe57251c27c11d6e6b5c518fc998d62d5bfd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 305 zcmV-10nYv(iwFRyACz1G1MSpHZo)7S24L1c#fSq?8*C$GSFx$oefI_?B$A1d?I88` z4UYz?Ds)5oQ2%c;iflRK%uJkLx*S7F52L|IDd)z}F4@aL6Zy=4um7%o;h5^s6tq{Oaa%5^Zwj&Iw2JjEAJ-r0iT##Vhen|? zM0#$Q92?G@#QyydIZ+cSs&F`GJQhEFKe+8O|9j_KPDA_vzM6k&<{#(ZnmOkGJM{JM zrZvZw$3kp;SUO(_BG=|B#DW&VbF9}J#yA520000000000000000Q^R8xfQyO04M+e DGPIJ| diff --git a/pkg/chartutil/testdata/moby/Chart.yaml b/pkg/chartutil/testdata/moby/Chart.yaml deleted file mode 100644 index a5f992c61..000000000 --- a/pkg/chartutil/testdata/moby/Chart.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -description: A Helm chart for Kubernetes -name: moby -version: 0.1.0 diff --git a/pkg/chartutil/testdata/moby/charts/pequod/Chart.yaml b/pkg/chartutil/testdata/moby/charts/pequod/Chart.yaml deleted file mode 100644 index f1a8ef76b..000000000 --- a/pkg/chartutil/testdata/moby/charts/pequod/Chart.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -description: A Helm chart for Kubernetes -name: pequod -version: 0.1.0 diff --git a/pkg/chartutil/testdata/moby/charts/pequod/charts/ahab/Chart.yaml b/pkg/chartutil/testdata/moby/charts/pequod/charts/ahab/Chart.yaml deleted file mode 100644 index a7ee7bf90..000000000 --- a/pkg/chartutil/testdata/moby/charts/pequod/charts/ahab/Chart.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -description: A Helm chart for Kubernetes -name: ahab -version: 0.1.0 diff --git a/pkg/chartutil/testdata/moby/charts/pequod/charts/ahab/values.yaml b/pkg/chartutil/testdata/moby/charts/pequod/charts/ahab/values.yaml deleted file mode 100644 index eee6980fa..000000000 --- a/pkg/chartutil/testdata/moby/charts/pequod/charts/ahab/values.yaml +++ /dev/null @@ -1,6 +0,0 @@ -scope: ahab -name: ahab -boat: true -nested: - foo: false - bar: true diff --git a/pkg/chartutil/testdata/moby/charts/pequod/values.yaml b/pkg/chartutil/testdata/moby/charts/pequod/values.yaml deleted file mode 100644 index d6e34b274..000000000 --- a/pkg/chartutil/testdata/moby/charts/pequod/values.yaml +++ /dev/null @@ -1,2 +0,0 @@ -scope: pequod -name: pequod diff --git a/pkg/chartutil/testdata/moby/charts/spouter/Chart.yaml b/pkg/chartutil/testdata/moby/charts/spouter/Chart.yaml deleted file mode 100644 index 0525085b6..000000000 --- a/pkg/chartutil/testdata/moby/charts/spouter/Chart.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -description: A Helm chart for Kubernetes -name: spouter -version: 0.1.0 diff --git a/pkg/chartutil/testdata/moby/charts/spouter/values.yaml b/pkg/chartutil/testdata/moby/charts/spouter/values.yaml deleted file mode 100644 index f71d92a9f..000000000 --- a/pkg/chartutil/testdata/moby/charts/spouter/values.yaml +++ /dev/null @@ -1 +0,0 @@ -scope: spouter diff --git a/pkg/chartutil/testdata/moby/values.yaml b/pkg/chartutil/testdata/moby/values.yaml deleted file mode 100644 index 2169d7566..000000000 --- a/pkg/chartutil/testdata/moby/values.yaml +++ /dev/null @@ -1,11 +0,0 @@ -scope: moby -name: moby -override: bad -top: nope -bottom: exists -right: exists -left: exists -front: exists -back: exists -nested: - boat: true From bade6478fcdd537957f0ce8a2cc5e06a14940ea0 Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Fri, 15 May 2020 13:23:10 -0400 Subject: [PATCH 118/281] Fixing error with strvals parsing Closes #8140 Signed-off-by: Matt Farina --- pkg/strvals/parser.go | 30 +++++++++++++++++++++------- pkg/strvals/parser_test.go | 41 +++++++++++++++++++++++++++++++++++--- 2 files changed, 61 insertions(+), 10 deletions(-) diff --git a/pkg/strvals/parser.go b/pkg/strvals/parser.go index 03adbd3cb..db2fb60fe 100644 --- a/pkg/strvals/parser.go +++ b/pkg/strvals/parser.go @@ -17,6 +17,7 @@ package strvals import ( "bytes" + "fmt" "io" "strconv" "strings" @@ -230,14 +231,17 @@ func set(data map[string]interface{}, key string, val interface{}) { data[key] = val } -func setIndex(list []interface{}, index int, val interface{}) []interface{} { +func setIndex(list []interface{}, index int, val interface{}) ([]interface{}, error) { + if index < 0 { + return list, fmt.Errorf("negative %d index not allowed", index) + } if len(list) <= index { newlist := make([]interface{}, index+1) copy(newlist, list) list = newlist } list[index] = val - return list + return list, nil } func (t *parser) keyIndex() (int, error) { @@ -252,6 +256,9 @@ func (t *parser) keyIndex() (int, error) { } func (t *parser) listItem(list []interface{}, i int) ([]interface{}, error) { + if i < 0 { + return list, fmt.Errorf("negative %d index not allowed", i) + } stop := runeSet([]rune{'[', '.', '='}) switch k, last, err := runesUntil(t.sc, stop); { case len(k) > 0: @@ -262,16 +269,19 @@ func (t *parser) listItem(list []interface{}, i int) ([]interface{}, error) { vl, e := t.valList() switch e { case nil: - return setIndex(list, i, vl), nil + return setIndex(list, i, vl) case io.EOF: - return setIndex(list, i, ""), err + return setIndex(list, i, "") case ErrNotList: rs, e := t.val() if e != nil && e != io.EOF { return list, e } v, e := t.reader(rs) - return setIndex(list, i, v), e + if e != nil { + return list, e + } + return setIndex(list, i, v) default: return list, e } @@ -283,7 +293,10 @@ func (t *parser) listItem(list []interface{}, i int) ([]interface{}, error) { } // Now we need to get the value after the ]. list2, err := t.listItem(list, i) - return setIndex(list, i, list2), err + if err != nil { + return list, err + } + return setIndex(list, i, list2) case last == '.': // We have a nested object. Send to t.key inner := map[string]interface{}{} @@ -299,7 +312,10 @@ func (t *parser) listItem(list []interface{}, i int) ([]interface{}, error) { // Recurse e := t.key(inner) - return setIndex(list, i, inner), e + if e != nil { + return list, e + } + return setIndex(list, i, inner) default: return nil, errors.Errorf("parse error: unexpected token %v", last) } diff --git a/pkg/strvals/parser_test.go b/pkg/strvals/parser_test.go index 44d317220..fb18980cf 100644 --- a/pkg/strvals/parser_test.go +++ b/pkg/strvals/parser_test.go @@ -28,6 +28,7 @@ func TestSetIndex(t *testing.T) { expect []interface{} add int val int + err bool }{ { name: "short", @@ -35,6 +36,7 @@ func TestSetIndex(t *testing.T) { expect: []interface{}{0, 1, 2}, add: 2, val: 2, + err: false, }, { name: "equal", @@ -42,6 +44,7 @@ func TestSetIndex(t *testing.T) { expect: []interface{}{0, 2}, add: 1, val: 2, + err: false, }, { name: "long", @@ -49,17 +52,41 @@ func TestSetIndex(t *testing.T) { expect: []interface{}{0, 1, 2, 4, 4, 5}, add: 3, val: 4, + err: false, + }, + { + name: "negative", + initial: []interface{}{0, 1, 2, 3, 4, 5}, + expect: []interface{}{0, 1, 2, 3, 4, 5}, + add: -1, + val: 4, + err: true, }, } for _, tt := range tests { - got := setIndex(tt.initial, tt.add, tt.val) + got, err := setIndex(tt.initial, tt.add, tt.val) + + if err != nil && tt.err == false { + t.Fatalf("%s: Expected no error but error returned", tt.name) + } else if err == nil && tt.err == true { + t.Fatalf("%s: Expected error but no error returned", tt.name) + } + if len(got) != len(tt.expect) { t.Fatalf("%s: Expected length %d, got %d", tt.name, len(tt.expect), len(got)) } - if gg := got[tt.add].(int); gg != tt.val { - t.Errorf("%s, Expected value %d, got %d", tt.name, tt.val, gg) + if !tt.err { + if gg := got[tt.add].(int); gg != tt.val { + t.Errorf("%s, Expected value %d, got %d", tt.name, tt.val, gg) + } + } + + for k, v := range got { + if v != tt.expect[k] { + t.Errorf("%s, Expected value %d, got %d", tt.name, tt.expect[k], v) + } } } } @@ -271,6 +298,10 @@ func TestParseSet(t *testing.T) { }, }, }, + { + str: "list[0].foo=bar,list[-30].hello=world", + err: true, + }, { str: "list[0]=foo,list[1]=bar", expect: map[string]interface{}{"list": []string{"foo", "bar"}}, @@ -283,6 +314,10 @@ func TestParseSet(t *testing.T) { str: "list[0]=foo,list[3]=bar", expect: map[string]interface{}{"list": []interface{}{"foo", nil, nil, "bar"}}, }, + { + str: "list[0]=foo,list[-20]=bar", + err: true, + }, { str: "illegal[0]name.foo=bar", err: true, From 8f1f0e0db2d96fa3d85c8b7a8e461f6bec7bced9 Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Fri, 15 May 2020 16:00:57 -0400 Subject: [PATCH 119/281] Recovering from panic that can occur with make Signed-off-by: Matt Farina --- pkg/strvals/parser.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/pkg/strvals/parser.go b/pkg/strvals/parser.go index db2fb60fe..c52f38ab1 100644 --- a/pkg/strvals/parser.go +++ b/pkg/strvals/parser.go @@ -231,7 +231,16 @@ func set(data map[string]interface{}, key string, val interface{}) { data[key] = val } -func setIndex(list []interface{}, index int, val interface{}) ([]interface{}, error) { +func setIndex(list []interface{}, index int, val interface{}) (l2 []interface{}, err error) { + // There are possible index values that are out of range on a target system + // causing a panic. This will catch the panic and return an error instead. + // The value of the index that causes a panic varies from system to system. + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("error processing index %d: %s", index, r) + } + }() + if index < 0 { return list, fmt.Errorf("negative %d index not allowed", index) } From 6857da251edd416454827ac692c85de1ade6b5e6 Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Fri, 15 May 2020 16:52:04 -0400 Subject: [PATCH 120/281] Catching a potential panic in strval parsing Signed-off-by: Matt Farina --- pkg/strvals/parser.go | 7 ++++++- pkg/strvals/parser_test.go | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/pkg/strvals/parser.go b/pkg/strvals/parser.go index c52f38ab1..c735412e9 100644 --- a/pkg/strvals/parser.go +++ b/pkg/strvals/parser.go @@ -150,7 +150,12 @@ func runeSet(r []rune) map[rune]bool { return s } -func (t *parser) key(data map[string]interface{}) error { +func (t *parser) key(data map[string]interface{}) (reterr error) { + defer func() { + if r := recover(); r != nil { + reterr = fmt.Errorf("unable to parse key: %s", r) + } + }() stop := runeSet([]rune{'=', '[', ',', '.'}) for { switch k, last, err := runesUntil(t.sc, stop); { diff --git a/pkg/strvals/parser_test.go b/pkg/strvals/parser_test.go index fb18980cf..742256153 100644 --- a/pkg/strvals/parser_test.go +++ b/pkg/strvals/parser_test.go @@ -362,6 +362,10 @@ func TestParseSet(t *testing.T) { }, }, }, + { + str: "]={}].", + err: true, + }, } for _, tt := range tests { From 1a46c3495ab2aa101e31fe9dfbe10802bb7d0a63 Mon Sep 17 00:00:00 2001 From: Alan Zhu Date: Mon, 18 May 2020 09:25:19 +0800 Subject: [PATCH 121/281] Revert "group command for easy read" Signed-off-by: Alan Zhu --- cmd/helm/root.go | 63 ++++++++++++++++++------------------------------ 1 file changed, 23 insertions(+), 40 deletions(-) diff --git a/cmd/helm/root.go b/cmd/helm/root.go index ff4a01ad7..143745f29 100644 --- a/cmd/helm/root.go +++ b/cmd/helm/root.go @@ -26,7 +26,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/tools/clientcmd" - "k8s.io/kubectl/pkg/util/templates" "helm.sh/helm/v3/internal/completion" "helm.sh/helm/v3/internal/experimental/registry" @@ -133,47 +132,31 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string flags.ParseErrorsWhitelist.UnknownFlags = true flags.Parse(args) - commandGroups := templates.CommandGroups{ - { - Message: "Release Management Commands:", - Commands: []*cobra.Command{ - newInstallCmd(actionConfig, out), - newListCmd(actionConfig, out), - newGetCmd(actionConfig, out), - newStatusCmd(actionConfig, out), - newUpgradeCmd(actionConfig, out), - newHistoryCmd(actionConfig, out), - newRollbackCmd(actionConfig, out), - newReleaseTestCmd(actionConfig, out), - newUninstallCmd(actionConfig, out), - }, - }, - { - Message: "Chart Commands:", - Commands: []*cobra.Command{ - newCreateCmd(out), - newDependencyCmd(out), - newPackageCmd(out), - newTemplateCmd(actionConfig, out), - newLintCmd(out), - newVerifyCmd(out), - }, - }, - { - Message: "Chart Repository Commands:", - Commands: []*cobra.Command{ - newRepoCmd(out), - newSearchCmd(out), - newPullCmd(out), - newShowCmd(out), - }, - }, - } - commandGroups.Add(cmd) - templates.ActsAsRootCommand(cmd, []string{"options"}, commandGroups...) - // Add subcommands cmd.AddCommand( + // chart commands + newCreateCmd(out), + newDependencyCmd(out), + newPullCmd(out), + newShowCmd(out), + newLintCmd(out), + newPackageCmd(out), + newRepoCmd(out), + newSearchCmd(out), + newVerifyCmd(out), + + // release commands + newGetCmd(actionConfig, out), + newHistoryCmd(actionConfig, out), + newInstallCmd(actionConfig, out), + newListCmd(actionConfig, out), + newReleaseTestCmd(actionConfig, out), + newRollbackCmd(actionConfig, out), + newStatusCmd(actionConfig, out), + newTemplateCmd(actionConfig, out), + newUninstallCmd(actionConfig, out), + newUpgradeCmd(actionConfig, out), + newCompletionCmd(out), newEnvCmd(out), newPluginCmd(out), From 5840e529e143d3f63539a312b81848f0b25ff6ba Mon Sep 17 00:00:00 2001 From: Thomas O'Donnell Date: Mon, 18 May 2020 16:40:40 +0200 Subject: [PATCH 122/281] Update the Helm version docs Have just updated the helm version docs to include the GoVersion field. Signed-off-by: Thomas O'Donnell --- cmd/helm/version.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/helm/version.go b/cmd/helm/version.go index b3f831e07..2f50c876c 100644 --- a/cmd/helm/version.go +++ b/cmd/helm/version.go @@ -33,12 +33,13 @@ Show the version for Helm. This will print a representation the version of Helm. The output will look something like this: -version.BuildInfo{Version:"v2.0.0", GitCommit:"ff52399e51bb880526e9cd0ed8386f6433b74da1", GitTreeState:"clean"} +version.BuildInfo{Version:"v3.2.1", GitCommit:"fe51cd1e31e6a202cba7dead9552a6d418ded79a", GitTreeState:"clean", GoVersion:"go1.13.10"} - Version is the semantic version of the release. - GitCommit is the SHA for the commit that this version was built from. - GitTreeState is "clean" if there are no local code changes when this binary was built, and "dirty" if the binary was built from locally modified code. +- GoVersion is the version of Go that was used to compile Helm. When using the --template flag the following properties are available to use in the template: From 2ae83f276b60085c550fc81fb6e21b38146e2d9f Mon Sep 17 00:00:00 2001 From: Martin Hickey Date: Mon, 18 May 2020 18:06:09 +0000 Subject: [PATCH 123/281] Fix repo cache setting Fix `repo add` and `repo update` to use a repository cache set using `--repository-cache` flag Signed-off-by: Martin Hickey Signed-off-by: Trond Hindenes --- cmd/helm/repo_add.go | 3 +++ cmd/helm/repo_update.go | 9 +++++++-- cmd/helm/repo_update_test.go | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/cmd/helm/repo_add.go b/cmd/helm/repo_add.go index 3d36fd0ed..9c67641e5 100644 --- a/cmd/helm/repo_add.go +++ b/cmd/helm/repo_add.go @@ -130,6 +130,9 @@ func (o *repoAddOptions) run(out io.Writer) error { return err } + if o.repoCache != "" { + r.CachePath = o.repoCache + } if _, err := r.DownloadIndexFile(); err != nil { return errors.Wrapf(err, "looks like %q is not a valid chart repository or cannot be reached", o.url) } diff --git a/cmd/helm/repo_update.go b/cmd/helm/repo_update.go index 027f18518..5c1086e81 100644 --- a/cmd/helm/repo_update.go +++ b/cmd/helm/repo_update.go @@ -37,8 +37,9 @@ Information is cached locally, where it is used by commands like 'helm search'. var errNoRepositories = errors.New("no repositories found. You must add one before updating") type repoUpdateOptions struct { - update func([]*repo.ChartRepository, io.Writer) - repoFile string + update func([]*repo.ChartRepository, io.Writer) + repoFile string + repoCache string } func newRepoUpdateCmd(out io.Writer) *cobra.Command { @@ -52,6 +53,7 @@ func newRepoUpdateCmd(out io.Writer) *cobra.Command { Args: require.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { o.repoFile = settings.RepositoryConfig + o.repoCache = settings.RepositoryCache return o.run(out) }, } @@ -69,6 +71,9 @@ func (o *repoUpdateOptions) run(out io.Writer) error { if err != nil { return err } + if o.repoCache != "" { + r.CachePath = o.repoCache + } repos = append(repos, r) } diff --git a/cmd/helm/repo_update_test.go b/cmd/helm/repo_update_test.go index 6ddce0637..b13d575e2 100644 --- a/cmd/helm/repo_update_test.go +++ b/cmd/helm/repo_update_test.go @@ -19,6 +19,8 @@ import ( "bytes" "fmt" "io" + "io/ioutil" + "path/filepath" "strings" "testing" @@ -79,3 +81,34 @@ func TestUpdateCharts(t *testing.T) { t.Error("Update was not successful") } } + +func TestUpdateChartsCustomCache(t *testing.T) { + defer resetEnv()() + defer ensure.HelmHome(t)() + + ts, err := repotest.NewTempServer("testdata/testserver/*.*") + if err != nil { + t.Fatal(err) + } + defer ts.Stop() + + r, err := repo.NewChartRepository(&repo.Entry{ + Name: "charts", + URL: ts.URL(), + }, getter.All(settings)) + if err != nil { + t.Error(err) + } + + cachePath := ensure.TempDir(t) + cacheFile := filepath.Join(cachePath, "charts-index.yaml") + r.CachePath = cachePath + + b := bytes.NewBuffer(nil) + updateCharts([]*repo.ChartRepository{r}, b) + + _, err = ioutil.ReadFile(cacheFile) + if err != nil { + t.Error(err) + } +} From 5600a2c82d236422e54b6089d5384dea44349900 Mon Sep 17 00:00:00 2001 From: Martin Hickey Date: Tue, 19 May 2020 15:45:14 +0000 Subject: [PATCH 124/281] Fix unit test Signed-off-by: Martin Hickey --- cmd/helm/repo_update_test.go | 52 ++++++++++++++---------------------- 1 file changed, 20 insertions(+), 32 deletions(-) diff --git a/cmd/helm/repo_update_test.go b/cmd/helm/repo_update_test.go index b13d575e2..981f0bba3 100644 --- a/cmd/helm/repo_update_test.go +++ b/cmd/helm/repo_update_test.go @@ -19,7 +19,7 @@ import ( "bytes" "fmt" "io" - "io/ioutil" + "os" "path/filepath" "strings" "testing" @@ -52,6 +52,25 @@ func TestUpdateCmd(t *testing.T) { } } +func TestUpdateCustomCacheCmd(t *testing.T) { + var out bytes.Buffer + rootDir := ensure.TempDir(t) + cachePath := filepath.Join(rootDir, "updcustomcache") + _ = os.Mkdir(cachePath, os.ModePerm) + defer os.RemoveAll(cachePath) + o := &repoUpdateOptions{ + update: updateCharts, + repoFile: "testdata/repositories.yaml", + repoCache: cachePath, + } + if err := o.run(&out); err != nil { + t.Fatal(err) + } + if _, err := os.Stat(filepath.Join(cachePath, "charts-index.yaml")); err != nil { + t.Fatalf("error finding created index file in custom cache: %#v", err) + } +} + func TestUpdateCharts(t *testing.T) { defer resetEnv()() defer ensure.HelmHome(t)() @@ -81,34 +100,3 @@ func TestUpdateCharts(t *testing.T) { t.Error("Update was not successful") } } - -func TestUpdateChartsCustomCache(t *testing.T) { - defer resetEnv()() - defer ensure.HelmHome(t)() - - ts, err := repotest.NewTempServer("testdata/testserver/*.*") - if err != nil { - t.Fatal(err) - } - defer ts.Stop() - - r, err := repo.NewChartRepository(&repo.Entry{ - Name: "charts", - URL: ts.URL(), - }, getter.All(settings)) - if err != nil { - t.Error(err) - } - - cachePath := ensure.TempDir(t) - cacheFile := filepath.Join(cachePath, "charts-index.yaml") - r.CachePath = cachePath - - b := bytes.NewBuffer(nil) - updateCharts([]*repo.ChartRepository{r}, b) - - _, err = ioutil.ReadFile(cacheFile) - if err != nil { - t.Error(err) - } -} From 146e0f9cc3b9c7ca9cb9dd0eba12de2270ae6faf Mon Sep 17 00:00:00 2001 From: hzliangbin Date: Thu, 21 May 2020 18:00:16 +0800 Subject: [PATCH 125/281] add kind_sorter support for SecretList Signed-off-by: Bin Liang --- pkg/releaseutil/kind_sorter.go | 2 ++ pkg/releaseutil/kind_sorter_test.go | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/pkg/releaseutil/kind_sorter.go b/pkg/releaseutil/kind_sorter.go index 5b131b3b0..a340dfc29 100644 --- a/pkg/releaseutil/kind_sorter.go +++ b/pkg/releaseutil/kind_sorter.go @@ -37,6 +37,7 @@ var InstallOrder KindSortOrder = []string{ "PodDisruptionBudget", "ServiceAccount", "Secret", + "SecretList", "ConfigMap", "StorageClass", "PersistentVolume", @@ -93,6 +94,7 @@ var UninstallOrder KindSortOrder = []string{ "PersistentVolume", "StorageClass", "ConfigMap", + "SecretList", "Secret", "ServiceAccount", "PodDisruptionBudget", diff --git a/pkg/releaseutil/kind_sorter_test.go b/pkg/releaseutil/kind_sorter_test.go index 341f528a0..71d355210 100644 --- a/pkg/releaseutil/kind_sorter_test.go +++ b/pkg/releaseutil/kind_sorter_test.go @@ -25,6 +25,10 @@ import ( func TestKindSorter(t *testing.T) { manifests := []Manifest{ + { + Name: "E", + Head: &SimpleHead{Kind: "SecretList"}, + }, { Name: "i", Head: &SimpleHead{Kind: "ClusterRole"}, @@ -168,8 +172,8 @@ func TestKindSorter(t *testing.T) { order KindSortOrder expected string }{ - {"install", InstallOrder, "aAbcC3def1gh2iIjJkKlLmnopqrxstuvw!"}, - {"uninstall", UninstallOrder, "wvmutsxrqponLlKkJjIi2hg1fed3CcbAa!"}, + {"install", InstallOrder, "aAbcC3deEf1gh2iIjJkKlLmnopqrxstuvw!"}, + {"uninstall", UninstallOrder, "wvmutsxrqponLlKkJjIi2hg1fEed3CcbAa!"}, } { var buf bytes.Buffer t.Run(test.description, func(t *testing.T) { From 6d49f562bce78c6459f11e82ea8d37e12edb8590 Mon Sep 17 00:00:00 2001 From: vitt-bagal <31851690+vitt-bagal@users.noreply.github.com> Date: Wed, 20 May 2020 19:00:17 +0530 Subject: [PATCH 126/281] Added s390x support Signed-off-by: vitthalb@us.ibm.com --- scripts/get-helm-3 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/get-helm-3 b/scripts/get-helm-3 index 1d0b3502e..22a93aa0e 100755 --- a/scripts/get-helm-3 +++ b/scripts/get-helm-3 @@ -33,6 +33,7 @@ initArch() { x86_64) ARCH="amd64";; i686) ARCH="386";; i386) ARCH="386";; + s390x) ARCH="s390x";; esac } @@ -60,7 +61,7 @@ runAsRoot() { # 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" + local supported="darwin-386\ndarwin-amd64\nlinux-386\nlinux-amd64\nlinux-arm\nlinux-arm64\nlinux-ppc64le\nlinux-s390x\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" From dbd001e5326561076e17a62b41ea3a9aa18e069d Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Thu, 21 May 2020 15:26:16 -0400 Subject: [PATCH 127/281] Removing tiller language Since Tiller is no longer part of Helm v3, internal documentation language about Tiller can be removed Signed-off-by: Matt Farina --- pkg/engine/doc.go | 8 ++++---- pkg/engine/engine.go | 2 +- pkg/kube/client.go | 2 +- pkg/lint/rules/template.go | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pkg/engine/doc.go b/pkg/engine/doc.go index a68b6f7af..6ff875c46 100644 --- a/pkg/engine/doc.go +++ b/pkg/engine/doc.go @@ -14,10 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -/*Package engine implements the Go template engine as a Tiller Engine. +/*Package engine implements the Go text template engine as needed for Helm. -Tiller provides a simple interface for taking a Chart and rendering its templates. -The 'engine' package implements this interface using Go's built-in 'text/template' -package. +When Helm renders templates it does so with additional functions and different +modes (e.g., strict, lint mode). This package handles the helm specific +implementation. */ package engine // import "helm.sh/helm/v3/pkg/engine" diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index c5d064ad5..5aa0ed8ec 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -33,7 +33,7 @@ import ( "helm.sh/helm/v3/pkg/chartutil" ) -// Engine is an implementation of 'cmd/tiller/environment'.Engine that uses Go templates. +// Engine is an implementation of the Helm rendering implementation for templates. type Engine struct { // If strict is enabled, template rendering will fail if a template references // a value that was not passed in. diff --git a/pkg/kube/client.go b/pkg/kube/client.go index 4683d8033..f908611db 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -434,7 +434,7 @@ func updateResource(c *Client, target *resource.Info, currentObj runtime.Object, if patch == nil || string(patch) == "{}" { c.Log("Looks like there are no changes for %s %q", target.Mapping.GroupVersionKind.Kind, target.Name) - // This needs to happen to make sure that tiller has the latest info from the API + // This needs to happen to make sure that Helm has the latest info from the API // Otherwise there will be no labels and other functions that use labels will panic if err := target.Get(); err != nil { return errors.Wrap(err, "failed to refresh resource information") diff --git a/pkg/lint/rules/template.go b/pkg/lint/rules/template.go index 787c5b26a..cf8b4f84b 100644 --- a/pkg/lint/rules/template.go +++ b/pkg/lint/rules/template.go @@ -57,7 +57,7 @@ func Templates(linter *support.Linter, values map[string]interface{}, namespace return } - // Load chart and parse templates, based on tiller/release_server + // Load chart and parse templates chart, err := loader.Load(linter.ChartDir) chartLoaded := linter.RunLinterRule(support.ErrorSev, path, err) From 99f277a2f3de395160b473ac7a8cae6c380b6d1c Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Fri, 22 May 2020 09:51:48 -0400 Subject: [PATCH 128/281] Using flags instead of persistent flags on status Persistent flags are available on subcommands. Status does not need the persistent nature. Closes #8149 Signed-off-by: Matt Farina --- cmd/helm/status.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/helm/status.go b/cmd/helm/status.go index 6313b3975..2efb2006c 100644 --- a/cmd/helm/status.go +++ b/cmd/helm/status.go @@ -74,7 +74,7 @@ func newStatusCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { return compListReleases(toComplete, cfg) }) - f := cmd.PersistentFlags() + f := cmd.Flags() f.IntVar(&client.Version, "revision", 0, "if set, display the status of the named release with revision") flag := f.Lookup("revision") From c56778a8d8c7fe3237bb38f6019d7003d85603d0 Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Fri, 22 May 2020 11:43:32 -0400 Subject: [PATCH 129/281] Removing legacy completions.bash file This file was previously used for shell completions but is no longer in use as there is a new completions system in place and there has been for some time. The contents of this file were out of date and reflect Helm v2. Closes #8186 Signed-off-by: Matt Farina --- scripts/completions.bash | 1632 -------------------------------------- 1 file changed, 1632 deletions(-) delete mode 100644 scripts/completions.bash diff --git a/scripts/completions.bash b/scripts/completions.bash deleted file mode 100644 index 027cc3322..000000000 --- a/scripts/completions.bash +++ /dev/null @@ -1,1632 +0,0 @@ -# bash completion for helm -*- shell-script -*- - -__debug() -{ - if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then - echo "$*" >> "${BASH_COMP_DEBUG_FILE}" - fi -} - -# Homebrew on Macs have version 1.3 of bash-completion which doesn't include -# _init_completion. This is a very minimal version of that function. -__my_init_completion() -{ - COMPREPLY=() - _get_comp_words_by_ref "$@" cur prev words cword -} - -__index_of_word() -{ - local w word=$1 - shift - index=0 - for w in "$@"; do - [[ $w = "$word" ]] && return - index=$((index+1)) - done - index=-1 -} - -__contains_word() -{ - local w word=$1; shift - for w in "$@"; do - [[ $w = "$word" ]] && return - done - return 1 -} - -__handle_reply() -{ - __debug "${FUNCNAME[0]}" - case $cur in - -*) - if [[ $(type -t compopt) = "builtin" ]]; then - compopt -o nospace - fi - local allflags - if [ ${#must_have_one_flag[@]} -ne 0 ]; then - allflags=("${must_have_one_flag[@]}") - else - allflags=("${flags[*]} ${two_word_flags[*]}") - fi - COMPREPLY=( $(compgen -W "${allflags[*]}" -- "$cur") ) - if [[ $(type -t compopt) = "builtin" ]]; then - [[ "${COMPREPLY[0]}" == *= ]] || compopt +o nospace - fi - - # complete after --flag=abc - if [[ $cur == *=* ]]; then - if [[ $(type -t compopt) = "builtin" ]]; then - compopt +o nospace - fi - - local index flag - flag="${cur%%=*}" - __index_of_word "${flag}" "${flags_with_completion[@]}" - if [[ ${index} -ge 0 ]]; then - COMPREPLY=() - PREFIX="" - cur="${cur#*=}" - ${flags_completion[${index}]} - if [ -n "${ZSH_VERSION}" ]; then - # zfs completion needs --flag= prefix - eval "COMPREPLY=( \"\${COMPREPLY[@]/#/${flag}=}\" )" - fi - fi - fi - return 0; - ;; - esac - - # check if we are handling a flag with special work handling - local index - __index_of_word "${prev}" "${flags_with_completion[@]}" - if [[ ${index} -ge 0 ]]; then - ${flags_completion[${index}]} - return - fi - - # we are parsing a flag and don't have a special handler, no completion - if [[ ${cur} != "${words[cword]}" ]]; then - return - fi - - local completions - completions=("${commands[@]}") - if [[ ${#must_have_one_noun[@]} -ne 0 ]]; then - completions=("${must_have_one_noun[@]}") - fi - if [[ ${#must_have_one_flag[@]} -ne 0 ]]; then - completions+=("${must_have_one_flag[@]}") - fi - COMPREPLY=( $(compgen -W "${completions[*]}" -- "$cur") ) - - if [[ ${#COMPREPLY[@]} -eq 0 && ${#noun_aliases[@]} -gt 0 && ${#must_have_one_noun[@]} -ne 0 ]]; then - COMPREPLY=( $(compgen -W "${noun_aliases[*]}" -- "$cur") ) - fi - - if [[ ${#COMPREPLY[@]} -eq 0 ]]; then - declare -F __custom_func >/dev/null && __custom_func - fi - - __ltrim_colon_completions "$cur" -} - -# The arguments should be in the form "ext1|ext2|extn" -__handle_filename_extension_flag() -{ - local ext="$1" - _filedir "@(${ext})" -} - -__handle_subdirs_in_dir_flag() -{ - local dir="$1" - pushd "${dir}" >/dev/null 2>&1 && _filedir -d && popd >/dev/null 2>&1 -} - -__handle_flag() -{ - __debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" - - # if a command required a flag, and we found it, unset must_have_one_flag() - local flagname=${words[c]} - local flagvalue - # if the word contained an = - if [[ ${words[c]} == *"="* ]]; then - flagvalue=${flagname#*=} # take in as flagvalue after the = - flagname=${flagname%%=*} # strip everything after the = - flagname="${flagname}=" # but put the = back - fi - __debug "${FUNCNAME[0]}: looking for ${flagname}" - if __contains_word "${flagname}" "${must_have_one_flag[@]}"; then - must_have_one_flag=() - fi - - # if you set a flag which only applies to this command, don't show subcommands - if __contains_word "${flagname}" "${local_nonpersistent_flags[@]}"; then - commands=() - fi - - # keep flag value with flagname as flaghash - if [ -n "${flagvalue}" ] ; then - flaghash[${flagname}]=${flagvalue} - elif [ -n "${words[ $((c+1)) ]}" ] ; then - flaghash[${flagname}]=${words[ $((c+1)) ]} - else - flaghash[${flagname}]="true" # pad "true" for bool flag - fi - - # skip the argument to a two word flag - if __contains_word "${words[c]}" "${two_word_flags[@]}"; then - c=$((c+1)) - # if we are looking for a flags value, don't show commands - if [[ $c -eq $cword ]]; then - commands=() - fi - fi - - c=$((c+1)) - -} - -__handle_noun() -{ - __debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" - - if __contains_word "${words[c]}" "${must_have_one_noun[@]}"; then - must_have_one_noun=() - elif __contains_word "${words[c]}" "${noun_aliases[@]}"; then - must_have_one_noun=() - fi - - nouns+=("${words[c]}") - c=$((c+1)) -} - -__handle_command() -{ - __debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" - - local next_command - if [[ -n ${last_command} ]]; then - next_command="_${last_command}_${words[c]//:/__}" - else - if [[ $c -eq 0 ]]; then - next_command="_$(basename "${words[c]//:/__}")" - else - next_command="_${words[c]//:/__}" - fi - fi - c=$((c+1)) - __debug "${FUNCNAME[0]}: looking for ${next_command}" - declare -F $next_command >/dev/null && $next_command -} - -__handle_word() -{ - if [[ $c -ge $cword ]]; then - __handle_reply - return - fi - __debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" - if [[ "${words[c]}" == -* ]]; then - __handle_flag - elif __contains_word "${words[c]}" "${commands[@]}"; then - __handle_command - elif [[ $c -eq 0 ]] && __contains_word "$(basename "${words[c]}")" "${commands[@]}"; then - __handle_command - else - __handle_noun - fi - __handle_word -} - -_helm_completion() -{ - last_command="helm_completion" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - must_have_one_noun+=("bash") - must_have_one_noun+=("zsh") - noun_aliases=() -} - -_helm_create() -{ - last_command="helm_create" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--starter=") - two_word_flags+=("-p") - local_nonpersistent_flags+=("--starter=") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_delete() -{ - last_command="helm_delete" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--dry-run") - local_nonpersistent_flags+=("--dry-run") - flags+=("--no-hooks") - local_nonpersistent_flags+=("--no-hooks") - flags+=("--keep-history") - local_nonpersistent_flags+=("--keep-history") - flags+=("--timeout=") - local_nonpersistent_flags+=("--timeout=") - flags+=("--tls") - local_nonpersistent_flags+=("--tls") - flags+=("--tls-ca-cert=") - local_nonpersistent_flags+=("--tls-ca-cert=") - flags+=("--tls-cert=") - local_nonpersistent_flags+=("--tls-cert=") - flags+=("--tls-key=") - local_nonpersistent_flags+=("--tls-key=") - flags+=("--tls-verify") - local_nonpersistent_flags+=("--tls-verify") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_dependency_build() -{ - last_command="helm_dependency_build" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--keyring=") - local_nonpersistent_flags+=("--keyring=") - flags+=("--verify") - local_nonpersistent_flags+=("--verify") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_dependency_list() -{ - last_command="helm_dependency_list" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_dependency_update() -{ - last_command="helm_dependency_update" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--keyring=") - local_nonpersistent_flags+=("--keyring=") - flags+=("--skip-refresh") - local_nonpersistent_flags+=("--skip-refresh") - flags+=("--verify") - local_nonpersistent_flags+=("--verify") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_dependency() -{ - last_command="helm_dependency" - commands=() - commands+=("build") - commands+=("list") - commands+=("update") - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_fetch() -{ - last_command="helm_fetch" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--ca-file=") - local_nonpersistent_flags+=("--ca-file=") - flags+=("--cert-file=") - local_nonpersistent_flags+=("--cert-file=") - flags+=("--destination=") - two_word_flags+=("-d") - local_nonpersistent_flags+=("--destination=") - flags+=("--devel") - local_nonpersistent_flags+=("--devel") - flags+=("--key-file=") - local_nonpersistent_flags+=("--key-file=") - flags+=("--keyring=") - local_nonpersistent_flags+=("--keyring=") - flags+=("--prov") - local_nonpersistent_flags+=("--prov") - flags+=("--repo=") - local_nonpersistent_flags+=("--repo=") - flags+=("--untar") - local_nonpersistent_flags+=("--untar") - flags+=("--untardir=") - local_nonpersistent_flags+=("--untardir=") - flags+=("--verify") - local_nonpersistent_flags+=("--verify") - flags+=("--version=") - local_nonpersistent_flags+=("--version=") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_get_hooks() -{ - last_command="helm_get_hooks" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--revision=") - local_nonpersistent_flags+=("--revision=") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_get_manifest() -{ - last_command="helm_get_manifest" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--revision=") - local_nonpersistent_flags+=("--revision=") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_get_values() -{ - last_command="helm_get_values" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--all") - flags+=("-a") - local_nonpersistent_flags+=("--all") - flags+=("--revision=") - local_nonpersistent_flags+=("--revision=") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_get() -{ - last_command="helm_get" - commands=() - commands+=("hooks") - commands+=("manifest") - commands+=("values") - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--revision=") - local_nonpersistent_flags+=("--revision=") - flags+=("--tls") - local_nonpersistent_flags+=("--tls") - flags+=("--tls-ca-cert=") - local_nonpersistent_flags+=("--tls-ca-cert=") - flags+=("--tls-cert=") - local_nonpersistent_flags+=("--tls-cert=") - flags+=("--tls-key=") - local_nonpersistent_flags+=("--tls-key=") - flags+=("--tls-verify") - local_nonpersistent_flags+=("--tls-verify") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_history() -{ - last_command="helm_history" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--max=") - local_nonpersistent_flags+=("--max=") - flags+=("--tls") - local_nonpersistent_flags+=("--tls") - flags+=("--tls-ca-cert=") - local_nonpersistent_flags+=("--tls-ca-cert=") - flags+=("--tls-cert=") - local_nonpersistent_flags+=("--tls-cert=") - flags+=("--tls-key=") - local_nonpersistent_flags+=("--tls-key=") - flags+=("--tls-verify") - local_nonpersistent_flags+=("--tls-verify") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_home() -{ - last_command="helm_home" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_init() -{ - last_command="helm_init" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--canary-image") - local_nonpersistent_flags+=("--canary-image") - flags+=("--client-only") - flags+=("-c") - local_nonpersistent_flags+=("--client-only") - flags+=("--dry-run") - local_nonpersistent_flags+=("--dry-run") - flags+=("--local-repo-url=") - local_nonpersistent_flags+=("--local-repo-url=") - flags+=("--net-host") - local_nonpersistent_flags+=("--net-host") - flags+=("--service-account=") - local_nonpersistent_flags+=("--service-account=") - flags+=("--skip-refresh") - local_nonpersistent_flags+=("--skip-refresh") - flags+=("--tiller-image=") - two_word_flags+=("-i") - local_nonpersistent_flags+=("--tiller-image=") - flags+=("--tiller-tls") - local_nonpersistent_flags+=("--tiller-tls") - flags+=("--tiller-tls-cert=") - local_nonpersistent_flags+=("--tiller-tls-cert=") - flags+=("--tiller-tls-key=") - local_nonpersistent_flags+=("--tiller-tls-key=") - flags+=("--tiller-tls-verify") - local_nonpersistent_flags+=("--tiller-tls-verify") - flags+=("--tls-ca-cert=") - local_nonpersistent_flags+=("--tls-ca-cert=") - flags+=("--upgrade") - local_nonpersistent_flags+=("--upgrade") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_inspect_chart() -{ - last_command="helm_inspect_chart" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--ca-file=") - local_nonpersistent_flags+=("--ca-file=") - flags+=("--cert-file=") - local_nonpersistent_flags+=("--cert-file=") - flags+=("--key-file=") - local_nonpersistent_flags+=("--key-file=") - flags+=("--keyring=") - local_nonpersistent_flags+=("--keyring=") - flags+=("--repo=") - local_nonpersistent_flags+=("--repo=") - flags+=("--verify") - local_nonpersistent_flags+=("--verify") - flags+=("--version=") - local_nonpersistent_flags+=("--version=") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_inspect_values() -{ - last_command="helm_inspect_values" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--ca-file=") - local_nonpersistent_flags+=("--ca-file=") - flags+=("--cert-file=") - local_nonpersistent_flags+=("--cert-file=") - flags+=("--key-file=") - local_nonpersistent_flags+=("--key-file=") - flags+=("--keyring=") - local_nonpersistent_flags+=("--keyring=") - flags+=("--repo=") - local_nonpersistent_flags+=("--repo=") - flags+=("--verify") - local_nonpersistent_flags+=("--verify") - flags+=("--version=") - local_nonpersistent_flags+=("--version=") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_inspect() -{ - last_command="helm_inspect" - commands=() - commands+=("chart") - commands+=("values") - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--ca-file=") - local_nonpersistent_flags+=("--ca-file=") - flags+=("--cert-file=") - local_nonpersistent_flags+=("--cert-file=") - flags+=("--key-file=") - local_nonpersistent_flags+=("--key-file=") - flags+=("--keyring=") - local_nonpersistent_flags+=("--keyring=") - flags+=("--repo=") - local_nonpersistent_flags+=("--repo=") - flags+=("--verify") - local_nonpersistent_flags+=("--verify") - flags+=("--version=") - local_nonpersistent_flags+=("--version=") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_install() -{ - last_command="helm_install" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--ca-file=") - local_nonpersistent_flags+=("--ca-file=") - flags+=("--cert-file=") - local_nonpersistent_flags+=("--cert-file=") - flags+=("--devel") - local_nonpersistent_flags+=("--devel") - flags+=("--dry-run") - local_nonpersistent_flags+=("--dry-run") - flags+=("--key-file=") - local_nonpersistent_flags+=("--key-file=") - flags+=("--keyring=") - local_nonpersistent_flags+=("--keyring=") - flags+=("--name=") - two_word_flags+=("-n") - local_nonpersistent_flags+=("--name=") - flags+=("--name-template=") - local_nonpersistent_flags+=("--name-template=") - flags+=("--namespace=") - local_nonpersistent_flags+=("--namespace=") - flags+=("--no-hooks") - local_nonpersistent_flags+=("--no-hooks") - flags+=("--replace") - local_nonpersistent_flags+=("--replace") - flags+=("--repo=") - local_nonpersistent_flags+=("--repo=") - flags+=("--set=") - local_nonpersistent_flags+=("--set=") - flags+=("--set-string=") - local_nonpersistent_flags+=("--set-string=") - flags+=("--timeout=") - local_nonpersistent_flags+=("--timeout=") - flags+=("--tls") - local_nonpersistent_flags+=("--tls") - flags+=("--tls-ca-cert=") - local_nonpersistent_flags+=("--tls-ca-cert=") - flags+=("--tls-cert=") - local_nonpersistent_flags+=("--tls-cert=") - flags+=("--tls-key=") - local_nonpersistent_flags+=("--tls-key=") - flags+=("--tls-verify") - local_nonpersistent_flags+=("--tls-verify") - flags+=("--values=") - two_word_flags+=("-f") - local_nonpersistent_flags+=("--values=") - flags+=("--verify") - local_nonpersistent_flags+=("--verify") - flags+=("--version=") - local_nonpersistent_flags+=("--version=") - flags+=("--wait") - local_nonpersistent_flags+=("--wait") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_lint() -{ - last_command="helm_lint" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--strict") - local_nonpersistent_flags+=("--strict") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_list() -{ - last_command="helm_list" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--all") - local_nonpersistent_flags+=("--all") - flags+=("--date") - flags+=("-d") - local_nonpersistent_flags+=("--date") - flags+=("--uninstalled") - local_nonpersistent_flags+=("--uninstalled") - flags+=("--uninstalling") - local_nonpersistent_flags+=("--uninstalling") - flags+=("--deployed") - local_nonpersistent_flags+=("--deployed") - flags+=("--failed") - local_nonpersistent_flags+=("--failed") - flags+=("--max=") - two_word_flags+=("-m") - local_nonpersistent_flags+=("--max=") - flags+=("--namespace=") - local_nonpersistent_flags+=("--namespace=") - flags+=("--offset=") - two_word_flags+=("-o") - local_nonpersistent_flags+=("--offset=") - flags+=("--reverse") - flags+=("-r") - local_nonpersistent_flags+=("--reverse") - flags+=("--short") - flags+=("-q") - local_nonpersistent_flags+=("--short") - flags+=("--tls") - local_nonpersistent_flags+=("--tls") - flags+=("--tls-ca-cert=") - local_nonpersistent_flags+=("--tls-ca-cert=") - flags+=("--tls-cert=") - local_nonpersistent_flags+=("--tls-cert=") - flags+=("--tls-key=") - local_nonpersistent_flags+=("--tls-key=") - flags+=("--tls-verify") - local_nonpersistent_flags+=("--tls-verify") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_package() -{ - last_command="helm_package" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--destination=") - two_word_flags+=("-d") - local_nonpersistent_flags+=("--destination=") - flags+=("--key=") - local_nonpersistent_flags+=("--key=") - flags+=("--keyring=") - local_nonpersistent_flags+=("--keyring=") - flags+=("--save") - local_nonpersistent_flags+=("--save") - flags+=("--sign") - local_nonpersistent_flags+=("--sign") - flags+=("--version=") - local_nonpersistent_flags+=("--version=") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_plugin_install() -{ - last_command="helm_plugin_install" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--version=") - local_nonpersistent_flags+=("--version=") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_plugin_list() -{ - last_command="helm_plugin_list" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_plugin_remove() -{ - last_command="helm_plugin_remove" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_plugin_update() -{ - last_command="helm_plugin_update" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_plugin() -{ - last_command="helm_plugin" - commands=() - commands+=("install") - commands+=("list") - commands+=("remove") - commands+=("update") - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_repo_add() -{ - last_command="helm_repo_add" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--ca-file=") - local_nonpersistent_flags+=("--ca-file=") - flags+=("--cert-file=") - local_nonpersistent_flags+=("--cert-file=") - flags+=("--key-file=") - local_nonpersistent_flags+=("--key-file=") - flags+=("--no-update") - local_nonpersistent_flags+=("--no-update") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_repo_index() -{ - last_command="helm_repo_index" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--merge=") - local_nonpersistent_flags+=("--merge=") - flags+=("--url=") - local_nonpersistent_flags+=("--url=") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_repo_list() -{ - last_command="helm_repo_list" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_repo_remove() -{ - last_command="helm_repo_remove" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_repo_update() -{ - last_command="helm_repo_update" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_repo() -{ - last_command="helm_repo" - commands=() - commands+=("add") - commands+=("index") - commands+=("list") - commands+=("remove") - commands+=("update") - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_reset() -{ - last_command="helm_reset" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--force") - flags+=("-f") - local_nonpersistent_flags+=("--force") - flags+=("--remove-helm-home") - local_nonpersistent_flags+=("--remove-helm-home") - flags+=("--tls") - local_nonpersistent_flags+=("--tls") - flags+=("--tls-ca-cert=") - local_nonpersistent_flags+=("--tls-ca-cert=") - flags+=("--tls-cert=") - local_nonpersistent_flags+=("--tls-cert=") - flags+=("--tls-key=") - local_nonpersistent_flags+=("--tls-key=") - flags+=("--tls-verify") - local_nonpersistent_flags+=("--tls-verify") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_rollback() -{ - last_command="helm_rollback" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--dry-run") - local_nonpersistent_flags+=("--dry-run") - flags+=("--force") - local_nonpersistent_flags+=("--force") - flags+=("--no-hooks") - local_nonpersistent_flags+=("--no-hooks") - flags+=("--recreate-pods") - local_nonpersistent_flags+=("--recreate-pods") - flags+=("--timeout=") - local_nonpersistent_flags+=("--timeout=") - flags+=("--tls") - local_nonpersistent_flags+=("--tls") - flags+=("--tls-ca-cert=") - local_nonpersistent_flags+=("--tls-ca-cert=") - flags+=("--tls-cert=") - local_nonpersistent_flags+=("--tls-cert=") - flags+=("--tls-key=") - local_nonpersistent_flags+=("--tls-key=") - flags+=("--tls-verify") - local_nonpersistent_flags+=("--tls-verify") - flags+=("--wait") - local_nonpersistent_flags+=("--wait") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_search() -{ - last_command="helm_search" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--regexp") - flags+=("-r") - local_nonpersistent_flags+=("--regexp") - flags+=("--version=") - two_word_flags+=("-v") - local_nonpersistent_flags+=("--version=") - flags+=("--versions") - flags+=("-l") - local_nonpersistent_flags+=("--versions") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_serve() -{ - last_command="helm_serve" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--address=") - local_nonpersistent_flags+=("--address=") - flags+=("--repo-path=") - local_nonpersistent_flags+=("--repo-path=") - flags+=("--url=") - local_nonpersistent_flags+=("--url=") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_status() -{ - last_command="helm_status" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--revision=") - flags+=("--tls") - local_nonpersistent_flags+=("--tls") - flags+=("--tls-ca-cert=") - local_nonpersistent_flags+=("--tls-ca-cert=") - flags+=("--tls-cert=") - local_nonpersistent_flags+=("--tls-cert=") - flags+=("--tls-key=") - local_nonpersistent_flags+=("--tls-key=") - flags+=("--tls-verify") - local_nonpersistent_flags+=("--tls-verify") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_test() -{ - last_command="helm_test" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--cleanup") - local_nonpersistent_flags+=("--cleanup") - flags+=("--timeout=") - local_nonpersistent_flags+=("--timeout=") - flags+=("--tls") - local_nonpersistent_flags+=("--tls") - flags+=("--tls-ca-cert=") - local_nonpersistent_flags+=("--tls-ca-cert=") - flags+=("--tls-cert=") - local_nonpersistent_flags+=("--tls-cert=") - flags+=("--tls-key=") - local_nonpersistent_flags+=("--tls-key=") - flags+=("--tls-verify") - local_nonpersistent_flags+=("--tls-verify") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_upgrade() -{ - last_command="helm_upgrade" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--ca-file=") - local_nonpersistent_flags+=("--ca-file=") - flags+=("--cert-file=") - local_nonpersistent_flags+=("--cert-file=") - flags+=("--devel") - local_nonpersistent_flags+=("--devel") - flags+=("--disable-hooks") - local_nonpersistent_flags+=("--disable-hooks") - flags+=("--dry-run") - local_nonpersistent_flags+=("--dry-run") - flags+=("--force") - local_nonpersistent_flags+=("--force") - flags+=("--install") - flags+=("-i") - local_nonpersistent_flags+=("--install") - flags+=("--key-file=") - local_nonpersistent_flags+=("--key-file=") - flags+=("--keyring=") - local_nonpersistent_flags+=("--keyring=") - flags+=("--namespace=") - local_nonpersistent_flags+=("--namespace=") - flags+=("--no-hooks") - local_nonpersistent_flags+=("--no-hooks") - flags+=("--recreate-pods") - local_nonpersistent_flags+=("--recreate-pods") - flags+=("--repo=") - local_nonpersistent_flags+=("--repo=") - flags+=("--reset-values") - local_nonpersistent_flags+=("--reset-values") - flags+=("--reuse-values") - local_nonpersistent_flags+=("--reuse-values") - flags+=("--set=") - local_nonpersistent_flags+=("--set=") - flags+=("--timeout=") - local_nonpersistent_flags+=("--timeout=") - flags+=("--tls") - local_nonpersistent_flags+=("--tls") - flags+=("--tls-ca-cert=") - local_nonpersistent_flags+=("--tls-ca-cert=") - flags+=("--tls-cert=") - local_nonpersistent_flags+=("--tls-cert=") - flags+=("--tls-key=") - local_nonpersistent_flags+=("--tls-key=") - flags+=("--tls-verify") - local_nonpersistent_flags+=("--tls-verify") - flags+=("--values=") - two_word_flags+=("-f") - local_nonpersistent_flags+=("--values=") - flags+=("--verify") - local_nonpersistent_flags+=("--verify") - flags+=("--version=") - local_nonpersistent_flags+=("--version=") - flags+=("--wait") - local_nonpersistent_flags+=("--wait") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_verify() -{ - last_command="helm_verify" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--keyring=") - local_nonpersistent_flags+=("--keyring=") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_version() -{ - last_command="helm_version" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--client") - flags+=("-c") - local_nonpersistent_flags+=("--client") - flags+=("--server") - flags+=("-s") - local_nonpersistent_flags+=("--server") - flags+=("--short") - local_nonpersistent_flags+=("--short") - flags+=("--tls") - local_nonpersistent_flags+=("--tls") - flags+=("--tls-ca-cert=") - local_nonpersistent_flags+=("--tls-ca-cert=") - flags+=("--tls-cert=") - local_nonpersistent_flags+=("--tls-cert=") - flags+=("--tls-key=") - local_nonpersistent_flags+=("--tls-key=") - flags+=("--tls-verify") - local_nonpersistent_flags+=("--tls-verify") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm() -{ - last_command="helm" - commands=() - commands+=("completion") - commands+=("create") - commands+=("delete") - commands+=("dependency") - commands+=("fetch") - commands+=("get") - commands+=("history") - commands+=("home") - commands+=("init") - commands+=("inspect") - commands+=("install") - commands+=("lint") - commands+=("list") - commands+=("package") - commands+=("plugin") - commands+=("repo") - commands+=("reset") - commands+=("rollback") - commands+=("search") - commands+=("serve") - commands+=("status") - commands+=("test") - commands+=("upgrade") - commands+=("verify") - commands+=("version") - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -__start_helm() -{ - local cur prev words cword - declare -A flaghash 2>/dev/null || : - if declare -F _init_completion >/dev/null 2>&1; then - _init_completion -s || return - else - __my_init_completion -n "=" || return - fi - - local c=0 - local flags=() - local two_word_flags=() - local local_nonpersistent_flags=() - local flags_with_completion=() - local flags_completion=() - local commands=("helm") - local must_have_one_flag=() - local must_have_one_noun=() - local last_command - local nouns=() - - __handle_word -} - -if [[ $(type -t compopt) = "builtin" ]]; then - complete -o default -F __start_helm helm -else - complete -o default -o nospace -F __start_helm helm -fi - -# ex: ts=4 sw=4 et filetype=sh From f182ebc11c9ea7a5bf75e35d8e061f3ac68482fd Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Fri, 22 May 2020 12:04:37 -0400 Subject: [PATCH 130/281] Fix issue with unhandled error on Stat If stat returns an error other than the directory not existing it was unhandled. When IsDir is called in one of these situations it causes a panic. Closes #8181 Signed-off-by: Matt Farina --- pkg/chartutil/save.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/pkg/chartutil/save.go b/pkg/chartutil/save.go index 1011436b5..2ce4eddaf 100644 --- a/pkg/chartutil/save.go +++ b/pkg/chartutil/save.go @@ -106,12 +106,17 @@ func Save(c *chart.Chart, outDir string) (string, error) { filename := fmt.Sprintf("%s-%s.tgz", c.Name(), c.Metadata.Version) filename = filepath.Join(outDir, filename) - if stat, err := os.Stat(filepath.Dir(filename)); os.IsNotExist(err) { - if err := os.MkdirAll(filepath.Dir(filename), 0755); err != nil { - return "", err + dir := filepath.Dir(filename) + if stat, err := os.Stat(dir); err != nil { + if os.IsNotExist(err) { + if err2 := os.MkdirAll(dir, 0755); err2 != nil { + return "", err2 + } + } else { + return "", errors.Wrapf(err, "stat %s", dir) } } else if !stat.IsDir() { - return "", errors.Errorf("is not a directory: %s", filepath.Dir(filename)) + return "", errors.Errorf("is not a directory: %s", dir) } f, err := os.Create(filename) From 44a2225035d93ab6e8dc40af7c3ee4ff13452fae Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Fri, 22 May 2020 11:39:20 -0700 Subject: [PATCH 131/281] ref(tests): localize unit test fixtures to package Use test fixtures that are in the same package as test. Signed-off-by: Adam Reese --- cmd/helm/template_test.go | 2 +- .../output/template-name-template.txt | 28 +- cmd/helm/testdata/output/template-set.txt | 28 +- .../output/template-show-only-glob.txt | 12 +- .../output/template-show-only-multiple.txt | 10 +- .../output/template-show-only-one.txt | 8 +- .../testdata/output/template-values-files.txt | 28 +- .../output/template-with-api-version.txt | 28 +- .../testdata/output/template-with-crds.txt | 28 +- cmd/helm/testdata/output/template.txt | 28 +- .../chart-with-no-templates-dir/values.yaml | 1 - .../testcharts/decompressedchart/.helmignore | 5 - .../testdata/testcharts/novals/Chart.yaml | 8 - cmd/helm/testdata/testcharts/novals/README.md | 13 - .../novals/templates/alpine-pod.yaml | 28 -- .../testdata/testcharts/subchart/Chart.yaml | 36 +++ .../subchart/charts/subchartA/Chart.yaml | 4 + .../charts/subchartA/templates/service.yaml | 15 ++ .../subchart/charts/subchartA/values.yaml | 17 ++ .../subchart/charts/subchartB/Chart.yaml | 4 + .../charts/subchartB/templates/service.yaml | 15 ++ .../subchart/charts/subchartB/values.yaml | 35 +++ .../testcharts/subchart/crds/crdA.yaml | 13 + .../testcharts/subchart/templates/NOTES.txt | 1 + .../subchart/templates/service.yaml | 22 ++ .../subchart/templates/subdir/role.yaml | 7 + .../templates/subdir/rolebinding.yaml | 12 + .../templates/subdir/serviceaccount.yaml | 4 + .../testdata/testcharts/subchart/values.yaml | 55 ++++ internal/resolver/resolver_test.go | 14 +- .../testdata/chartpath/base/Chart.yaml | 3 + pkg/action/dependency_test.go | 10 +- pkg/action/lint_test.go | 26 +- pkg/action/show.go | 20 +- pkg/action/show_test.go | 61 ++--- .../charts/chart-missing-deps/.helmignore | 5 - .../charts/chart-missing-deps/Chart.yaml | 18 -- .../charts/chart-missing-deps/README.md | 232 ---------------- .../chart-missing-deps/templates/NOTES.txt | 38 --- .../chart-missing-deps/templates/_helpers.tpl | 24 -- .../charts/chart-missing-deps/values.yaml | 254 ------------------ .../.helmignore | 5 - .../Chart.yaml | 18 -- .../README.md | 3 - .../templates/NOTES.txt | 1 - .../values.yaml | 254 ------------------ .../chart-with-no-templates-dir/Chart.yaml | 0 .../chart-with-schema-negative/Chart.yaml | 7 + .../templates/empty.yaml | 1 + .../values.schema.json | 67 +++++ .../chart-with-schema-negative/values.yaml | 14 + .../charts/chart-with-schema/Chart.yaml | 7 + .../chart-with-schema/extra-values.yaml | 2 + .../chart-with-schema/templates/empty.yaml | 1 + .../chart-with-schema/values.schema.json | 67 +++++ .../charts/chart-with-schema/values.yaml | 17 ++ .../charts/compressedchart-0.1.0.tar.gz | Bin 0 -> 477 bytes .../testdata/charts/compressedchart-0.1.0.tgz | Bin 0 -> 477 bytes .../testdata/charts/compressedchart-0.2.0.tgz | Bin 0 -> 477 bytes .../testdata/charts/compressedchart-0.3.0.tgz | Bin 0 -> 477 bytes .../compressedchart-with-hyphens-0.1.0.tgz | Bin 0 -> 548 bytes .../charts}/corrupted-compressed-chart.tgz | 0 .../charts}/decompressedchart/Chart.yaml | 0 .../charts}/decompressedchart/values.yaml | 0 .../multiplecharts-lint-chart-1/Chart.yaml | 0 .../templates/configmap.yaml | 0 .../multiplecharts-lint-chart-1/values.yaml | 0 .../multiplecharts-lint-chart-2/Chart.yaml | 0 .../templates/configmap.yaml | 0 .../multiplecharts-lint-chart-2/values.yaml | 0 .../charts/pre-release-chart-0.1.0-alpha.tgz | Bin 0 -> 355 bytes ...s-tgz.txt => list-compressed-deps-tgz.txt} | 0 ...ssed-deps.txt => list-compressed-deps.txt} | 0 ...missing-deps.txt => list-missing-deps.txt} | 0 ...tgz.txt => list-uncompressed-deps-tgz.txt} | 0 ...ed-deps.txt => list-uncompressed-deps.txt} | 0 76 files changed, 591 insertions(+), 1073 deletions(-) delete mode 100644 cmd/helm/testdata/testcharts/chart-with-no-templates-dir/values.yaml delete mode 100644 cmd/helm/testdata/testcharts/decompressedchart/.helmignore delete mode 100644 cmd/helm/testdata/testcharts/novals/Chart.yaml delete mode 100644 cmd/helm/testdata/testcharts/novals/README.md delete mode 100644 cmd/helm/testdata/testcharts/novals/templates/alpine-pod.yaml create mode 100644 cmd/helm/testdata/testcharts/subchart/Chart.yaml create mode 100644 cmd/helm/testdata/testcharts/subchart/charts/subchartA/Chart.yaml create mode 100644 cmd/helm/testdata/testcharts/subchart/charts/subchartA/templates/service.yaml create mode 100644 cmd/helm/testdata/testcharts/subchart/charts/subchartA/values.yaml create mode 100644 cmd/helm/testdata/testcharts/subchart/charts/subchartB/Chart.yaml create mode 100644 cmd/helm/testdata/testcharts/subchart/charts/subchartB/templates/service.yaml create mode 100644 cmd/helm/testdata/testcharts/subchart/charts/subchartB/values.yaml create mode 100644 cmd/helm/testdata/testcharts/subchart/crds/crdA.yaml create mode 100644 cmd/helm/testdata/testcharts/subchart/templates/NOTES.txt create mode 100644 cmd/helm/testdata/testcharts/subchart/templates/service.yaml create mode 100644 cmd/helm/testdata/testcharts/subchart/templates/subdir/role.yaml create mode 100644 cmd/helm/testdata/testcharts/subchart/templates/subdir/rolebinding.yaml create mode 100644 cmd/helm/testdata/testcharts/subchart/templates/subdir/serviceaccount.yaml create mode 100644 cmd/helm/testdata/testcharts/subchart/values.yaml create mode 100644 internal/resolver/testdata/chartpath/base/Chart.yaml delete mode 100755 pkg/action/testdata/charts/chart-missing-deps/.helmignore delete mode 100755 pkg/action/testdata/charts/chart-missing-deps/README.md delete mode 100755 pkg/action/testdata/charts/chart-missing-deps/templates/NOTES.txt delete mode 100755 pkg/action/testdata/charts/chart-missing-deps/templates/_helpers.tpl delete mode 100755 pkg/action/testdata/charts/chart-missing-deps/values.yaml delete mode 100755 pkg/action/testdata/charts/chart-with-compressed-dependencies/.helmignore delete mode 100755 pkg/action/testdata/charts/chart-with-compressed-dependencies/README.md delete mode 100755 pkg/action/testdata/charts/chart-with-compressed-dependencies/templates/NOTES.txt delete mode 100755 pkg/action/testdata/charts/chart-with-compressed-dependencies/values.yaml rename {cmd/helm/testdata/testcharts => pkg/action/testdata/charts}/chart-with-no-templates-dir/Chart.yaml (100%) create mode 100644 pkg/action/testdata/charts/chart-with-schema-negative/Chart.yaml create mode 100644 pkg/action/testdata/charts/chart-with-schema-negative/templates/empty.yaml create mode 100644 pkg/action/testdata/charts/chart-with-schema-negative/values.schema.json create mode 100644 pkg/action/testdata/charts/chart-with-schema-negative/values.yaml create mode 100644 pkg/action/testdata/charts/chart-with-schema/Chart.yaml create mode 100644 pkg/action/testdata/charts/chart-with-schema/extra-values.yaml create mode 100644 pkg/action/testdata/charts/chart-with-schema/templates/empty.yaml create mode 100644 pkg/action/testdata/charts/chart-with-schema/values.schema.json create mode 100644 pkg/action/testdata/charts/chart-with-schema/values.yaml create mode 100644 pkg/action/testdata/charts/compressedchart-0.1.0.tar.gz create mode 100644 pkg/action/testdata/charts/compressedchart-0.1.0.tgz create mode 100644 pkg/action/testdata/charts/compressedchart-0.2.0.tgz create mode 100644 pkg/action/testdata/charts/compressedchart-0.3.0.tgz create mode 100644 pkg/action/testdata/charts/compressedchart-with-hyphens-0.1.0.tgz rename {cmd/helm/testdata/testcharts => pkg/action/testdata/charts}/corrupted-compressed-chart.tgz (100%) rename {cmd/helm/testdata/testcharts => pkg/action/testdata/charts}/decompressedchart/Chart.yaml (100%) rename {cmd/helm/testdata/testcharts => pkg/action/testdata/charts}/decompressedchart/values.yaml (100%) rename {cmd/helm/testdata/testcharts => pkg/action/testdata/charts}/multiplecharts-lint-chart-1/Chart.yaml (100%) rename {cmd/helm/testdata/testcharts => pkg/action/testdata/charts}/multiplecharts-lint-chart-1/templates/configmap.yaml (100%) rename {cmd/helm/testdata/testcharts => pkg/action/testdata/charts}/multiplecharts-lint-chart-1/values.yaml (100%) rename {cmd/helm/testdata/testcharts => pkg/action/testdata/charts}/multiplecharts-lint-chart-2/Chart.yaml (100%) rename {cmd/helm/testdata/testcharts => pkg/action/testdata/charts}/multiplecharts-lint-chart-2/templates/configmap.yaml (100%) rename {cmd/helm/testdata/testcharts => pkg/action/testdata/charts}/multiplecharts-lint-chart-2/values.yaml (100%) create mode 100644 pkg/action/testdata/charts/pre-release-chart-0.1.0-alpha.tgz rename pkg/action/testdata/output/{compressed-deps-tgz.txt => list-compressed-deps-tgz.txt} (100%) rename pkg/action/testdata/output/{compressed-deps.txt => list-compressed-deps.txt} (100%) rename pkg/action/testdata/output/{missing-deps.txt => list-missing-deps.txt} (100%) rename pkg/action/testdata/output/{uncompressed-deps-tgz.txt => list-uncompressed-deps-tgz.txt} (100%) rename pkg/action/testdata/output/{uncompressed-deps.txt => list-uncompressed-deps.txt} (100%) diff --git a/cmd/helm/template_test.go b/cmd/helm/template_test.go index 87ee79e5a..92dd9825e 100644 --- a/cmd/helm/template_test.go +++ b/cmd/helm/template_test.go @@ -22,7 +22,7 @@ import ( "testing" ) -var chartPath = "./../../pkg/chartutil/testdata/subpop/charts/subchart1" +var chartPath = "testdata/testcharts/subchart" func TestTemplateCmd(t *testing.T) { tests := []cmdTestCase{ diff --git a/cmd/helm/testdata/output/template-name-template.txt b/cmd/helm/testdata/output/template-name-template.txt index 24f2bb616..84a9e565c 100644 --- a/cmd/helm/testdata/output/template-name-template.txt +++ b/cmd/helm/testdata/output/template-name-template.txt @@ -1,34 +1,34 @@ --- -# Source: subchart1/templates/subdir/serviceaccount.yaml +# Source: subchart/templates/subdir/serviceaccount.yaml apiVersion: v1 kind: ServiceAccount metadata: - name: subchart1-sa + name: subchart-sa --- -# Source: subchart1/templates/subdir/role.yaml +# Source: subchart/templates/subdir/role.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: - name: subchart1-role + name: subchart-role rules: - resources: ["*"] verbs: ["get","list","watch"] --- -# Source: subchart1/templates/subdir/rolebinding.yaml +# Source: subchart/templates/subdir/rolebinding.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: - name: subchart1-binding + name: subchart-binding roleRef: apiGroup: rbac.authorization.k8s.io kind: Role - name: subchart1-role + name: subchart-role subjects: - kind: ServiceAccount - name: subchart1-sa + name: subchart-sa namespace: default --- -# Source: subchart1/charts/subcharta/templates/service.yaml +# Source: subchart/charts/subcharta/templates/service.yaml apiVersion: v1 kind: Service metadata: @@ -45,7 +45,7 @@ spec: selector: app.kubernetes.io/name: subcharta --- -# Source: subchart1/charts/subchartb/templates/service.yaml +# Source: subchart/charts/subchartb/templates/service.yaml apiVersion: v1 kind: Service metadata: @@ -62,13 +62,13 @@ spec: selector: app.kubernetes.io/name: subchartb --- -# Source: subchart1/templates/service.yaml +# Source: subchart/templates/service.yaml apiVersion: v1 kind: Service metadata: - name: subchart1 + name: subchart labels: - helm.sh/chart: "subchart1-0.1.0" + helm.sh/chart: "subchart-0.1.0" app.kubernetes.io/instance: "foobar-YWJj-baz" kube-version/major: "1" kube-version/minor: "18" @@ -81,4 +81,4 @@ spec: protocol: TCP name: nginx selector: - app.kubernetes.io/name: subchart1 + app.kubernetes.io/name: subchart diff --git a/cmd/helm/testdata/output/template-set.txt b/cmd/helm/testdata/output/template-set.txt index 4c7f1f626..1cb97723e 100644 --- a/cmd/helm/testdata/output/template-set.txt +++ b/cmd/helm/testdata/output/template-set.txt @@ -1,34 +1,34 @@ --- -# Source: subchart1/templates/subdir/serviceaccount.yaml +# Source: subchart/templates/subdir/serviceaccount.yaml apiVersion: v1 kind: ServiceAccount metadata: - name: subchart1-sa + name: subchart-sa --- -# Source: subchart1/templates/subdir/role.yaml +# Source: subchart/templates/subdir/role.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: - name: subchart1-role + name: subchart-role rules: - resources: ["*"] verbs: ["get","list","watch"] --- -# Source: subchart1/templates/subdir/rolebinding.yaml +# Source: subchart/templates/subdir/rolebinding.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: - name: subchart1-binding + name: subchart-binding roleRef: apiGroup: rbac.authorization.k8s.io kind: Role - name: subchart1-role + name: subchart-role subjects: - kind: ServiceAccount - name: subchart1-sa + name: subchart-sa namespace: default --- -# Source: subchart1/charts/subcharta/templates/service.yaml +# Source: subchart/charts/subcharta/templates/service.yaml apiVersion: v1 kind: Service metadata: @@ -45,7 +45,7 @@ spec: selector: app.kubernetes.io/name: subcharta --- -# Source: subchart1/charts/subchartb/templates/service.yaml +# Source: subchart/charts/subchartb/templates/service.yaml apiVersion: v1 kind: Service metadata: @@ -62,13 +62,13 @@ spec: selector: app.kubernetes.io/name: subchartb --- -# Source: subchart1/templates/service.yaml +# Source: subchart/templates/service.yaml apiVersion: v1 kind: Service metadata: - name: subchart1 + name: subchart labels: - helm.sh/chart: "subchart1-0.1.0" + helm.sh/chart: "subchart-0.1.0" app.kubernetes.io/instance: "RELEASE-NAME" kube-version/major: "1" kube-version/minor: "18" @@ -81,4 +81,4 @@ spec: protocol: TCP name: apache selector: - app.kubernetes.io/name: subchart1 + app.kubernetes.io/name: subchart diff --git a/cmd/helm/testdata/output/template-show-only-glob.txt b/cmd/helm/testdata/output/template-show-only-glob.txt index 0970e6cd3..cc651f596 100644 --- a/cmd/helm/testdata/output/template-show-only-glob.txt +++ b/cmd/helm/testdata/output/template-show-only-glob.txt @@ -1,23 +1,23 @@ --- -# Source: subchart1/templates/subdir/role.yaml +# Source: subchart/templates/subdir/role.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: - name: subchart1-role + name: subchart-role rules: - resources: ["*"] verbs: ["get","list","watch"] --- -# Source: subchart1/templates/subdir/rolebinding.yaml +# Source: subchart/templates/subdir/rolebinding.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: - name: subchart1-binding + name: subchart-binding roleRef: apiGroup: rbac.authorization.k8s.io kind: Role - name: subchart1-role + name: subchart-role subjects: - kind: ServiceAccount - name: subchart1-sa + name: subchart-sa namespace: default diff --git a/cmd/helm/testdata/output/template-show-only-multiple.txt b/cmd/helm/testdata/output/template-show-only-multiple.txt index 75b590e20..1c4b1f29e 100644 --- a/cmd/helm/testdata/output/template-show-only-multiple.txt +++ b/cmd/helm/testdata/output/template-show-only-multiple.txt @@ -1,11 +1,11 @@ --- -# Source: subchart1/templates/service.yaml +# Source: subchart/templates/service.yaml apiVersion: v1 kind: Service metadata: - name: subchart1 + name: subchart labels: - helm.sh/chart: "subchart1-0.1.0" + helm.sh/chart: "subchart-0.1.0" app.kubernetes.io/instance: "RELEASE-NAME" kube-version/major: "1" kube-version/minor: "18" @@ -19,9 +19,9 @@ spec: protocol: TCP name: nginx selector: - app.kubernetes.io/name: subchart1 + app.kubernetes.io/name: subchart --- -# Source: subchart1/charts/subcharta/templates/service.yaml +# Source: subchart/charts/subcharta/templates/service.yaml apiVersion: v1 kind: Service metadata: diff --git a/cmd/helm/testdata/output/template-show-only-one.txt b/cmd/helm/testdata/output/template-show-only-one.txt index ce61f481f..7b1443ea8 100644 --- a/cmd/helm/testdata/output/template-show-only-one.txt +++ b/cmd/helm/testdata/output/template-show-only-one.txt @@ -1,11 +1,11 @@ --- -# Source: subchart1/templates/service.yaml +# Source: subchart/templates/service.yaml apiVersion: v1 kind: Service metadata: - name: subchart1 + name: subchart labels: - helm.sh/chart: "subchart1-0.1.0" + helm.sh/chart: "subchart-0.1.0" app.kubernetes.io/instance: "RELEASE-NAME" kube-version/major: "1" kube-version/minor: "18" @@ -19,4 +19,4 @@ spec: protocol: TCP name: nginx selector: - app.kubernetes.io/name: subchart1 + app.kubernetes.io/name: subchart diff --git a/cmd/helm/testdata/output/template-values-files.txt b/cmd/helm/testdata/output/template-values-files.txt index 4c7f1f626..1cb97723e 100644 --- a/cmd/helm/testdata/output/template-values-files.txt +++ b/cmd/helm/testdata/output/template-values-files.txt @@ -1,34 +1,34 @@ --- -# Source: subchart1/templates/subdir/serviceaccount.yaml +# Source: subchart/templates/subdir/serviceaccount.yaml apiVersion: v1 kind: ServiceAccount metadata: - name: subchart1-sa + name: subchart-sa --- -# Source: subchart1/templates/subdir/role.yaml +# Source: subchart/templates/subdir/role.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: - name: subchart1-role + name: subchart-role rules: - resources: ["*"] verbs: ["get","list","watch"] --- -# Source: subchart1/templates/subdir/rolebinding.yaml +# Source: subchart/templates/subdir/rolebinding.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: - name: subchart1-binding + name: subchart-binding roleRef: apiGroup: rbac.authorization.k8s.io kind: Role - name: subchart1-role + name: subchart-role subjects: - kind: ServiceAccount - name: subchart1-sa + name: subchart-sa namespace: default --- -# Source: subchart1/charts/subcharta/templates/service.yaml +# Source: subchart/charts/subcharta/templates/service.yaml apiVersion: v1 kind: Service metadata: @@ -45,7 +45,7 @@ spec: selector: app.kubernetes.io/name: subcharta --- -# Source: subchart1/charts/subchartb/templates/service.yaml +# Source: subchart/charts/subchartb/templates/service.yaml apiVersion: v1 kind: Service metadata: @@ -62,13 +62,13 @@ spec: selector: app.kubernetes.io/name: subchartb --- -# Source: subchart1/templates/service.yaml +# Source: subchart/templates/service.yaml apiVersion: v1 kind: Service metadata: - name: subchart1 + name: subchart labels: - helm.sh/chart: "subchart1-0.1.0" + helm.sh/chart: "subchart-0.1.0" app.kubernetes.io/instance: "RELEASE-NAME" kube-version/major: "1" kube-version/minor: "18" @@ -81,4 +81,4 @@ spec: protocol: TCP name: apache selector: - app.kubernetes.io/name: subchart1 + app.kubernetes.io/name: subchart diff --git a/cmd/helm/testdata/output/template-with-api-version.txt b/cmd/helm/testdata/output/template-with-api-version.txt index 210dcc503..ea4b5c96b 100644 --- a/cmd/helm/testdata/output/template-with-api-version.txt +++ b/cmd/helm/testdata/output/template-with-api-version.txt @@ -1,34 +1,34 @@ --- -# Source: subchart1/templates/subdir/serviceaccount.yaml +# Source: subchart/templates/subdir/serviceaccount.yaml apiVersion: v1 kind: ServiceAccount metadata: - name: subchart1-sa + name: subchart-sa --- -# Source: subchart1/templates/subdir/role.yaml +# Source: subchart/templates/subdir/role.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: - name: subchart1-role + name: subchart-role rules: - resources: ["*"] verbs: ["get","list","watch"] --- -# Source: subchart1/templates/subdir/rolebinding.yaml +# Source: subchart/templates/subdir/rolebinding.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: - name: subchart1-binding + name: subchart-binding roleRef: apiGroup: rbac.authorization.k8s.io kind: Role - name: subchart1-role + name: subchart-role subjects: - kind: ServiceAccount - name: subchart1-sa + name: subchart-sa namespace: default --- -# Source: subchart1/charts/subcharta/templates/service.yaml +# Source: subchart/charts/subcharta/templates/service.yaml apiVersion: v1 kind: Service metadata: @@ -45,7 +45,7 @@ spec: selector: app.kubernetes.io/name: subcharta --- -# Source: subchart1/charts/subchartb/templates/service.yaml +# Source: subchart/charts/subchartb/templates/service.yaml apiVersion: v1 kind: Service metadata: @@ -62,13 +62,13 @@ spec: selector: app.kubernetes.io/name: subchartb --- -# Source: subchart1/templates/service.yaml +# Source: subchart/templates/service.yaml apiVersion: v1 kind: Service metadata: - name: subchart1 + name: subchart labels: - helm.sh/chart: "subchart1-0.1.0" + helm.sh/chart: "subchart-0.1.0" app.kubernetes.io/instance: "RELEASE-NAME" kube-version/major: "1" kube-version/minor: "18" @@ -82,4 +82,4 @@ spec: protocol: TCP name: nginx selector: - app.kubernetes.io/name: subchart1 + app.kubernetes.io/name: subchart diff --git a/cmd/helm/testdata/output/template-with-crds.txt b/cmd/helm/testdata/output/template-with-crds.txt index 289eec291..fa2a79bac 100644 --- a/cmd/helm/testdata/output/template-with-crds.txt +++ b/cmd/helm/testdata/output/template-with-crds.txt @@ -15,36 +15,36 @@ spec: singular: authconfig --- -# Source: subchart1/templates/subdir/serviceaccount.yaml +# Source: subchart/templates/subdir/serviceaccount.yaml apiVersion: v1 kind: ServiceAccount metadata: - name: subchart1-sa + name: subchart-sa --- -# Source: subchart1/templates/subdir/role.yaml +# Source: subchart/templates/subdir/role.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: - name: subchart1-role + name: subchart-role rules: - resources: ["*"] verbs: ["get","list","watch"] --- -# Source: subchart1/templates/subdir/rolebinding.yaml +# Source: subchart/templates/subdir/rolebinding.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: - name: subchart1-binding + name: subchart-binding roleRef: apiGroup: rbac.authorization.k8s.io kind: Role - name: subchart1-role + name: subchart-role subjects: - kind: ServiceAccount - name: subchart1-sa + name: subchart-sa namespace: default --- -# Source: subchart1/charts/subcharta/templates/service.yaml +# Source: subchart/charts/subcharta/templates/service.yaml apiVersion: v1 kind: Service metadata: @@ -61,7 +61,7 @@ spec: selector: app.kubernetes.io/name: subcharta --- -# Source: subchart1/charts/subchartb/templates/service.yaml +# Source: subchart/charts/subchartb/templates/service.yaml apiVersion: v1 kind: Service metadata: @@ -78,13 +78,13 @@ spec: selector: app.kubernetes.io/name: subchartb --- -# Source: subchart1/templates/service.yaml +# Source: subchart/templates/service.yaml apiVersion: v1 kind: Service metadata: - name: subchart1 + name: subchart labels: - helm.sh/chart: "subchart1-0.1.0" + helm.sh/chart: "subchart-0.1.0" app.kubernetes.io/instance: "RELEASE-NAME" kube-version/major: "1" kube-version/minor: "18" @@ -98,4 +98,4 @@ spec: protocol: TCP name: nginx selector: - app.kubernetes.io/name: subchart1 + app.kubernetes.io/name: subchart diff --git a/cmd/helm/testdata/output/template.txt b/cmd/helm/testdata/output/template.txt index 82a21c5a1..9195f98b7 100644 --- a/cmd/helm/testdata/output/template.txt +++ b/cmd/helm/testdata/output/template.txt @@ -1,34 +1,34 @@ --- -# Source: subchart1/templates/subdir/serviceaccount.yaml +# Source: subchart/templates/subdir/serviceaccount.yaml apiVersion: v1 kind: ServiceAccount metadata: - name: subchart1-sa + name: subchart-sa --- -# Source: subchart1/templates/subdir/role.yaml +# Source: subchart/templates/subdir/role.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: - name: subchart1-role + name: subchart-role rules: - resources: ["*"] verbs: ["get","list","watch"] --- -# Source: subchart1/templates/subdir/rolebinding.yaml +# Source: subchart/templates/subdir/rolebinding.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: - name: subchart1-binding + name: subchart-binding roleRef: apiGroup: rbac.authorization.k8s.io kind: Role - name: subchart1-role + name: subchart-role subjects: - kind: ServiceAccount - name: subchart1-sa + name: subchart-sa namespace: default --- -# Source: subchart1/charts/subcharta/templates/service.yaml +# Source: subchart/charts/subcharta/templates/service.yaml apiVersion: v1 kind: Service metadata: @@ -45,7 +45,7 @@ spec: selector: app.kubernetes.io/name: subcharta --- -# Source: subchart1/charts/subchartb/templates/service.yaml +# Source: subchart/charts/subchartb/templates/service.yaml apiVersion: v1 kind: Service metadata: @@ -62,13 +62,13 @@ spec: selector: app.kubernetes.io/name: subchartb --- -# Source: subchart1/templates/service.yaml +# Source: subchart/templates/service.yaml apiVersion: v1 kind: Service metadata: - name: subchart1 + name: subchart labels: - helm.sh/chart: "subchart1-0.1.0" + helm.sh/chart: "subchart-0.1.0" app.kubernetes.io/instance: "RELEASE-NAME" kube-version/major: "1" kube-version/minor: "18" @@ -81,4 +81,4 @@ spec: protocol: TCP name: nginx selector: - app.kubernetes.io/name: subchart1 + app.kubernetes.io/name: subchart diff --git a/cmd/helm/testdata/testcharts/chart-with-no-templates-dir/values.yaml b/cmd/helm/testdata/testcharts/chart-with-no-templates-dir/values.yaml deleted file mode 100644 index ec82367be..000000000 --- a/cmd/helm/testdata/testcharts/chart-with-no-templates-dir/values.yaml +++ /dev/null @@ -1 +0,0 @@ -justAValue: "an example chart here" diff --git a/cmd/helm/testdata/testcharts/decompressedchart/.helmignore b/cmd/helm/testdata/testcharts/decompressedchart/.helmignore deleted file mode 100644 index 435b756d8..000000000 --- a/cmd/helm/testdata/testcharts/decompressedchart/.helmignore +++ /dev/null @@ -1,5 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -.git diff --git a/cmd/helm/testdata/testcharts/novals/Chart.yaml b/cmd/helm/testdata/testcharts/novals/Chart.yaml deleted file mode 100644 index a4282470b..000000000 --- a/cmd/helm/testdata/testcharts/novals/Chart.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -description: Deploy a basic Alpine Linux pod -home: https://helm.sh/helm -name: novals -sources: - - https://github.com/helm/helm -version: 0.2.0 -appVersion: 3.3 diff --git a/cmd/helm/testdata/testcharts/novals/README.md b/cmd/helm/testdata/testcharts/novals/README.md deleted file mode 100644 index fcf7ee017..000000000 --- a/cmd/helm/testdata/testcharts/novals/README.md +++ /dev/null @@ -1,13 +0,0 @@ -#Alpine: A simple Helm chart - -Run a single pod of Alpine Linux. - -This example was generated using the command `helm create alpine`. - -The `templates/` directory contains a very simple pod resource with a -couple of parameters. - -The `values.yaml` file contains the default values for the -`alpine-pod.yaml` template. - -You can install this example using `helm install ./alpine`. diff --git a/cmd/helm/testdata/testcharts/novals/templates/alpine-pod.yaml b/cmd/helm/testdata/testcharts/novals/templates/alpine-pod.yaml deleted file mode 100644 index 96c92d61d..000000000 --- a/cmd/helm/testdata/testcharts/novals/templates/alpine-pod.yaml +++ /dev/null @@ -1,28 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: "{{.Release.Name}}-{{.Values.Name}}" - labels: - # The "app.kubernetes.io/managed-by" label is used to track which tool - # deployed a given chart. It is useful for admins who want to see what - # releases a particular tool is responsible for. - app.kubernetes.io/managed-by: {{.Release.Service | quote }} - # The "app.kubernetes.io/instance" convention makes it easy to tie a release - # to all of the Kubernetes resources that were created as part of that - # release. - app.kubernetes.io/instance: {{.Release.Name | quote }} - app.kubernetes.io/version: {{ .Chart.AppVersion }} - # This makes it easy to audit chart usage. - helm.sh/chart: "{{.Chart.Name}}-{{.Chart.Version}}" - annotations: - "helm.sh/created": {{.Release.Time.Seconds | quote }} -spec: - # This shows how to use a simple value. This will look for a passed-in value - # called restartPolicy. If it is not found, it will use the default value. - # {{default "Never" .restartPolicy}} is a slightly optimized version of the - # more conventional syntax: {{.restartPolicy | default "Never"}} - restartPolicy: {{default "Never" .Values.restartPolicy}} - containers: - - name: waiter - image: "alpine:3.3" - command: ["/bin/sleep","9000"] diff --git a/cmd/helm/testdata/testcharts/subchart/Chart.yaml b/cmd/helm/testdata/testcharts/subchart/Chart.yaml new file mode 100644 index 000000000..b03ea3cd3 --- /dev/null +++ b/cmd/helm/testdata/testcharts/subchart/Chart.yaml @@ -0,0 +1,36 @@ +apiVersion: v1 +description: A Helm chart for Kubernetes +name: subchart +version: 0.1.0 +dependencies: + - name: subcharta + repository: http://localhost:10191 + version: 0.1.0 + condition: subcharta.enabled + tags: + - front-end + - subcharta + import-values: + - child: SCAdata + parent: imported-chartA + - child: SCAdata + parent: overridden-chartA + - child: SCAdata + parent: imported-chartA-B + + - name: subchartb + repository: http://localhost:10191 + version: 0.1.0 + condition: subchartb.enabled + import-values: + - child: SCBdata + parent: imported-chartB + - child: SCBdata + parent: imported-chartA-B + - child: exports.SCBexported2 + parent: exports.SCBexported2 + - SCBexported1 + + tags: + - front-end + - subchartb diff --git a/cmd/helm/testdata/testcharts/subchart/charts/subchartA/Chart.yaml b/cmd/helm/testdata/testcharts/subchart/charts/subchartA/Chart.yaml new file mode 100644 index 000000000..be3edcefb --- /dev/null +++ b/cmd/helm/testdata/testcharts/subchart/charts/subchartA/Chart.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +description: A Helm chart for Kubernetes +name: subcharta +version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/subchart/charts/subchartA/templates/service.yaml b/cmd/helm/testdata/testcharts/subchart/charts/subchartA/templates/service.yaml new file mode 100644 index 000000000..27501e1e0 --- /dev/null +++ b/cmd/helm/testdata/testcharts/subchart/charts/subchartA/templates/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Chart.Name }} + labels: + helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.externalPort }} + targetPort: {{ .Values.service.internalPort }} + protocol: TCP + name: {{ .Values.service.name }} + selector: + app.kubernetes.io/name: {{ .Chart.Name }} diff --git a/cmd/helm/testdata/testcharts/subchart/charts/subchartA/values.yaml b/cmd/helm/testdata/testcharts/subchart/charts/subchartA/values.yaml new file mode 100644 index 000000000..f0381ae6a --- /dev/null +++ b/cmd/helm/testdata/testcharts/subchart/charts/subchartA/values.yaml @@ -0,0 +1,17 @@ +# Default values for subchart. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. +# subchartA +service: + name: apache + type: ClusterIP + externalPort: 80 + internalPort: 80 +SCAdata: + SCAbool: false + SCAfloat: 3.1 + SCAint: 55 + SCAstring: "jabba" + SCAnested1: + SCAnested2: true + diff --git a/cmd/helm/testdata/testcharts/subchart/charts/subchartB/Chart.yaml b/cmd/helm/testdata/testcharts/subchart/charts/subchartB/Chart.yaml new file mode 100644 index 000000000..c3c6bbaf0 --- /dev/null +++ b/cmd/helm/testdata/testcharts/subchart/charts/subchartB/Chart.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +description: A Helm chart for Kubernetes +name: subchartb +version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/subchart/charts/subchartB/templates/service.yaml b/cmd/helm/testdata/testcharts/subchart/charts/subchartB/templates/service.yaml new file mode 100644 index 000000000..27501e1e0 --- /dev/null +++ b/cmd/helm/testdata/testcharts/subchart/charts/subchartB/templates/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Chart.Name }} + labels: + helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.externalPort }} + targetPort: {{ .Values.service.internalPort }} + protocol: TCP + name: {{ .Values.service.name }} + selector: + app.kubernetes.io/name: {{ .Chart.Name }} diff --git a/cmd/helm/testdata/testcharts/subchart/charts/subchartB/values.yaml b/cmd/helm/testdata/testcharts/subchart/charts/subchartB/values.yaml new file mode 100644 index 000000000..774fdd75c --- /dev/null +++ b/cmd/helm/testdata/testcharts/subchart/charts/subchartB/values.yaml @@ -0,0 +1,35 @@ +# Default values for subchart. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. +service: + name: nginx + type: ClusterIP + externalPort: 80 + internalPort: 80 + +SCBdata: + SCBbool: true + SCBfloat: 7.77 + SCBint: 33 + SCBstring: "boba" + +exports: + SCBexported1: + SCBexported1A: + SCBexported1B: 1965 + + SCBexported2: + SCBexported2A: "blaster" + +global: + kolla: + nova: + api: + all: + port: 8774 + metadata: + all: + port: 8775 + + + diff --git a/cmd/helm/testdata/testcharts/subchart/crds/crdA.yaml b/cmd/helm/testdata/testcharts/subchart/crds/crdA.yaml new file mode 100644 index 000000000..fca77fd4b --- /dev/null +++ b/cmd/helm/testdata/testcharts/subchart/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 diff --git a/cmd/helm/testdata/testcharts/subchart/templates/NOTES.txt b/cmd/helm/testdata/testcharts/subchart/templates/NOTES.txt new file mode 100644 index 000000000..4bdf443f6 --- /dev/null +++ b/cmd/helm/testdata/testcharts/subchart/templates/NOTES.txt @@ -0,0 +1 @@ +Sample notes for {{ .Chart.Name }} \ No newline at end of file diff --git a/cmd/helm/testdata/testcharts/subchart/templates/service.yaml b/cmd/helm/testdata/testcharts/subchart/templates/service.yaml new file mode 100644 index 000000000..fee94dced --- /dev/null +++ b/cmd/helm/testdata/testcharts/subchart/templates/service.yaml @@ -0,0 +1,22 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Chart.Name }} + labels: + helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + app.kubernetes.io/instance: "{{ .Release.Name }}" + kube-version/major: "{{ .Capabilities.KubeVersion.Major }}" + kube-version/minor: "{{ .Capabilities.KubeVersion.Minor }}" + kube-version/version: "v{{ .Capabilities.KubeVersion.Major }}.{{ .Capabilities.KubeVersion.Minor }}.0" +{{- if .Capabilities.APIVersions.Has "helm.k8s.io/test" }} + kube-api-version/test: v1 +{{- end }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.externalPort }} + targetPort: {{ .Values.service.internalPort }} + protocol: TCP + name: {{ .Values.service.name }} + selector: + app.kubernetes.io/name: {{ .Chart.Name }} diff --git a/cmd/helm/testdata/testcharts/subchart/templates/subdir/role.yaml b/cmd/helm/testdata/testcharts/subchart/templates/subdir/role.yaml new file mode 100644 index 000000000..91b954e5f --- /dev/null +++ b/cmd/helm/testdata/testcharts/subchart/templates/subdir/role.yaml @@ -0,0 +1,7 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ .Chart.Name }}-role +rules: +- resources: ["*"] + verbs: ["get","list","watch"] diff --git a/cmd/helm/testdata/testcharts/subchart/templates/subdir/rolebinding.yaml b/cmd/helm/testdata/testcharts/subchart/templates/subdir/rolebinding.yaml new file mode 100644 index 000000000..5d193f1a6 --- /dev/null +++ b/cmd/helm/testdata/testcharts/subchart/templates/subdir/rolebinding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ .Chart.Name }}-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ .Chart.Name }}-role +subjects: +- kind: ServiceAccount + name: {{ .Chart.Name }}-sa + namespace: default diff --git a/cmd/helm/testdata/testcharts/subchart/templates/subdir/serviceaccount.yaml b/cmd/helm/testdata/testcharts/subchart/templates/subdir/serviceaccount.yaml new file mode 100644 index 000000000..7126c7d89 --- /dev/null +++ b/cmd/helm/testdata/testcharts/subchart/templates/subdir/serviceaccount.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ .Chart.Name }}-sa diff --git a/cmd/helm/testdata/testcharts/subchart/values.yaml b/cmd/helm/testdata/testcharts/subchart/values.yaml new file mode 100644 index 000000000..8a3ab6c64 --- /dev/null +++ b/cmd/helm/testdata/testcharts/subchart/values.yaml @@ -0,0 +1,55 @@ +# Default values for subchart. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. +# subchart +service: + name: nginx + type: ClusterIP + externalPort: 80 + internalPort: 80 + + +SC1data: + SC1bool: true + SC1float: 3.14 + SC1int: 100 + SC1string: "dollywood" + SC1extra1: 11 + +imported-chartA: + SC1extra2: 1.337 + +overridden-chartA: + SCAbool: true + SCAfloat: 3.14 + SCAint: 100 + SCAstring: "jabbathehut" + SC1extra3: true + +imported-chartA-B: + SC1extra5: "tiller" + +overridden-chartA-B: + SCAbool: true + SCAfloat: 3.33 + SCAint: 555 + SCAstring: "wormwood" + SCAextra1: 23 + + SCBbool: true + SCBfloat: 0.25 + SCBint: 98 + SCBstring: "murkwood" + SCBextra1: 13 + + SC1extra6: 77 + +SCBexported1A: + SC1extra7: true + +exports: + SC1exported1: + global: + SC1exported2: + all: + SC1exported3: "SC1expstr" diff --git a/internal/resolver/resolver_test.go b/internal/resolver/resolver_test.go index bb3d3e6ac..0b0c3a6f8 100644 --- a/internal/resolver/resolver_test.go +++ b/internal/resolver/resolver_test.go @@ -74,18 +74,18 @@ func TestResolve(t *testing.T) { { name: "repo from valid local path", req: []*chart.Dependency{ - {Name: "signtest", Repository: "file://../../../../cmd/helm/testdata/testcharts/signtest", Version: "0.1.0"}, + {Name: "base", Repository: "file://base", Version: "0.1.0"}, }, expect: &chart.Lock{ Dependencies: []*chart.Dependency{ - {Name: "signtest", Repository: "file://../../../../cmd/helm/testdata/testcharts/signtest", Version: "0.1.0"}, + {Name: "base", Repository: "file://base", Version: "0.1.0"}, }, }, }, { name: "repo from invalid local path", req: []*chart.Dependency{ - {Name: "notexist", Repository: "file://../testdata/notexist", Version: "0.1.0"}, + {Name: "notexist", Repository: "file://testdata/notexist", Version: "0.1.0"}, }, err: true, }, @@ -232,9 +232,9 @@ func TestGetLocalPath(t *testing.T) { }, { name: "relative path", - repo: "file://../../../../cmd/helm/testdata/testcharts/signtest", + repo: "file://../../testdata/chartpath/base", chartpath: "foo/bar", - expect: "../../cmd/helm/testdata/testcharts/signtest", + expect: "testdata/chartpath/base", }, { name: "current directory path", @@ -244,13 +244,13 @@ func TestGetLocalPath(t *testing.T) { }, { name: "invalid local path", - repo: "file://../testdata/notexist", + repo: "file://testdata/notexist", chartpath: "testdata/chartpath", err: true, }, { name: "invalid path under current directory", - repo: "../charts/nonexistentdependency", + repo: "charts/nonexistentdependency", chartpath: "testdata/chartpath/charts", err: true, }, diff --git a/internal/resolver/testdata/chartpath/base/Chart.yaml b/internal/resolver/testdata/chartpath/base/Chart.yaml new file mode 100644 index 000000000..860b09091 --- /dev/null +++ b/internal/resolver/testdata/chartpath/base/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v2 +name: base +version: 0.1.0 diff --git a/pkg/action/dependency_test.go b/pkg/action/dependency_test.go index 158acbfb9..4f3cb69a5 100644 --- a/pkg/action/dependency_test.go +++ b/pkg/action/dependency_test.go @@ -30,23 +30,23 @@ func TestList(t *testing.T) { }{ { chart: "testdata/charts/chart-with-compressed-dependencies", - golden: "output/compressed-deps.txt", + golden: "output/list-compressed-deps.txt", }, { chart: "testdata/charts/chart-with-compressed-dependencies-2.1.8.tgz", - golden: "output/compressed-deps-tgz.txt", + golden: "output/list-compressed-deps-tgz.txt", }, { chart: "testdata/charts/chart-with-uncompressed-dependencies", - golden: "output/uncompressed-deps.txt", + golden: "output/list-uncompressed-deps.txt", }, { chart: "testdata/charts/chart-with-uncompressed-dependencies-2.1.8.tgz", - golden: "output/uncompressed-deps-tgz.txt", + golden: "output/list-uncompressed-deps-tgz.txt", }, { chart: "testdata/charts/chart-missing-deps", - golden: "output/missing-deps.txt", + golden: "output/list-missing-deps.txt", }, } { buf := bytes.Buffer{} diff --git a/pkg/action/lint_test.go b/pkg/action/lint_test.go index 68d3c4bb4..1828461f3 100644 --- a/pkg/action/lint_test.go +++ b/pkg/action/lint_test.go @@ -24,10 +24,10 @@ var ( values = make(map[string]interface{}) namespace = "testNamespace" strict = false - chart1MultipleChartLint = "../../cmd/helm/testdata/testcharts/multiplecharts-lint-chart-1" - chart2MultipleChartLint = "../../cmd/helm/testdata/testcharts/multiplecharts-lint-chart-2" - corruptedTgzChart = "../../cmd/helm/testdata/testcharts/corrupted-compressed-chart.tgz" - chartWithNoTemplatesDir = "../../cmd/helm/testdata/testcharts/chart-with-no-templates-dir" + chart1MultipleChartLint = "testdata/charts/multiplecharts-lint-chart-1" + chart2MultipleChartLint = "testdata/charts/multiplecharts-lint-chart-2" + corruptedTgzChart = "testdata/charts/corrupted-compressed-chart.tgz" + chartWithNoTemplatesDir = "testdata/charts/chart-with-no-templates-dir" ) func TestLintChart(t *testing.T) { @@ -38,41 +38,41 @@ func TestLintChart(t *testing.T) { }{ { name: "decompressed-chart", - chartPath: "../../cmd/helm/testdata/testcharts/decompressedchart/", + chartPath: "testdata/charts/decompressedchart/", }, { name: "archived-chart-path", - chartPath: "../../cmd/helm/testdata/testcharts/compressedchart-0.1.0.tgz", + chartPath: "testdata/charts/compressedchart-0.1.0.tgz", }, { name: "archived-chart-path-with-hyphens", - chartPath: "../../cmd/helm/testdata/testcharts/compressedchart-with-hyphens-0.1.0.tgz", + chartPath: "testdata/charts/compressedchart-with-hyphens-0.1.0.tgz", }, { name: "archived-tar-gz-chart-path", - chartPath: "../../cmd/helm/testdata/testcharts/compressedchart-0.1.0.tar.gz", + chartPath: "testdata/charts/compressedchart-0.1.0.tar.gz", }, { name: "invalid-archived-chart-path", - chartPath: "../../cmd/helm/testdata/testcharts/invalidcompressedchart0.1.0.tgz", + chartPath: "testdata/charts/invalidcompressedchart0.1.0.tgz", err: true, }, { name: "chart-missing-manifest", - chartPath: "../../cmd/helm/testdata/testcharts/chart-missing-manifest", + chartPath: "testdata/charts/chart-missing-manifest", err: true, }, { name: "chart-with-schema", - chartPath: "../../cmd/helm/testdata/testcharts/chart-with-schema", + chartPath: "testdata/charts/chart-with-schema", }, { name: "chart-with-schema-negative", - chartPath: "../../cmd/helm/testdata/testcharts/chart-with-schema-negative", + chartPath: "testdata/charts/chart-with-schema-negative", }, { name: "pre-release-chart", - chartPath: "../../cmd/helm/testdata/testcharts/pre-release-chart-0.1.0-alpha.tgz", + chartPath: "testdata/charts/pre-release-chart-0.1.0-alpha.tgz", }, } diff --git a/pkg/action/show.go b/pkg/action/show.go index cc85477cd..9baa9cf43 100644 --- a/pkg/action/show.go +++ b/pkg/action/show.go @@ -54,6 +54,7 @@ type Show struct { ChartPathOptions Devel bool OutputFormat ShowOutputFormat + chart *chart.Chart // for testing } // NewShow creates a new Show object with the given configuration. @@ -65,25 +66,28 @@ func NewShow(output ShowOutputFormat) *Show { // Run executes 'helm show' against the given release. func (s *Show) Run(chartpath string) (string, error) { - var out strings.Builder - chrt, err := loader.Load(chartpath) - if err != nil { - return "", err + if s.chart == nil { + chrt, err := loader.Load(chartpath) + if err != nil { + return "", err + } + s.chart = chrt } - cf, err := yaml.Marshal(chrt.Metadata) + cf, err := yaml.Marshal(s.chart.Metadata) if err != nil { return "", err } + var out strings.Builder if s.OutputFormat == ShowChart || s.OutputFormat == ShowAll { fmt.Fprintf(&out, "%s\n", cf) } - if (s.OutputFormat == ShowValues || s.OutputFormat == ShowAll) && chrt.Values != nil { + if (s.OutputFormat == ShowValues || s.OutputFormat == ShowAll) && s.chart.Values != nil { if s.OutputFormat == ShowAll { fmt.Fprintln(&out, "---") } - for _, f := range chrt.Raw { + for _, f := range s.chart.Raw { if f.Name == chartutil.ValuesfileName { fmt.Fprintln(&out, string(f.Data)) } @@ -94,7 +98,7 @@ func (s *Show) Run(chartpath string) (string, error) { if s.OutputFormat == ShowAll { fmt.Fprintln(&out, "---") } - readme := findReadme(chrt.Files) + readme := findReadme(s.chart.Files) if readme == nil { return out.String(), nil } diff --git a/pkg/action/show_test.go b/pkg/action/show_test.go index 0a532746a..7be9daaa5 100644 --- a/pkg/action/show_test.go +++ b/pkg/action/show_test.go @@ -17,55 +17,50 @@ limitations under the License. package action import ( - "io/ioutil" - "strings" "testing" + + "helm.sh/helm/v3/pkg/chart" ) func TestShow(t *testing.T) { client := NewShow(ShowAll) - - output, err := client.Run("../../cmd/helm/testdata/testcharts/alpine") - if err != nil { - t.Fatal(err) + client.chart = &chart.Chart{ + Metadata: &chart.Metadata{Name: "alpine"}, + Files: []*chart.File{ + {Name: "README.md", Data: []byte("README\n")}, + }, + Raw: []*chart.File{ + {Name: "values.yaml", Data: []byte("VALUES\n")}, + }, + Values: map[string]interface{}{}, } - // Load the data from the textfixture directly. - cdata, err := ioutil.ReadFile("../../cmd/helm/testdata/testcharts/alpine/Chart.yaml") + output, err := client.Run("") if err != nil { t.Fatal(err) } - data, err := ioutil.ReadFile("../../cmd/helm/testdata/testcharts/alpine/values.yaml") - if err != nil { - t.Fatal(err) - } - readmeData, err := ioutil.ReadFile("../../cmd/helm/testdata/testcharts/alpine/README.md") - if err != nil { - t.Fatal(err) - } - parts := strings.SplitN(output, "---", 3) - if len(parts) != 3 { - t.Fatalf("Expected 2 parts, got %d", len(parts)) - } - expect := []string{ - strings.ReplaceAll(strings.TrimSpace(string(cdata)), "\r", ""), - strings.ReplaceAll(strings.TrimSpace(string(data)), "\r", ""), - strings.ReplaceAll(strings.TrimSpace(string(readmeData)), "\r", ""), - } + expect := `name: alpine + +--- +VALUES - // Problem: ghodss/yaml doesn't marshal into struct order. To solve, we - // have to carefully craft the Chart.yaml to match. - for i, got := range parts { - got = strings.ReplaceAll(strings.TrimSpace(got), "\r", "") - if got != expect[i] { - t.Errorf("Expected\n%q\nGot\n%q\n", expect[i], got) - } +--- +README + +` + if output != expect { + t.Errorf("Expected\n%q\nGot\n%q\n", expect, output) } +} + +func TestShowNoValues(t *testing.T) { + client := NewShow(ShowAll) + client.chart = new(chart.Chart) // Regression tests for missing values. See issue #1024. client.OutputFormat = ShowValues - output, err = client.Run("../../cmd/helm/testdata/testcharts/novals") + output, err := client.Run("") if err != nil { t.Fatal(err) } diff --git a/pkg/action/testdata/charts/chart-missing-deps/.helmignore b/pkg/action/testdata/charts/chart-missing-deps/.helmignore deleted file mode 100755 index e2cf7941f..000000000 --- a/pkg/action/testdata/charts/chart-missing-deps/.helmignore +++ /dev/null @@ -1,5 +0,0 @@ -.git -# OWNERS file for Kubernetes -OWNERS -# example production yaml -values-production.yaml \ No newline at end of file diff --git a/pkg/action/testdata/charts/chart-missing-deps/Chart.yaml b/pkg/action/testdata/charts/chart-missing-deps/Chart.yaml index 8304984fd..ba10ee803 100755 --- a/pkg/action/testdata/charts/chart-missing-deps/Chart.yaml +++ b/pkg/action/testdata/charts/chart-missing-deps/Chart.yaml @@ -1,20 +1,2 @@ -appVersion: 4.9.8 -description: Web publishing platform for building blogs and websites. -engine: gotpl -home: http://www.wordpress.com/ -icon: https://bitnami.com/assets/stacks/wordpress/img/wordpress-stack-220x234.png -keywords: -- wordpress -- cms -- blog -- http -- web -- application -- php -maintainers: -- email: containers@bitnami.com - name: bitnami-bot name: chart-with-missing-deps -sources: -- https://github.com/bitnami/bitnami-docker-wordpress version: 2.1.8 diff --git a/pkg/action/testdata/charts/chart-missing-deps/README.md b/pkg/action/testdata/charts/chart-missing-deps/README.md deleted file mode 100755 index 5859a17fa..000000000 --- a/pkg/action/testdata/charts/chart-missing-deps/README.md +++ /dev/null @@ -1,232 +0,0 @@ -# WordPress - -[WordPress](https://wordpress.org/) is one of the most versatile open source content management systems on the market. A publishing platform for building blogs and websites. - -## TL;DR; - -```console -$ helm install stable/wordpress -``` - -## Introduction - -This chart bootstraps a [WordPress](https://github.com/bitnami/bitnami-docker-wordpress) deployment on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. - -It also packages the [Bitnami MariaDB chart](https://github.com/kubernetes/charts/tree/master/stable/mariadb) which is required for bootstrapping a MariaDB deployment for the database requirements of the WordPress application. - -## Prerequisites - -- Kubernetes 1.4+ with Beta APIs enabled -- PV provisioner support in the underlying infrastructure - -## Installing the Chart - -To install the chart with the release name `my-release`: - -```console -$ helm install --name my-release stable/wordpress -``` - -The command deploys WordPress on the Kubernetes cluster in the default configuration. The [configuration](#configuration) section lists the parameters that can be configured during installation. - -> **Tip**: List all releases using `helm list` - -## Uninstalling the Chart - -To uninstall/delete the `my-release` deployment: - -```console -$ helm delete my-release -``` - -The command removes all the Kubernetes components associated with the chart and deletes the release. - -## Configuration - -The following table lists the configurable parameters of the WordPress chart and their default values. - -| Parameter | Description | Default | -|----------------------------------|--------------------------------------------|---------------------------------------------------------| -| `image.registry` | WordPress image registry | `docker.io` | -| `image.repository` | WordPress image name | `bitnami/wordpress` | -| `image.tag` | WordPress image tag | `{VERSION}` | -| `image.pullPolicy` | Image pull policy | `Always` if `imageTag` is `latest`, else `IfNotPresent` | -| `image.pullSecrets` | Specify image pull secrets | `nil` | -| `wordpressUsername` | User of the application | `user` | -| `wordpressPassword` | Application password | _random 10 character long alphanumeric string_ | -| `wordpressEmail` | Admin email | `user@example.com` | -| `wordpressFirstName` | First name | `FirstName` | -| `wordpressLastName` | Last name | `LastName` | -| `wordpressBlogName` | Blog name | `User's Blog!` | -| `wordpressTablePrefix` | Table prefix | `wp_` | -| `allowEmptyPassword` | Allow DB blank passwords | `yes` | -| `smtpHost` | SMTP host | `nil` | -| `smtpPort` | SMTP port | `nil` | -| `smtpUser` | SMTP user | `nil` | -| `smtpPassword` | SMTP password | `nil` | -| `smtpUsername` | User name for SMTP emails | `nil` | -| `smtpProtocol` | SMTP protocol [`tls`, `ssl`] | `nil` | -| `replicaCount` | Number of WordPress Pods to run | `1` | -| `mariadb.enabled` | Deploy MariaDB container(s) | `true` | -| `mariadb.rootUser.password` | MariaDB admin password | `nil` | -| `mariadb.db.name` | Database name to create | `bitnami_wordpress` | -| `mariadb.db.user` | Database user to create | `bn_wordpress` | -| `mariadb.db.password` | Password for the database | _random 10 character long alphanumeric string_ | -| `externalDatabase.host` | Host of the external database | `localhost` | -| `externalDatabase.user` | Existing username in the external db | `bn_wordpress` | -| `externalDatabase.password` | Password for the above username | `nil` | -| `externalDatabase.database` | Name of the existing database | `bitnami_wordpress` | -| `externalDatabase.port` | Database port number | `3306` | -| `serviceType` | Kubernetes Service type | `LoadBalancer` | -| `serviceExternalTrafficPolicy` | Enable client source IP preservation | `Cluster` | -| `nodePorts.http` | Kubernetes http node port | `""` | -| `nodePorts.https` | Kubernetes https node port | `""` | -| `healthcheckHttps` | Use https for liveliness and readiness | `false` | -| `ingress.enabled` | Enable ingress controller resource | `false` | -| `ingress.hosts[0].name` | Hostname to your WordPress installation | `wordpress.local` | -| `ingress.hosts[0].path` | Path within the url structure | `/` | -| `ingress.hosts[0].tls` | Utilize TLS backend in ingress | `false` | -| `ingress.hosts[0].tlsSecret` | TLS Secret (certificates) | `wordpress.local-tls-secret` | -| `ingress.hosts[0].annotations` | Annotations for this host's ingress record | `[]` | -| `ingress.secrets[0].name` | TLS Secret Name | `nil` | -| `ingress.secrets[0].certificate` | TLS Secret Certificate | `nil` | -| `ingress.secrets[0].key` | TLS Secret Key | `nil` | -| `persistence.enabled` | Enable persistence using PVC | `true` | -| `persistence.existingClaim` | Enable persistence using an existing PVC | `nil` | -| `persistence.storageClass` | PVC Storage Class | `nil` (uses alpha storage class annotation) | -| `persistence.accessMode` | PVC Access Mode | `ReadWriteOnce` | -| `persistence.size` | PVC Storage Request | `10Gi` | -| `nodeSelector` | Node labels for pod assignment | `{}` | -| `tolerations` | List of node taints to tolerate | `[]` | -| `affinity` | Map of node/pod affinities | `{}` | - -The above parameters map to the env variables defined in [bitnami/wordpress](http://github.com/bitnami/bitnami-docker-wordpress). For more information please refer to the [bitnami/wordpress](http://github.com/bitnami/bitnami-docker-wordpress) image documentation. - -Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example, - -```console -$ helm install --name my-release \ - --set wordpressUsername=admin,wordpressPassword=password,mariadb.mariadbRootPassword=secretpassword \ - stable/wordpress -``` - -The above command sets the WordPress administrator account username and password to `admin` and `password` respectively. Additionally, it sets the MariaDB `root` user password to `secretpassword`. - -Alternatively, a YAML file that specifies the values for the above parameters can be provided while installing the chart. For example, - -```console -$ helm install --name my-release -f values.yaml stable/wordpress -``` - -> **Tip**: You can use the default [values.yaml](values.yaml) - -## Production and horizontal scaling - -The following repo contains the recommended production settings for wordpress capture in an alternative [values file](values-production.yaml). Please read carefully the comments in the values-production.yaml file to set up your environment appropriately. - -To horizontally scale this chart, first download the [values-production.yaml](values-production.yaml) file to your local folder, then: - -```console -$ helm install --name my-release -f ./values-production.yaml stable/wordpress -``` - -Note that [values-production.yaml](values-production.yaml) includes a replicaCount of 3, so there will be 3 WordPress pods. As a result, to use the /admin portal and to ensure you can scale wordpress you need to provide a ReadWriteMany PVC, if you don't have a provisioner for this type of storage, we recommend that you install the nfs provisioner and map it to a RWO volume. - -```console -$ helm install stable/nfs-server-provisioner --set persistence.enabled=true,persistence.size=10Gi -$ helm install --name my-release -f values-production.yaml --set persistence.storageClass=nfs stable/wordpress -``` - -## Persistence - -The [Bitnami WordPress](https://github.com/bitnami/bitnami-docker-wordpress) image stores the WordPress data and configurations at the `/bitnami` path of the container. - -Persistent Volume Claims are used to keep the data across deployments. This is known to work in GCE, AWS, and minikube. -See the [Configuration](#configuration) section to configure the PVC or to disable persistence. - -## Using an external database - -Sometimes you may want to have Wordpress connect to an external database rather than installing one inside your cluster, e.g. to use a managed database service, or use run a single database server for all your applications. To do this, the chart allows you to specify credentials for an external database under the [`externalDatabase` parameter](#configuration). You should also disable the MariaDB installation with the `mariadb.enabled` option. For example: - -```console -$ helm install stable/wordpress \ - --set mariadb.enabled=false,externalDatabase.host=myexternalhost,externalDatabase.user=myuser,externalDatabase.password=mypassword,externalDatabase.database=mydatabase,externalDatabase.port=3306 -``` - -Note also if you disable MariaDB per above you MUST supply values for the `externalDatabase` connection. - -## Ingress - -This chart provides support for ingress resources. If you have an -ingress controller installed on your cluster, such as [nginx-ingress](https://kubeapps.com/charts/stable/nginx-ingress) -or [traefik](https://kubeapps.com/charts/stable/traefik) you can utilize -the ingress controller to serve your WordPress application. - -To enable ingress integration, please set `ingress.enabled` to `true` - -### Hosts - -Most likely you will only want to have one hostname that maps to this -WordPress installation, however, it is possible to have more than one -host. To facilitate this, the `ingress.hosts` object is an array. - -For each item, please indicate a `name`, `tls`, `tlsSecret`, and any -`annotations` that you may want the ingress controller to know about. - -Indicating TLS will cause WordPress to generate HTTPS URLs, and -WordPress will be connected to at port 443. The actual secret that -`tlsSecret` references do not have to be generated by this chart. -However, please note that if TLS is enabled, the ingress record will not -work until this secret exists. - -For annotations, please see [this document](https://github.com/kubernetes/ingress-nginx/blob/master/docs/annotations.md). -Not all annotations are supported by all ingress controllers, but this -document does a good job of indicating which annotation is supported by -many popular ingress controllers. - -### TLS Secrets - -This chart will facilitate the creation of TLS secrets for use with the -ingress controller, however, this is not required. There are three -common use cases: - -* helm generates/manages certificate secrets -* user generates/manages certificates separately -* an additional tool (like [kube-lego](https://kubeapps.com/charts/stable/kube-lego)) -manages the secrets for the application - -In the first two cases, one will need a certificate and a key. We would -expect them to look like this: - -* certificate files should look like (and there can be more than one -certificate if there is a certificate chain) - -``` ------BEGIN CERTIFICATE----- -MIID6TCCAtGgAwIBAgIJAIaCwivkeB5EMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV -... -jScrvkiBO65F46KioCL9h5tDvomdU1aqpI/CBzhvZn1c0ZTf87tGQR8NK7v7 ------END CERTIFICATE----- -``` -* keys should look like: -``` ------BEGIN RSA PRIVATE KEY----- -MIIEogIBAAKCAQEAvLYcyu8f3skuRyUgeeNpeDvYBCDcgq+LsWap6zbX5f8oLqp4 -... -wrj2wDbCDCFmfqnSJ+dKI3vFLlEz44sAV8jX/kd4Y6ZTQhlLbYc= ------END RSA PRIVATE KEY----- -```` - -If you are going to use Helm to manage the certificates, please copy -these values into the `certificate` and `key` values for a given -`ingress.secrets` entry. - -If you are going are going to manage TLS secrets outside of Helm, please -know that you can create a TLS secret by doing the following: - -``` -kubectl create secret tls wordpress.local-tls --key /path/to/key.key --cert /path/to/cert.crt -``` - -Please see [this example](https://github.com/kubernetes/contrib/tree/master/ingress/controllers/nginx/examples/tls) -for more information. diff --git a/pkg/action/testdata/charts/chart-missing-deps/templates/NOTES.txt b/pkg/action/testdata/charts/chart-missing-deps/templates/NOTES.txt deleted file mode 100755 index 55626e4d1..000000000 --- a/pkg/action/testdata/charts/chart-missing-deps/templates/NOTES.txt +++ /dev/null @@ -1,38 +0,0 @@ -1. Get the WordPress URL: - -{{- if .Values.ingress.enabled }} - - You should be able to access your new WordPress installation through - - {{- range .Values.ingress.hosts }} - {{ if .tls }}https{{ else }}http{{ end }}://{{ .name }}/admin - {{- end }} - -{{- else if contains "LoadBalancer" .Values.serviceType }} - - NOTE: It may take a few minutes for the LoadBalancer IP to be available. - Watch the status with: 'kubectl get svc --namespace {{ .Release.Namespace }} -w {{ template "fullname" . }}' - - export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') - echo "WordPress URL: http://$SERVICE_IP/" - echo "WordPress Admin URL: http://$SERVICE_IP/admin" - -{{- else if contains "ClusterIP" .Values.serviceType }} - - echo "WordPress URL: http://127.0.0.1:8080/" - echo "WordPress Admin URL: http://127.0.0.1:8080/admin" - kubectl port-forward --namespace {{ .Release.Namespace }} svc/{{ template "fullname" . }} 8080:80 - -{{- else if contains "NodePort" .Values.serviceType }} - - export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "fullname" . }}) - export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") - echo "WordPress URL: http://$NODE_IP:$NODE_PORT/" - echo "WordPress Admin URL: http://$NODE_IP:$NODE_PORT/admin" - -{{- end }} - -2. Login with the following credentials to see your blog - - echo Username: {{ .Values.wordpressUsername }} - echo Password: $(kubectl get secret --namespace {{ .Release.Namespace }} {{ template "fullname" . }} -o jsonpath="{.data.wordpress-password}" | base64 --decode) diff --git a/pkg/action/testdata/charts/chart-missing-deps/templates/_helpers.tpl b/pkg/action/testdata/charts/chart-missing-deps/templates/_helpers.tpl deleted file mode 100755 index 1e52d321c..000000000 --- a/pkg/action/testdata/charts/chart-missing-deps/templates/_helpers.tpl +++ /dev/null @@ -1,24 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Expand the name of the chart. -*/}} -{{- define "name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -*/}} -{{- define "fullname" -}} -{{- $name := default .Chart.Name .Values.nameOverride -}} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -*/}} -{{- define "mariadb.fullname" -}} -{{- printf "%s-%s" .Release.Name "mariadb" | trunc 63 | trimSuffix "-" -}} -{{- end -}} diff --git a/pkg/action/testdata/charts/chart-missing-deps/values.yaml b/pkg/action/testdata/charts/chart-missing-deps/values.yaml deleted file mode 100755 index 3cb66dafd..000000000 --- a/pkg/action/testdata/charts/chart-missing-deps/values.yaml +++ /dev/null @@ -1,254 +0,0 @@ -## Bitnami WordPress image version -## ref: https://hub.docker.com/r/bitnami/wordpress/tags/ -## -image: - registry: docker.io - repository: bitnami/wordpress - tag: 4.9.8-debian-9 - ## Specify a imagePullPolicy - ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' - ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images - ## - pullPolicy: IfNotPresent - ## Optionally specify an array of imagePullSecrets. - ## Secrets must be manually created in the namespace. - ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ - ## - # pullSecrets: - # - myRegistrKeySecretName - -## User of the application -## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables -## -wordpressUsername: user - -## Application password -## Defaults to a random 10-character alphanumeric string if not set -## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables -## -# wordpressPassword: - -## Admin email -## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables -## -wordpressEmail: user@example.com - -## First name -## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables -## -wordpressFirstName: FirstName - -## Last name -## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables -## -wordpressLastName: LastName - -## Blog name -## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables -## -wordpressBlogName: User's Blog! - -## Table prefix -## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables -## -wordpressTablePrefix: wp_ - -## Set to `yes` to allow the container to be started with blank passwords -## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables -allowEmptyPassword: yes - -## SMTP mail delivery configuration -## ref: https://github.com/bitnami/bitnami-docker-wordpress/#smtp-configuration -## -# smtpHost: -# smtpPort: -# smtpUser: -# smtpPassword: -# smtpUsername: -# smtpProtocol: - -replicaCount: 1 - -externalDatabase: -## All of these values are only used when mariadb.enabled is set to false - ## Database host - host: localhost - - ## non-root Username for Wordpress Database - user: bn_wordpress - - ## Database password - password: "" - - ## Database name - database: bitnami_wordpress - - ## Database port number - port: 3306 - -## -## MariaDB chart configuration -## -mariadb: - ## Whether to deploy a mariadb server to satisfy the applications database requirements. To use an external database set this to false and configure the externalDatabase parameters - enabled: true - ## Disable MariaDB replication - replication: - enabled: false - - ## Create a database and a database user - ## ref: https://github.com/bitnami/bitnami-docker-mariadb/blob/master/README.md#creating-a-database-user-on-first-run - ## - db: - name: bitnami_wordpress - user: bn_wordpress - ## If the password is not specified, mariadb will generates a random password - ## - # password: - - ## MariaDB admin password - ## ref: https://github.com/bitnami/bitnami-docker-mariadb/blob/master/README.md#setting-the-root-password-on-first-run - ## - # rootUser: - # password: - - ## Enable persistence using Persistent Volume Claims - ## ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ - ## - master: - persistence: - enabled: true - ## mariadb data Persistent Volume Storage Class - ## If defined, storageClassName: - ## If set to "-", storageClassName: "", which disables dynamic provisioning - ## If undefined (the default) or set to null, no storageClassName spec is - ## set, choosing the default provisioner. (gp2 on AWS, standard on - ## GKE, AWS & OpenStack) - ## - # storageClass: "-" - accessMode: ReadWriteOnce - size: 8Gi - -## Kubernetes configuration -## For minikube, set this to NodePort, elsewhere use LoadBalancer or ClusterIP -## -serviceType: LoadBalancer -## -## serviceType: NodePort -## nodePorts: -## http: -## https: -nodePorts: - http: "" - https: "" -## Enable client source IP preservation -## ref http://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip -## -serviceExternalTrafficPolicy: Cluster - -## Allow health checks to be pointed at the https port -healthcheckHttps: false - -## Configure extra options for liveness and readiness probes -## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes) -livenessProbe: - initialDelaySeconds: 120 - periodSeconds: 10 - timeoutSeconds: 5 - failureThreshold: 6 - successThreshold: 1 -readinessProbe: - initialDelaySeconds: 30 - periodSeconds: 10 - timeoutSeconds: 5 - failureThreshold: 6 - successThreshold: 1 - -## Configure the ingress resource that allows you to access the -## Wordpress installation. Set up the URL -## ref: http://kubernetes.io/docs/user-guide/ingress/ -## -ingress: - ## Set to true to enable ingress record generation - enabled: false - - ## The list of hostnames to be covered with this ingress record. - ## Most likely this will be just one host, but in the event more hosts are needed, this is an array - hosts: - - name: wordpress.local - - ## Set this to true in order to enable TLS on the ingress record - ## A side effect of this will be that the backend wordpress service will be connected at port 443 - tls: false - - ## If TLS is set to true, you must declare what secret will store the key/certificate for TLS - tlsSecret: wordpress.local-tls - - ## Ingress annotations done as key:value pairs - ## If you're using kube-lego, you will want to add: - ## kubernetes.io/tls-acme: true - ## - ## For a full list of possible ingress annotations, please see - ## ref: https://github.com/kubernetes/ingress-nginx/blob/master/docs/annotations.md - ## - ## If tls is set to true, annotation ingress.kubernetes.io/secure-backends: "true" will automatically be set - annotations: - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: true - - secrets: - ## If you're providing your own certificates, please use this to add the certificates as secrets - ## key and certificate should start with -----BEGIN CERTIFICATE----- or - ## -----BEGIN RSA PRIVATE KEY----- - ## - ## name should line up with a tlsSecret set further up - ## If you're using kube-lego, this is unneeded, as it will create the secret for you if it is not set - ## - ## It is also possible to create and manage the certificates outside of this helm chart - ## Please see README.md for more information - # - name: wordpress.local-tls - # key: - # certificate: - -## Enable persistence using Persistent Volume Claims -## ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ -## -persistence: - enabled: true - ## wordpress data Persistent Volume Storage Class - ## If defined, storageClassName: - ## If set to "-", storageClassName: "", which disables dynamic provisioning - ## If undefined (the default) or set to null, no storageClassName spec is - ## set, choosing the default provisioner. (gp2 on AWS, standard on - ## GKE, AWS & OpenStack) - ## - # storageClass: "-" - ## - ## If you want to reuse an existing claim, you can pass the name of the PVC using - ## the existingClaim variable - # existingClaim: your-claim - accessMode: ReadWriteOnce - size: 10Gi - -## Configure resource requests and limits -## ref: http://kubernetes.io/docs/user-guide/compute-resources/ -## -resources: - requests: - memory: 512Mi - cpu: 300m - -## Node labels for pod assignment -## Ref: https://kubernetes.io/docs/user-guide/node-selection/ -## -nodeSelector: {} - -## Tolerations for pod assignment -## Ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ -## -tolerations: [] - -## Affinity for pod assignment -## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity -## -affinity: {} diff --git a/pkg/action/testdata/charts/chart-with-compressed-dependencies/.helmignore b/pkg/action/testdata/charts/chart-with-compressed-dependencies/.helmignore deleted file mode 100755 index e2cf7941f..000000000 --- a/pkg/action/testdata/charts/chart-with-compressed-dependencies/.helmignore +++ /dev/null @@ -1,5 +0,0 @@ -.git -# OWNERS file for Kubernetes -OWNERS -# example production yaml -values-production.yaml \ No newline at end of file diff --git a/pkg/action/testdata/charts/chart-with-compressed-dependencies/Chart.yaml b/pkg/action/testdata/charts/chart-with-compressed-dependencies/Chart.yaml index 602644caa..1d16590b6 100755 --- a/pkg/action/testdata/charts/chart-with-compressed-dependencies/Chart.yaml +++ b/pkg/action/testdata/charts/chart-with-compressed-dependencies/Chart.yaml @@ -1,20 +1,2 @@ -appVersion: 4.9.8 -description: Web publishing platform for building blogs and websites. -engine: gotpl -home: http://www.wordpress.com/ -icon: https://bitnami.com/assets/stacks/wordpress/img/wordpress-stack-220x234.png -keywords: -- wordpress -- cms -- blog -- http -- web -- application -- php -maintainers: -- email: containers@bitnami.com - name: bitnami-bot name: chart-with-compressed-dependencies -sources: -- https://github.com/bitnami/bitnami-docker-wordpress version: 2.1.8 diff --git a/pkg/action/testdata/charts/chart-with-compressed-dependencies/README.md b/pkg/action/testdata/charts/chart-with-compressed-dependencies/README.md deleted file mode 100755 index 3174417e0..000000000 --- a/pkg/action/testdata/charts/chart-with-compressed-dependencies/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# WordPress - -This is a testing fork of the Wordpress chart. It is not operational. diff --git a/pkg/action/testdata/charts/chart-with-compressed-dependencies/templates/NOTES.txt b/pkg/action/testdata/charts/chart-with-compressed-dependencies/templates/NOTES.txt deleted file mode 100755 index 3b94f9157..000000000 --- a/pkg/action/testdata/charts/chart-with-compressed-dependencies/templates/NOTES.txt +++ /dev/null @@ -1 +0,0 @@ -Placeholder diff --git a/pkg/action/testdata/charts/chart-with-compressed-dependencies/values.yaml b/pkg/action/testdata/charts/chart-with-compressed-dependencies/values.yaml deleted file mode 100755 index 3cb66dafd..000000000 --- a/pkg/action/testdata/charts/chart-with-compressed-dependencies/values.yaml +++ /dev/null @@ -1,254 +0,0 @@ -## Bitnami WordPress image version -## ref: https://hub.docker.com/r/bitnami/wordpress/tags/ -## -image: - registry: docker.io - repository: bitnami/wordpress - tag: 4.9.8-debian-9 - ## Specify a imagePullPolicy - ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' - ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images - ## - pullPolicy: IfNotPresent - ## Optionally specify an array of imagePullSecrets. - ## Secrets must be manually created in the namespace. - ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ - ## - # pullSecrets: - # - myRegistrKeySecretName - -## User of the application -## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables -## -wordpressUsername: user - -## Application password -## Defaults to a random 10-character alphanumeric string if not set -## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables -## -# wordpressPassword: - -## Admin email -## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables -## -wordpressEmail: user@example.com - -## First name -## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables -## -wordpressFirstName: FirstName - -## Last name -## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables -## -wordpressLastName: LastName - -## Blog name -## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables -## -wordpressBlogName: User's Blog! - -## Table prefix -## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables -## -wordpressTablePrefix: wp_ - -## Set to `yes` to allow the container to be started with blank passwords -## ref: https://github.com/bitnami/bitnami-docker-wordpress#environment-variables -allowEmptyPassword: yes - -## SMTP mail delivery configuration -## ref: https://github.com/bitnami/bitnami-docker-wordpress/#smtp-configuration -## -# smtpHost: -# smtpPort: -# smtpUser: -# smtpPassword: -# smtpUsername: -# smtpProtocol: - -replicaCount: 1 - -externalDatabase: -## All of these values are only used when mariadb.enabled is set to false - ## Database host - host: localhost - - ## non-root Username for Wordpress Database - user: bn_wordpress - - ## Database password - password: "" - - ## Database name - database: bitnami_wordpress - - ## Database port number - port: 3306 - -## -## MariaDB chart configuration -## -mariadb: - ## Whether to deploy a mariadb server to satisfy the applications database requirements. To use an external database set this to false and configure the externalDatabase parameters - enabled: true - ## Disable MariaDB replication - replication: - enabled: false - - ## Create a database and a database user - ## ref: https://github.com/bitnami/bitnami-docker-mariadb/blob/master/README.md#creating-a-database-user-on-first-run - ## - db: - name: bitnami_wordpress - user: bn_wordpress - ## If the password is not specified, mariadb will generates a random password - ## - # password: - - ## MariaDB admin password - ## ref: https://github.com/bitnami/bitnami-docker-mariadb/blob/master/README.md#setting-the-root-password-on-first-run - ## - # rootUser: - # password: - - ## Enable persistence using Persistent Volume Claims - ## ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ - ## - master: - persistence: - enabled: true - ## mariadb data Persistent Volume Storage Class - ## If defined, storageClassName: - ## If set to "-", storageClassName: "", which disables dynamic provisioning - ## If undefined (the default) or set to null, no storageClassName spec is - ## set, choosing the default provisioner. (gp2 on AWS, standard on - ## GKE, AWS & OpenStack) - ## - # storageClass: "-" - accessMode: ReadWriteOnce - size: 8Gi - -## Kubernetes configuration -## For minikube, set this to NodePort, elsewhere use LoadBalancer or ClusterIP -## -serviceType: LoadBalancer -## -## serviceType: NodePort -## nodePorts: -## http: -## https: -nodePorts: - http: "" - https: "" -## Enable client source IP preservation -## ref http://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip -## -serviceExternalTrafficPolicy: Cluster - -## Allow health checks to be pointed at the https port -healthcheckHttps: false - -## Configure extra options for liveness and readiness probes -## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes) -livenessProbe: - initialDelaySeconds: 120 - periodSeconds: 10 - timeoutSeconds: 5 - failureThreshold: 6 - successThreshold: 1 -readinessProbe: - initialDelaySeconds: 30 - periodSeconds: 10 - timeoutSeconds: 5 - failureThreshold: 6 - successThreshold: 1 - -## Configure the ingress resource that allows you to access the -## Wordpress installation. Set up the URL -## ref: http://kubernetes.io/docs/user-guide/ingress/ -## -ingress: - ## Set to true to enable ingress record generation - enabled: false - - ## The list of hostnames to be covered with this ingress record. - ## Most likely this will be just one host, but in the event more hosts are needed, this is an array - hosts: - - name: wordpress.local - - ## Set this to true in order to enable TLS on the ingress record - ## A side effect of this will be that the backend wordpress service will be connected at port 443 - tls: false - - ## If TLS is set to true, you must declare what secret will store the key/certificate for TLS - tlsSecret: wordpress.local-tls - - ## Ingress annotations done as key:value pairs - ## If you're using kube-lego, you will want to add: - ## kubernetes.io/tls-acme: true - ## - ## For a full list of possible ingress annotations, please see - ## ref: https://github.com/kubernetes/ingress-nginx/blob/master/docs/annotations.md - ## - ## If tls is set to true, annotation ingress.kubernetes.io/secure-backends: "true" will automatically be set - annotations: - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: true - - secrets: - ## If you're providing your own certificates, please use this to add the certificates as secrets - ## key and certificate should start with -----BEGIN CERTIFICATE----- or - ## -----BEGIN RSA PRIVATE KEY----- - ## - ## name should line up with a tlsSecret set further up - ## If you're using kube-lego, this is unneeded, as it will create the secret for you if it is not set - ## - ## It is also possible to create and manage the certificates outside of this helm chart - ## Please see README.md for more information - # - name: wordpress.local-tls - # key: - # certificate: - -## Enable persistence using Persistent Volume Claims -## ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ -## -persistence: - enabled: true - ## wordpress data Persistent Volume Storage Class - ## If defined, storageClassName: - ## If set to "-", storageClassName: "", which disables dynamic provisioning - ## If undefined (the default) or set to null, no storageClassName spec is - ## set, choosing the default provisioner. (gp2 on AWS, standard on - ## GKE, AWS & OpenStack) - ## - # storageClass: "-" - ## - ## If you want to reuse an existing claim, you can pass the name of the PVC using - ## the existingClaim variable - # existingClaim: your-claim - accessMode: ReadWriteOnce - size: 10Gi - -## Configure resource requests and limits -## ref: http://kubernetes.io/docs/user-guide/compute-resources/ -## -resources: - requests: - memory: 512Mi - cpu: 300m - -## Node labels for pod assignment -## Ref: https://kubernetes.io/docs/user-guide/node-selection/ -## -nodeSelector: {} - -## Tolerations for pod assignment -## Ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ -## -tolerations: [] - -## Affinity for pod assignment -## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity -## -affinity: {} diff --git a/cmd/helm/testdata/testcharts/chart-with-no-templates-dir/Chart.yaml b/pkg/action/testdata/charts/chart-with-no-templates-dir/Chart.yaml similarity index 100% rename from cmd/helm/testdata/testcharts/chart-with-no-templates-dir/Chart.yaml rename to pkg/action/testdata/charts/chart-with-no-templates-dir/Chart.yaml diff --git a/pkg/action/testdata/charts/chart-with-schema-negative/Chart.yaml b/pkg/action/testdata/charts/chart-with-schema-negative/Chart.yaml new file mode 100644 index 000000000..395d24f6a --- /dev/null +++ b/pkg/action/testdata/charts/chart-with-schema-negative/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +description: Empty testing chart +home: https://k8s.io/helm +name: empty +sources: +- https://github.com/kubernetes/helm +version: 0.1.0 diff --git a/pkg/action/testdata/charts/chart-with-schema-negative/templates/empty.yaml b/pkg/action/testdata/charts/chart-with-schema-negative/templates/empty.yaml new file mode 100644 index 000000000..c80812f6e --- /dev/null +++ b/pkg/action/testdata/charts/chart-with-schema-negative/templates/empty.yaml @@ -0,0 +1 @@ +# This file is intentionally blank diff --git a/pkg/action/testdata/charts/chart-with-schema-negative/values.schema.json b/pkg/action/testdata/charts/chart-with-schema-negative/values.schema.json new file mode 100644 index 000000000..4df89bbe8 --- /dev/null +++ b/pkg/action/testdata/charts/chart-with-schema-negative/values.schema.json @@ -0,0 +1,67 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": { + "addresses": { + "description": "List of addresses", + "items": { + "properties": { + "city": { + "type": "string" + }, + "number": { + "type": "number" + }, + "street": { + "type": "string" + } + }, + "type": "object" + }, + "type": "array" + }, + "age": { + "description": "Age", + "minimum": 0, + "type": "integer" + }, + "employmentInfo": { + "properties": { + "salary": { + "minimum": 0, + "type": "number" + }, + "title": { + "type": "string" + } + }, + "required": [ + "salary" + ], + "type": "object" + }, + "firstname": { + "description": "First name", + "type": "string" + }, + "lastname": { + "type": "string" + }, + "likesCoffee": { + "type": "boolean" + }, + "phoneNumbers": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "firstname", + "lastname", + "addresses", + "employmentInfo" + ], + "title": "Values", + "type": "object" +} diff --git a/pkg/action/testdata/charts/chart-with-schema-negative/values.yaml b/pkg/action/testdata/charts/chart-with-schema-negative/values.yaml new file mode 100644 index 000000000..5a1250bff --- /dev/null +++ b/pkg/action/testdata/charts/chart-with-schema-negative/values.yaml @@ -0,0 +1,14 @@ +firstname: John +lastname: Doe +age: -5 +likesCoffee: true +addresses: + - city: Springfield + street: Main + number: 12345 + - city: New York + street: Broadway + number: 67890 +phoneNumbers: + - "(888) 888-8888" + - "(555) 555-5555" diff --git a/pkg/action/testdata/charts/chart-with-schema/Chart.yaml b/pkg/action/testdata/charts/chart-with-schema/Chart.yaml new file mode 100644 index 000000000..395d24f6a --- /dev/null +++ b/pkg/action/testdata/charts/chart-with-schema/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +description: Empty testing chart +home: https://k8s.io/helm +name: empty +sources: +- https://github.com/kubernetes/helm +version: 0.1.0 diff --git a/pkg/action/testdata/charts/chart-with-schema/extra-values.yaml b/pkg/action/testdata/charts/chart-with-schema/extra-values.yaml new file mode 100644 index 000000000..76c290c4f --- /dev/null +++ b/pkg/action/testdata/charts/chart-with-schema/extra-values.yaml @@ -0,0 +1,2 @@ +age: -5 +employmentInfo: null diff --git a/pkg/action/testdata/charts/chart-with-schema/templates/empty.yaml b/pkg/action/testdata/charts/chart-with-schema/templates/empty.yaml new file mode 100644 index 000000000..c80812f6e --- /dev/null +++ b/pkg/action/testdata/charts/chart-with-schema/templates/empty.yaml @@ -0,0 +1 @@ +# This file is intentionally blank diff --git a/pkg/action/testdata/charts/chart-with-schema/values.schema.json b/pkg/action/testdata/charts/chart-with-schema/values.schema.json new file mode 100644 index 000000000..4df89bbe8 --- /dev/null +++ b/pkg/action/testdata/charts/chart-with-schema/values.schema.json @@ -0,0 +1,67 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": { + "addresses": { + "description": "List of addresses", + "items": { + "properties": { + "city": { + "type": "string" + }, + "number": { + "type": "number" + }, + "street": { + "type": "string" + } + }, + "type": "object" + }, + "type": "array" + }, + "age": { + "description": "Age", + "minimum": 0, + "type": "integer" + }, + "employmentInfo": { + "properties": { + "salary": { + "minimum": 0, + "type": "number" + }, + "title": { + "type": "string" + } + }, + "required": [ + "salary" + ], + "type": "object" + }, + "firstname": { + "description": "First name", + "type": "string" + }, + "lastname": { + "type": "string" + }, + "likesCoffee": { + "type": "boolean" + }, + "phoneNumbers": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "firstname", + "lastname", + "addresses", + "employmentInfo" + ], + "title": "Values", + "type": "object" +} diff --git a/pkg/action/testdata/charts/chart-with-schema/values.yaml b/pkg/action/testdata/charts/chart-with-schema/values.yaml new file mode 100644 index 000000000..042dea664 --- /dev/null +++ b/pkg/action/testdata/charts/chart-with-schema/values.yaml @@ -0,0 +1,17 @@ +firstname: John +lastname: Doe +age: 25 +likesCoffee: true +employmentInfo: + title: Software Developer + salary: 100000 +addresses: + - city: Springfield + street: Main + number: 12345 + - city: New York + street: Broadway + number: 67890 +phoneNumbers: + - "(888) 888-8888" + - "(555) 555-5555" diff --git a/pkg/action/testdata/charts/compressedchart-0.1.0.tar.gz b/pkg/action/testdata/charts/compressedchart-0.1.0.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..3c9c24d76063d6a904405b2d6a7a84cb087f1ce4 GIT binary patch literal 477 zcmV<30V4h%iwG0|00000|0w_~VMtOiV@ORlOnEsqVl!4SWK%V1T2nbTPgYhoO;>Dc zVQyr3R8em|NM&qo0PL4vi_<_5!26s}F{UjHhD>x0Xejw~|-egB1QU=&JEdrHEIt>CEtv%dd}n=<=92zSKn;o(8OM@#S> zYFc5(0#_R!xxRXQ%zfa$rtiOMiLGgzk94**j`?3M7Jt0|r`i8O7{f;tq39Bbhr@%1 zO-l}zo#EQJ1_D-Jv7w}jF??!Gg4BiJqa;WzF+;Dc zVQyr3R8em|NM&qo0PL4vi_<_5!26s}F{UjHhD>x0Xejw~|-egB1QU=&JEdrHEIt>CEtv%dd}n=<=92zSKn;o(8OM@#S> zYFc5(0#_R!xxRXQ%zfa$rtiOMiLGgzk94**j`?3M7Jt0|r`i8O7{f;tq39Bbhr@%1 zO-l}zo#EQJ1_D-Jv7w}jF??!Gg4BiJqa;WzF+;Dc zVQyr3R8em|NM&qo0PL4vi_<_5!26s}F)eG5__n?E62T&$9nR zaPZh}uYVQ7^}*#!N0u3azW+itFbbuoJtg79R&dn+S>OM~O_}{4ggavP@bIACqb2wb zHLb8?fvb&=Twgst=05OW)AwJs#MU&^XY$NVoBi$C7~)9n8sjNv1SP;?2z!{Nch zrX>f<&Tws90|BeA*icf%7(TToLFz*AQ4*wspoCYeko Tb2>i)00960YK^g$02BZKS`FmP literal 0 HcmV?d00001 diff --git a/pkg/action/testdata/charts/compressedchart-0.3.0.tgz b/pkg/action/testdata/charts/compressedchart-0.3.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..051bd6fd9a030a3f4327e8002a8912c1f5f68050 GIT binary patch literal 477 zcmV<30V4h%iwG0|00000|0w_~VMtOiV@ORlOnEsqVl!4SWK%V1T2nbTPgYhoO;>Dc zVQyr3R8em|NM&qo0PL4vi_<_5!26s}Fj!(#55d%N%6r{|dRMblL`R$bVgxL;q*9Y4-md z>^-)d>tBU%y?6Q2k!8-o(0?Ht7=_Z)o|156D>&%?bm;&5rp*4Ig*#&Q@bIACqb2wb z4K1-}fvdHT++00A=05OWGxT3|#MZRVM>^RWhx{++^FQAIRrdcZjNv1SP;?2z!~Vg; zx+4dR-f&}F3jyn|*iur(7(R6-LFz;BQ4*w%n4x9A0E<$0#EPK51s@!5z`Na*+mIko1U8OTq2AtqxfdU)P_4<|CYeko Tb38u+00960Q~zm}02BZK0h8&p literal 0 HcmV?d00001 diff --git a/pkg/action/testdata/charts/compressedchart-with-hyphens-0.1.0.tgz b/pkg/action/testdata/charts/compressedchart-with-hyphens-0.1.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..379210a92c1795429d2c4baae389f1e971f26824 GIT binary patch literal 548 zcmV+<0^9u`iwG0|00000|0w_~VMtOiV@ORlOnEsqVl!4SWK%V1T2nbTPgYhoO;>Dc zVQyr3R8em|NM&qo0PL4fi`y^|#dG$jc->rDXjD0A64=|)92WW)wwIoYVoz*QSrU?* zG;H^~7dcDXrjTqgP3fZFMMBsbi zPWu0B_M89nr2n%p#E0nPPIpGV%QZGNX)If*N~tSYQG5|qH0t|nfN!leE_nEwltQJ< z5{(E&Ep_!Aj+6*;9W6i9KdlR0WlY0RR8Xa#gbc6aWCh@%&~0 literal 0 HcmV?d00001 diff --git a/cmd/helm/testdata/testcharts/corrupted-compressed-chart.tgz b/pkg/action/testdata/charts/corrupted-compressed-chart.tgz similarity index 100% rename from cmd/helm/testdata/testcharts/corrupted-compressed-chart.tgz rename to pkg/action/testdata/charts/corrupted-compressed-chart.tgz diff --git a/cmd/helm/testdata/testcharts/decompressedchart/Chart.yaml b/pkg/action/testdata/charts/decompressedchart/Chart.yaml similarity index 100% rename from cmd/helm/testdata/testcharts/decompressedchart/Chart.yaml rename to pkg/action/testdata/charts/decompressedchart/Chart.yaml diff --git a/cmd/helm/testdata/testcharts/decompressedchart/values.yaml b/pkg/action/testdata/charts/decompressedchart/values.yaml similarity index 100% rename from cmd/helm/testdata/testcharts/decompressedchart/values.yaml rename to pkg/action/testdata/charts/decompressedchart/values.yaml diff --git a/cmd/helm/testdata/testcharts/multiplecharts-lint-chart-1/Chart.yaml b/pkg/action/testdata/charts/multiplecharts-lint-chart-1/Chart.yaml similarity index 100% rename from cmd/helm/testdata/testcharts/multiplecharts-lint-chart-1/Chart.yaml rename to pkg/action/testdata/charts/multiplecharts-lint-chart-1/Chart.yaml diff --git a/cmd/helm/testdata/testcharts/multiplecharts-lint-chart-1/templates/configmap.yaml b/pkg/action/testdata/charts/multiplecharts-lint-chart-1/templates/configmap.yaml similarity index 100% rename from cmd/helm/testdata/testcharts/multiplecharts-lint-chart-1/templates/configmap.yaml rename to pkg/action/testdata/charts/multiplecharts-lint-chart-1/templates/configmap.yaml diff --git a/cmd/helm/testdata/testcharts/multiplecharts-lint-chart-1/values.yaml b/pkg/action/testdata/charts/multiplecharts-lint-chart-1/values.yaml similarity index 100% rename from cmd/helm/testdata/testcharts/multiplecharts-lint-chart-1/values.yaml rename to pkg/action/testdata/charts/multiplecharts-lint-chart-1/values.yaml diff --git a/cmd/helm/testdata/testcharts/multiplecharts-lint-chart-2/Chart.yaml b/pkg/action/testdata/charts/multiplecharts-lint-chart-2/Chart.yaml similarity index 100% rename from cmd/helm/testdata/testcharts/multiplecharts-lint-chart-2/Chart.yaml rename to pkg/action/testdata/charts/multiplecharts-lint-chart-2/Chart.yaml diff --git a/cmd/helm/testdata/testcharts/multiplecharts-lint-chart-2/templates/configmap.yaml b/pkg/action/testdata/charts/multiplecharts-lint-chart-2/templates/configmap.yaml similarity index 100% rename from cmd/helm/testdata/testcharts/multiplecharts-lint-chart-2/templates/configmap.yaml rename to pkg/action/testdata/charts/multiplecharts-lint-chart-2/templates/configmap.yaml diff --git a/cmd/helm/testdata/testcharts/multiplecharts-lint-chart-2/values.yaml b/pkg/action/testdata/charts/multiplecharts-lint-chart-2/values.yaml similarity index 100% rename from cmd/helm/testdata/testcharts/multiplecharts-lint-chart-2/values.yaml rename to pkg/action/testdata/charts/multiplecharts-lint-chart-2/values.yaml diff --git a/pkg/action/testdata/charts/pre-release-chart-0.1.0-alpha.tgz b/pkg/action/testdata/charts/pre-release-chart-0.1.0-alpha.tgz new file mode 100644 index 0000000000000000000000000000000000000000..5d5770fed227de5f776eb9080eb377aad6293c1b GIT binary patch literal 355 zcmV-p0i6CHiwG0|00000|0w_~VMtOiV@ORlOnEsqVl!4SWK%V1T2nbTPgYhoO;>Dc zVQyr3R8em|NM&qo0PK{zYV$x4g;nb*23$-3NWU)N&csC^NtY(&SQ_DlU9Ff|8T|G^ za%V#lh=@thS7=o1ZFbK&gK#2jnUs^}ND}@%OyBfO&PEG?h*+29ToLiQVwP7?_P@yL zI1Ma8b~7i_FmV`{SsQ%M$8b5@3*jnN45@T9YE&=p2h=9&w(}W z$?+C$9uN+r2y|ofk(Ta0{KWJPp`$V@VjMq`0UE1~Q@$ zJRGL~X*n=`@No8{Kwvjm3an`imvnLG Date: Sun, 15 Dec 2019 20:13:56 +0530 Subject: [PATCH 132/281] Show errors when linting for Chart.yaml version and appVersion fields of type non-string Signed-off-by: Karuppiah Natarajan --- .../multiplecharts-lint-chart-1/Chart.yaml | 2 +- .../multiplecharts-lint-chart-2/Chart.yaml | 2 +- pkg/lint/rules/chartfile.go | 42 ++++++++++ pkg/lint/rules/chartfile_test.go | 78 +++++++++++++------ .../testdata/anotherbadchartfile/Chart.yaml | 15 ++++ 5 files changed, 112 insertions(+), 27 deletions(-) create mode 100644 pkg/lint/rules/testdata/anotherbadchartfile/Chart.yaml diff --git a/pkg/action/testdata/charts/multiplecharts-lint-chart-1/Chart.yaml b/pkg/action/testdata/charts/multiplecharts-lint-chart-1/Chart.yaml index 3b342ae4f..e33c97e8c 100644 --- a/pkg/action/testdata/charts/multiplecharts-lint-chart-1/Chart.yaml +++ b/pkg/action/testdata/charts/multiplecharts-lint-chart-1/Chart.yaml @@ -1,4 +1,4 @@ apiVersion: v1 name: multiplecharts-lint-chart-1 -version: 1 +version: "1" icon: "" \ No newline at end of file diff --git a/pkg/action/testdata/charts/multiplecharts-lint-chart-2/Chart.yaml b/pkg/action/testdata/charts/multiplecharts-lint-chart-2/Chart.yaml index bd101d808..b27de2754 100644 --- a/pkg/action/testdata/charts/multiplecharts-lint-chart-2/Chart.yaml +++ b/pkg/action/testdata/charts/multiplecharts-lint-chart-2/Chart.yaml @@ -1,4 +1,4 @@ apiVersion: v1 name: multiplecharts-lint-chart-2 -version: 1 +version: "1" icon: "" \ No newline at end of file diff --git a/pkg/lint/rules/chartfile.go b/pkg/lint/rules/chartfile.go index 91a64fe13..b49f2cec0 100644 --- a/pkg/lint/rules/chartfile.go +++ b/pkg/lint/rules/chartfile.go @@ -18,12 +18,14 @@ package rules // import "helm.sh/helm/v3/pkg/lint/rules" import ( "fmt" + "io/ioutil" "os" "path/filepath" "github.com/Masterminds/semver/v3" "github.com/asaskevich/govalidator" "github.com/pkg/errors" + "sigs.k8s.io/yaml" "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/chartutil" @@ -45,11 +47,18 @@ func Chartfile(linter *support.Linter) { return } + // type check for Chart.yaml . ignoring error as any parse + // errors would already be caught in the above load function + chartFileForTypeCheck, _ := loadChartFileForTypeCheck(chartPath) + linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartName(chartFile)) // Chart metadata linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartAPIVersion(chartFile)) + + linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartVersionType(chartFileForTypeCheck)) linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartVersion(chartFile)) + linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartAppVersionType(chartFileForTypeCheck)) linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartMaintainer(chartFile)) linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartSources(chartFile)) linter.RunLinterRule(support.InfoSev, chartFileName, validateChartIconPresence(chartFile)) @@ -58,6 +67,26 @@ func Chartfile(linter *support.Linter) { linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartDependencies(chartFile)) } +func validateChartVersionType(data map[string]interface{}) error { + return isStringValue(data, "version") +} + +func validateChartAppVersionType(data map[string]interface{}) error { + return isStringValue(data, "appVersion") +} + +func isStringValue(data map[string]interface{}, key string) error { + value, ok := data[key] + if !ok { + return nil + } + valueType := fmt.Sprintf("%T", value) + if valueType != "string" { + return errors.Errorf("%s should be of type string but it's of type %s", key, valueType) + } + return nil +} + func validateChartYamlNotDirectory(chartPath string) error { fi, err := os.Stat(chartPath) @@ -166,3 +195,16 @@ func validateChartType(cf *chart.Metadata) error { } return nil } + +// loadChartFileForTypeCheck loads the Chart.yaml +// in a generic form of a map[string]interface{}, so that the type +// of the values can be checked +func loadChartFileForTypeCheck(filename string) (map[string]interface{}, error) { + b, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + y := make(map[string]interface{}) + err = yaml.Unmarshal(b, &y) + return y, err +} diff --git a/pkg/lint/rules/chartfile_test.go b/pkg/lint/rules/chartfile_test.go index d032dd12f..087cda047 100644 --- a/pkg/lint/rules/chartfile_test.go +++ b/pkg/lint/rules/chartfile_test.go @@ -30,7 +30,8 @@ import ( ) const ( - badChartDir = "testdata/badchartfile" + badChartDir = "testdata/badchartfile" + anotherBadChartDir = "testdata/anotherbadchartfile" ) var ( @@ -184,36 +185,63 @@ func TestValidateChartIconURL(t *testing.T) { } func TestChartfile(t *testing.T) { - linter := support.Linter{ChartDir: badChartDir} - Chartfile(&linter) - msgs := linter.Messages + t.Run("Chart.yaml basic validity issues", func(t *testing.T) { + linter := support.Linter{ChartDir: badChartDir} + Chartfile(&linter) + msgs := linter.Messages + expectedNumberOfErrorMessages := 6 + + if len(msgs) != expectedNumberOfErrorMessages { + t.Errorf("Expected %d errors, got %d", expectedNumberOfErrorMessages, len(msgs)) + return + } - 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[0].Err.Error(), "name is required") { - t.Errorf("Unexpected message 0: %s", msgs[0].Err) - } + 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[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(), "version '0.0.0.0' is not a valid SemVer") { + t.Errorf("Unexpected message 2: %s", msgs[2].Err) + } - 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(), "icon is recommended") { + t.Errorf("Unexpected message 3: %s", msgs[3].Err) + } - 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(), "chart type is not valid in apiVersion") { + t.Errorf("Unexpected message 4: %s", msgs[4].Err) + } - 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(), "dependencies are not valid in the Chart file with apiVersion") { + t.Errorf("Unexpected message 5: %s", msgs[5].Err) + } + }) - 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) - } + t.Run("Chart.yaml validity issues due to type mismatch", func(t *testing.T) { + linter := support.Linter{ChartDir: anotherBadChartDir} + Chartfile(&linter) + msgs := linter.Messages + expectedNumberOfErrorMessages := 3 + + if len(msgs) != expectedNumberOfErrorMessages { + t.Errorf("Expected %d errors, got %d", expectedNumberOfErrorMessages, len(msgs)) + return + } + if !strings.Contains(msgs[0].Err.Error(), "version should be of type string") { + t.Errorf("Unexpected message 0: %s", msgs[0].Err) + } + + if !strings.Contains(msgs[1].Err.Error(), "version '7.2445e+06' is not a valid SemVer") { + t.Errorf("Unexpected message 1: %s", msgs[1].Err) + } + + if !strings.Contains(msgs[2].Err.Error(), "appVersion should be of type string") { + t.Errorf("Unexpected message 2: %s", msgs[2].Err) + } + }) } diff --git a/pkg/lint/rules/testdata/anotherbadchartfile/Chart.yaml b/pkg/lint/rules/testdata/anotherbadchartfile/Chart.yaml new file mode 100644 index 000000000..7f3cab390 --- /dev/null +++ b/pkg/lint/rules/testdata/anotherbadchartfile/Chart.yaml @@ -0,0 +1,15 @@ +name: "some-chart" +apiVersion: v2 +description: A Helm chart for Kubernetes +version: 72445e2 +home: "" +type: application +appVersion: 72225e2 +icon: "https://some-url.com/icon.jpeg" +dependencies: + - name: mariadb + version: 5.x.x + repository: https://kubernetes-charts.storage.googleapis.com/ + condition: mariadb.enabled + tags: + - database From a0fd1d81f03aaabbd961f0bfd0dfd907493078c6 Mon Sep 17 00:00:00 2001 From: Peter Engelbert Date: Thu, 28 May 2020 13:44:43 -0500 Subject: [PATCH 133/281] Fix crashing `helm chart list` with large list With a large list of charts, `helm chart list` will crash with the following message: ``` $ helm chart list --debug Error: open /home/me/.cache/helm/registry/cache/blobs/sha256/109971e44d63f7fd11fff60d19db41c2429a136943be2e3f8fd3e4c165156536: too many open files helm.go:75: [debug] open /home/me/.cache/helm/registry/cache/blobs/sha256/109971e44d63f7fd11fff60d19db41c2429a136943be2e3f8fd3e4c165156536: too many open files ``` Signed-off-by: Peter Engelbert --- internal/experimental/registry/cache.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/experimental/registry/cache.go b/internal/experimental/registry/cache.go index fbd62562a..5aca63668 100644 --- a/internal/experimental/registry/cache.go +++ b/internal/experimental/registry/cache.go @@ -357,6 +357,8 @@ func (cache *Cache) fetchBlob(desc *ocispec.Descriptor) ([]byte, error) { if err != nil { return nil, err } + defer reader.Close() + bytes := make([]byte, desc.Size) _, err = reader.ReadAt(bytes, 0) if err != nil { From 7dec5dcb8e2857dd14d0ea43f1c68876b5ac8a2d Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Fri, 29 May 2020 15:54:42 -0400 Subject: [PATCH 134/281] chore(helm): Avoid confusion in command usage Having both the `showCmd` and the `subCmd` passed to `addShowFlags()` can easily lead to mistakes in using the wrong command. Instead, `addShowFlags()` should only focus on the `subCmd` Signed-off-by: Marc Khouzam --- cmd/helm/show.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/helm/show.go b/cmd/helm/show.go index b335c5f76..91c35a53c 100644 --- a/cmd/helm/show.go +++ b/cmd/helm/show.go @@ -136,7 +136,8 @@ func newShowCmd(out io.Writer) *cobra.Command { cmds := []*cobra.Command{all, readmeSubCmd, valuesSubCmd, chartSubCmd} for _, subCmd := range cmds { - addShowFlags(showCommand, subCmd, client) + addShowFlags(subCmd, client) + showCommand.AddCommand(subCmd) // Register the completion function for each subcommand completion.RegisterValidArgsFunc(subCmd, validArgsFunc) @@ -145,12 +146,11 @@ func newShowCmd(out io.Writer) *cobra.Command { return showCommand } -func addShowFlags(showCmd *cobra.Command, subCmd *cobra.Command, client *action.Show) { +func addShowFlags(subCmd *cobra.Command, client *action.Show) { f := subCmd.Flags() f.BoolVar(&client.Devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored") addChartPathOptionsFlags(f, &client.ChartPathOptions) - showCmd.AddCommand(subCmd) } func runShow(args []string, client *action.Show) (string, error) { From f11e74ee571f6a21ea5fd647513324d7268ff8d3 Mon Sep 17 00:00:00 2001 From: Peter Engelbert Date: Wed, 3 Jun 2020 15:23:26 -0500 Subject: [PATCH 135/281] Add unit test for man-in-the-middle attack on pull Add a unit test that proves the digest of the received content being checked. The check should ensure that the digest of the received content is identical to the digest provided by the manifest in the layers[0] descriptor. This check is currently implemented in containerd, so the unit test ensures security in the case a breaking change is made in containerd. Signed-off-by: Peter Engelbert --- internal/experimental/registry/client_test.go | 66 +++++++++++++++++-- 1 file changed, 62 insertions(+), 4 deletions(-) diff --git a/internal/experimental/registry/client_test.go b/internal/experimental/registry/client_test.go index 6e9d5db36..2d208b7b9 100644 --- a/internal/experimental/registry/client_test.go +++ b/internal/experimental/registry/client_test.go @@ -24,11 +24,16 @@ import ( "io/ioutil" "net" "net/http" + "net/http/httptest" + "net/url" "os" "path/filepath" + "strings" "testing" "time" + "github.com/containerd/containerd/errdefs" + auth "github.com/deislabs/oras/pkg/auth/docker" "github.com/docker/distribution/configuration" "github.com/docker/distribution/registry" @@ -49,10 +54,11 @@ var ( type RegistryClientTestSuite struct { suite.Suite - Out io.Writer - DockerRegistryHost string - CacheRootDir string - RegistryClient *Client + Out io.Writer + DockerRegistryHost string + CompromisedRegistryHost string + CacheRootDir string + RegistryClient *Client } func (suite *RegistryClientTestSuite) SetupSuite() { @@ -116,6 +122,8 @@ func (suite *RegistryClientTestSuite) SetupSuite() { dockerRegistry, err := registry.NewRegistry(context.Background(), config) suite.Nil(err, "no error creating test registry") + suite.CompromisedRegistryHost = initCompromisedRegistryTestServer() + // Start Docker registry go dockerRegistry.ListenAndServe() } @@ -232,6 +240,16 @@ func (suite *RegistryClientTestSuite) Test_7_Logout() { suite.Nil(err, "no error logging out of registry") } +func (suite *RegistryClientTestSuite) Test_8_ManInTheMiddle() { + ref, err := ParseReference(fmt.Sprintf("%s/testrepo/supposedlysafechart:9.9.9", suite.CompromisedRegistryHost)) + suite.Nil(err) + + // returns content that does not match the expected digest + err = suite.RegistryClient.PullChart(ref) + suite.NotNil(err) + suite.True(errdefs.IsFailedPrecondition(err)) +} + func TestRegistryClientTestSuite(t *testing.T) { suite.Run(t, new(RegistryClientTestSuite)) } @@ -250,3 +268,43 @@ func getFreePort() (int, error) { defer l.Close() return l.Addr().(*net.TCPAddr).Port, nil } + +func initCompromisedRegistryTestServer() string { + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if strings.Contains(r.URL.Path, "manifests") { + w.Header().Set("Content-Type", "application/vnd.oci.image.manifest.v1+json") + w.WriteHeader(200) + + // layers[0] is the blob []byte("a") + w.Write([]byte( + `{ "schemaVersion": 2, "config": { + "mediaType": "application/vnd.cncf.helm.config.v1+json", + "digest": "sha256:a705ee2789ab50a5ba20930f246dbd5cc01ff9712825bb98f57ee8414377f133", + "size": 181 + }, + "layers": [ + { + "mediaType": "application/tar+gzip", + "digest": "sha256:ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb", + "size": 1 + } + ] +}`)) + } else if r.URL.Path == "/v2/testrepo/supposedlysafechart/blobs/sha256:a705ee2789ab50a5ba20930f246dbd5cc01ff9712825bb98f57ee8414377f133" { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + w.Write([]byte("{\"name\":\"mychart\",\"version\":\"0.1.0\",\"description\":\"A Helm chart for Kubernetes\\n" + + "an 'application' or a 'library' chart.\",\"apiVersion\":\"v2\",\"appVersion\":\"1.16.0\",\"type\":" + + "\"application\"}")) + } else if r.URL.Path == "/v2/testrepo/supposedlysafechart/blobs/sha256:ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb" { + w.Header().Set("Content-Type", "application/tar+gzip") + w.WriteHeader(200) + w.Write([]byte("b")) + } else { + w.WriteHeader(500) + } + })) + + u, _ := url.Parse(s.URL) + return fmt.Sprintf("localhost:%s", u.Port()) +} From 8f3c0a160cb9e5aa15a2c7a2f618704eea5de743 Mon Sep 17 00:00:00 2001 From: Calle Pettersson Date: Tue, 2 Jun 2020 13:43:53 +0200 Subject: [PATCH 136/281] Report what cause finding chart to fail Signed-off-by: Calle Pettersson --- pkg/downloader/manager.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/downloader/manager.go b/pkg/downloader/manager.go index 00198de0c..28a656e55 100644 --- a/pkg/downloader/manager.go +++ b/pkg/downloader/manager.go @@ -571,7 +571,7 @@ func (m *Manager) findChartURL(name, version, repoURL string, repos map[string]* if err == nil { return } - err = errors.Errorf("chart %s not found in %s", name, repoURL) + err = errors.Errorf("chart %s not found in %s: %s", name, repoURL, err) return } From 562767b04015546e53677bb3c8aa5cae12897fa3 Mon Sep 17 00:00:00 2001 From: wawa0210 Date: Tue, 9 Jun 2020 17:49:02 +0800 Subject: [PATCH 137/281] Fix description is ignore when installed with upgrade Signed-off-by: wawa0210 --- cmd/helm/upgrade.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/helm/upgrade.go b/cmd/helm/upgrade.go index c263d32e7..f8103b485 100644 --- a/cmd/helm/upgrade.go +++ b/cmd/helm/upgrade.go @@ -100,6 +100,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { instClient.PostRenderer = client.PostRenderer instClient.DisableOpenAPIValidation = client.DisableOpenAPIValidation instClient.SubNotes = client.SubNotes + instClient.Description = client.Description rel, err := runInstall(args, instClient, valueOpts, out) if err != nil { From e09e8604e6160775890c154b1fb8b6683a8bbb6f Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Fri, 29 May 2020 14:58:52 -0400 Subject: [PATCH 138/281] fix(doc): generic description for --version/verify The flags added by addChartPathOptionsFlags() are used for `helm install` and `helm upgrade` but also for `helm template`, `helm show` and `helm pull`. Because of the latter three, we should not use the word 'install' in the description of the --version and --verify flags. Signed-off-by: Marc Khouzam --- cmd/helm/flags.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/helm/flags.go b/cmd/helm/flags.go index 246cb0dd5..6b9c3050b 100644 --- a/cmd/helm/flags.go +++ b/cmd/helm/flags.go @@ -41,8 +41,8 @@ func addValueOptionsFlags(f *pflag.FlagSet, v *values.Options) { } func addChartPathOptionsFlags(f *pflag.FlagSet, c *action.ChartPathOptions) { - f.StringVar(&c.Version, "version", "", "specify the exact chart version to install. If this is not specified, the latest version is installed") - f.BoolVar(&c.Verify, "verify", false, "verify the package before installing it") + f.StringVar(&c.Version, "version", "", "specify the exact chart version to use. If this is not specified, the latest version is used") + f.BoolVar(&c.Verify, "verify", false, "verify the package before using it") f.StringVar(&c.Keyring, "keyring", defaultKeyring(), "location of public keys used for verification") f.StringVar(&c.RepoURL, "repo", "", "chart repository url where to locate the requested chart") f.StringVar(&c.Username, "username", "", "chart repository username where to locate the requested chart") From 70f89e5f26015a92432bdeb94ddb6d6e52532b19 Mon Sep 17 00:00:00 2001 From: Andrew Melis Date: Sat, 6 Jun 2020 03:32:49 -0400 Subject: [PATCH 139/281] Make helm ls return only current releases if providing state filter Previously, the `helm ls --$state` operation would display outdated releases under certain conditions. Given the following set of releases: ``` NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE bar 1 Wed Apr 8 16:54:39 2020 DEPLOYED bar-4.0.0 1.0 default foo 1 Fri Feb 7 06:16:56 2020 DEPLOYED foo-0.1.0 1.0 default foo 2 Mon May 4 07:16:56 2020 FAILED foo-0.1.0 1.0 default foo 3 Mon May 4 07:20:00 2020 FAILED foo-0.1.0 1.0 default foo 4 Tue May 5 08:16:56 2020 DEPLOYED foo-0.2.0 1.0 default qux 1 Tue Jun 9 10:32:00 2020 DEPLOYED qux-4.0.3 1.0 default qux 2 Tue Jun 9 10:57:00 2020 FAILED qux-4.0.3 1.0 default ``` `helm ls --failed` produced the following output: ``` NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE foo 3 Mon May 4 07:20:00 2020 FAILED foo-0.1.0 1.0 default qux 2 Tue Jun 9 10:57:00 2020 FAILED qux-4.0.0 1.0 default ``` Including the `qux` release in that `helm ls --failed` output is not controversial; the most recent revision of `qux` was not successful and an operator should investigate. Including the `foo` release in the output, however, is questionable. Revision 3 of `foo` is _not_ the most recent release of `foo`, and that FAILED release was fixed in a susubsequent upgrade. A user may see that FAILED deploy and start taking inappropriate action. Further, that issue was fixed months ago in this example -- troubleshooting an old deploy may not be safe if significant changes have occurred. Concern over this behavior was raised in https://github.com/helm/helm/issues/7495. This behavior applied to all the state filter flags (--deployed, --failed, --pending, etc.), and a user could pass multiple state filter flags to a single command. The previous behavior can be summarized as follows: For each release name, all release revisions having any of the supplied state flags were retrieved, and the most recent revision among these was returned (regardless of whether a newer revision of an unspecified state exists). This change request alters the helm list action to match user expectations such that only "current" releases are shown when filtering on release state. After this change, the following output would be produced by `helm ls --failed`: ``` NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE qux 2 Tue Jun 9 10:57:00 2020 FAILED qux-4.0.0 1.0 default ``` The command now returns only `qux` because it is the only "current" FAILED release. This behavior change applies to all the state filters _except_ `superseded`, which now becomes a special case. By definition, at least one newer release exists ahead of each superseded release. A conditional is included in this change request to maintain the preexisting behavior (return "most recent" superseded revison for each release name) if the superseded state filter is requested. --- Note that there is an alternate perspective that a state filter flag should return all releases of a given state rather than only the "current" releases. In the above example, `helm ls --failed` with this approach would return the following: ``` NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE foo 2 Mon May 4 07:16:56 2020 FAILED foo-0.1.0 1.0 default foo 3 Mon May 4 07:20:00 2020 FAILED foo-0.1.0 1.0 default qux 2 Tue Jun 9 10:57:00 2020 FAILED qux-4.0.0 1.0 default ``` Multiple FAILED `foo` revisions are included in the output, unlike the current behavior. This approach is logical and achievable. It allows a user to find exactly what is requested: all historical releases of a given state. In order to achieve continuity with helm behavior, however, a new filter (something like "current") would probably need to be implemented and become the new default. Given current helm behavior as well as the comments in the #7495, I did not pursue this approach. --- Technical details: - Moved list action state mask filter after latest release filter Previously, the list operation in helm/pkg/action/list.go skipped releases that were not covered by the state mask on _retrieval_ from the Releases store: ``` results, err := l.cfg.Releases.List(func(rel *release.Release) bool { // Skip anything that the mask doesn't cover currentStatus := l.StateMask.FromName(rel.Info.Status.String()) if l.StateMask¤tStatus == 0 { return false } ... ``` https://github.com/helm/helm/blob/8ea6b970ecd02365a230420692350057d48278e5/pkg/action/list.go#L154-L159 While filtering on retrieval in this manner avoided an extra iteration through the entire list to check on the supplied condition later, it introduced the possibility of returning an outdated release to the user because newer releases (that would have otherwise squashed outdated releases in the `filterList` function) are simply not included in the set of working records. This change moves the state mask filtering process to _after_ the set of current releases is built. Outdated, potentially misleading releases are scrubbed out prior to the application of the state mask filter. As written, this state mask filtration (in the new `filterStateMask` method on `*List`) incurs an additional, potentially expensive iteration over the set of releases to return to the user. An alternative approach could avoid that extra iteration and fit this logic into the existing `filterList` function at the cost of making `filterList` function a little harder to understand. - Rename filterList to filterLatestReleases for clarity Another function that filters the list is added, so update to the more descriptive name here. - List superseded releases without filtering for latest This change makes superseded releases a special case, as they would _never_ be displayed otherwise (by definition, as superseded releases have been replaced by a newer release), so a conditional maintains current behavior ("return newest superseded revision for each release name") Fixes #7495. Signed-off-by: Andrew Melis --- pkg/action/list.go | 35 +++++++++++++++++++------- pkg/action/list_test.go | 56 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 79 insertions(+), 12 deletions(-) diff --git a/pkg/action/list.go b/pkg/action/list.go index ac6fd1b75..3d5e6d7a6 100644 --- a/pkg/action/list.go +++ b/pkg/action/list.go @@ -152,12 +152,6 @@ func (l *List) Run() ([]*release.Release, error) { } results, err := l.cfg.Releases.List(func(rel *release.Release) bool { - // Skip anything that the mask doesn't cover - currentStatus := l.StateMask.FromName(rel.Info.Status.String()) - if l.StateMask¤tStatus == 0 { - return false - } - // Skip anything that doesn't match the filter. if filter != nil && !filter.MatchString(rel.Name) { return false @@ -173,7 +167,16 @@ func (l *List) Run() ([]*release.Release, error) { return results, nil } - results = filterList(results) + // by definition, superseded releases are never shown if + // only the latest releases are returned. so if requested statemask + // is _only_ ListSuperseded, skip the latest release filter + if l.StateMask != ListSuperseded { + results = filterLatestReleases(results) + } + + // State mask application must occur after filtering to + // latest releases, otherwise outdated entries can be returned + results = l.filterStateMask(results) // Unfortunately, we have to sort before truncating, which can incur substantial overhead l.sort(results) @@ -222,8 +225,8 @@ func (l *List) sort(rels []*release.Release) { } } -// filterList returns a list scrubbed of old releases. -func filterList(releases []*release.Release) []*release.Release { +// filterLatestReleases returns a list scrubbed of old releases. +func filterLatestReleases(releases []*release.Release) []*release.Release { latestReleases := make(map[string]*release.Release) for _, rls := range releases { @@ -242,6 +245,20 @@ func filterList(releases []*release.Release) []*release.Release { return list } +func (l *List) filterStateMask(releases []*release.Release) []*release.Release { + desiredStateReleases := make([]*release.Release, 0) + + for _, rls := range releases { + currentStatus := l.StateMask.FromName(rls.Info.Status.String()) + if l.StateMask¤tStatus == 0 { + continue + } + desiredStateReleases = append(desiredStateReleases, rls) + } + + return desiredStateReleases +} + // SetStateMask calculates the state mask based on parameters. func (l *List) SetStateMask() { if l.All { diff --git a/pkg/action/list_test.go b/pkg/action/list_test.go index 378a747b0..b8e2ece68 100644 --- a/pkg/action/list_test.go +++ b/pkg/action/list_test.go @@ -188,6 +188,56 @@ func TestList_StateMask(t *testing.T) { is.Len(res, 3) } +func TestList_StateMaskWithStaleRevisions(t *testing.T) { + is := assert.New(t) + lister := newListFixture(t) + lister.StateMask = ListFailed + + makeMeSomeReleasesWithStaleFailure(lister.cfg.Releases, t) + + res, err := lister.Run() + + is.NoError(err) + is.Len(res, 1) + + // "dirty" release should _not_ be present as most recent + // release is deployed despite failed release in past + is.Equal("failed", res[0].Name) +} + +func makeMeSomeReleasesWithStaleFailure(store *storage.Storage, t *testing.T) { + t.Helper() + one := namedReleaseStub("clean", release.StatusDeployed) + one.Namespace = "default" + one.Version = 1 + + two := namedReleaseStub("dirty", release.StatusDeployed) + two.Namespace = "default" + two.Version = 1 + + three := namedReleaseStub("dirty", release.StatusFailed) + three.Namespace = "default" + three.Version = 2 + + four := namedReleaseStub("dirty", release.StatusDeployed) + four.Namespace = "default" + four.Version = 3 + + five := namedReleaseStub("failed", release.StatusFailed) + five.Namespace = "default" + five.Version = 1 + + for _, rel := range []*release.Release{one, two, three, four, five} { + if err := store.Create(rel); err != nil { + t.Fatal(err) + } + } + + all, err := store.ListReleases() + assert.NoError(t, err) + assert.Len(t, all, 5, "sanity test: five items added") +} + func TestList_Filter(t *testing.T) { is := assert.New(t) lister := newListFixture(t) @@ -236,7 +286,7 @@ func makeMeSomeReleases(store *storage.Storage, t *testing.T) { assert.Len(t, all, 3, "sanity test: three items added") } -func TestFilterList(t *testing.T) { +func TestFilterLatestReleases(t *testing.T) { t.Run("should filter old versions of the same release", func(t *testing.T) { r1 := releaseStub() r1.Name = "r" @@ -248,7 +298,7 @@ func TestFilterList(t *testing.T) { another.Name = "another" another.Version = 1 - filteredList := filterList([]*release.Release{r1, r2, another}) + filteredList := filterLatestReleases([]*release.Release{r1, r2, another}) expectedFilteredList := []*release.Release{r2, another} assert.ElementsMatch(t, expectedFilteredList, filteredList) @@ -264,7 +314,7 @@ func TestFilterList(t *testing.T) { r2.Namespace = "testing" r2.Version = 2 - filteredList := filterList([]*release.Release{r1, r2}) + filteredList := filterLatestReleases([]*release.Release{r1, r2}) expectedFilteredList := []*release.Release{r1, r2} assert.ElementsMatch(t, expectedFilteredList, filteredList) From 88a3d6eaea65971b3e4884b5abbcccc4d7a79f72 Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Sat, 11 Apr 2020 14:06:56 -0400 Subject: [PATCH 140/281] feat(comp): Move custom completions to Cobra 1.0 Cobra 1.0 introduces custom Go completions. This commit replaces Helm's own solution to use Cobra's solution. This allows to completely remove Helm's internal "completion" package. Signed-off-by: Marc Khouzam --- cmd/helm/flags.go | 13 +- cmd/helm/get_all.go | 25 +- cmd/helm/get_hooks.go | 28 +- cmd/helm/get_manifest.go | 28 +- cmd/helm/get_notes.go | 25 +- cmd/helm/get_values.go | 26 +- cmd/helm/history.go | 21 +- cmd/helm/install.go | 13 +- cmd/helm/list.go | 9 +- cmd/helm/load_plugins.go | 22 +- cmd/helm/plugin_uninstall.go | 16 +- cmd/helm/plugin_update.go | 16 +- cmd/helm/pull.go | 15 +- cmd/helm/release_testing.go | 15 +- cmd/helm/repo_remove.go | 10 +- cmd/helm/rollback.go | 15 +- cmd/helm/root.go | 40 +- cmd/helm/search_repo.go | 23 +- cmd/helm/show.go | 44 +- cmd/helm/status.go | 26 +- cmd/helm/template.go | 9 +- cmd/helm/testdata/output/output-comp.txt | 1 + cmd/helm/testdata/output/plugin_args_comp.txt | 1 + .../testdata/output/plugin_args_flag_comp.txt | 1 + .../output/plugin_args_many_args_comp.txt | 1 + .../testdata/output/plugin_args_ns_comp.txt | 1 + .../output/plugin_echo_bad_directive.txt | 1 + .../output/plugin_echo_no_directive.txt | 1 + cmd/helm/testdata/output/revision-comp.txt | 1 + .../output/revision-wrong-args-comp.txt | 1 + cmd/helm/testdata/output/status-comp.txt | 1 + .../output/status-wrong-args-comp.txt | 1 + cmd/helm/uninstall.go | 15 +- cmd/helm/upgrade.go | 21 +- internal/completion/complete.go | 399 ------------------ 35 files changed, 235 insertions(+), 650 deletions(-) delete mode 100644 internal/completion/complete.go diff --git a/cmd/helm/flags.go b/cmd/helm/flags.go index 6b9c3050b..d4cd88fef 100644 --- a/cmd/helm/flags.go +++ b/cmd/helm/flags.go @@ -18,12 +18,12 @@ package main import ( "fmt" + "log" "strings" "github.com/spf13/cobra" "github.com/spf13/pflag" - "helm.sh/helm/v3/internal/completion" "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/cli/output" "helm.sh/helm/v3/pkg/cli/values" @@ -55,19 +55,22 @@ func addChartPathOptionsFlags(f *pflag.FlagSet, c *action.ChartPathOptions) { // bindOutputFlag will add the output flag to the given command and bind the // value to the given format pointer func bindOutputFlag(cmd *cobra.Command, varRef *output.Format) { - f := cmd.Flags() - flag := f.VarPF(newOutputValue(output.Table, varRef), outputFlag, "o", + cmd.Flags().VarP(newOutputValue(output.Table, varRef), outputFlag, "o", fmt.Sprintf("prints the output in the specified format. Allowed values: %s", strings.Join(output.Formats(), ", "))) - completion.RegisterFlagCompletionFunc(flag, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { + err := cmd.RegisterFlagCompletionFunc(outputFlag, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { var formatNames []string for _, format := range output.Formats() { if strings.HasPrefix(format, toComplete) { formatNames = append(formatNames, format) } } - return formatNames, completion.BashCompDirectiveDefault + return formatNames, cobra.ShellCompDirectiveDefault }) + + if err != nil { + log.Fatal(err) + } } type outputValue output.Format diff --git a/cmd/helm/get_all.go b/cmd/helm/get_all.go index 7d893d7e0..1ebc7e387 100644 --- a/cmd/helm/get_all.go +++ b/cmd/helm/get_all.go @@ -18,11 +18,11 @@ package main import ( "io" + "log" "github.com/spf13/cobra" "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/internal/completion" "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/cli/output" ) @@ -41,6 +41,12 @@ func newGetAllCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { Short: "download all information for a named release", Long: getAllHelp, Args: require.ExactArgs(1), + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + return compListReleases(toComplete, cfg) + }, RunE: func(cmd *cobra.Command, args []string) error { res, err := client.Run(args[0]) if err != nil { @@ -57,24 +63,19 @@ func newGetAllCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { }, } - // Function providing dynamic auto-completion - completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) != 0 { - return nil, completion.BashCompDirectiveNoFileComp - } - return compListReleases(toComplete, cfg) - }) - f := cmd.Flags() f.IntVar(&client.Version, "revision", 0, "get the named release with revision") - flag := f.Lookup("revision") - completion.RegisterFlagCompletionFunc(flag, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { + err := cmd.RegisterFlagCompletionFunc("revision", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) == 1 { return compListRevisions(cfg, args[0]) } - return nil, completion.BashCompDirectiveNoFileComp + return nil, cobra.ShellCompDirectiveNoFileComp }) + if err != nil { + log.Fatal(err) + } + f.StringVar(&template, "template", "", "go template for formatting the output, eg: {{.Release.Name}}") return cmd diff --git a/cmd/helm/get_hooks.go b/cmd/helm/get_hooks.go index c2087b1ba..6453c30eb 100644 --- a/cmd/helm/get_hooks.go +++ b/cmd/helm/get_hooks.go @@ -19,11 +19,11 @@ package main import ( "fmt" "io" + "log" "github.com/spf13/cobra" "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/internal/completion" "helm.sh/helm/v3/pkg/action" ) @@ -41,6 +41,12 @@ func newGetHooksCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { Short: "download all hooks for a named release", Long: getHooksHelp, Args: require.ExactArgs(1), + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + return compListReleases(toComplete, cfg) + }, RunE: func(cmd *cobra.Command, args []string) error { res, err := client.Run(args[0]) if err != nil { @@ -53,23 +59,17 @@ func newGetHooksCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { }, } - // Function providing dynamic auto-completion - completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) != 0 { - return nil, completion.BashCompDirectiveNoFileComp - } - return compListReleases(toComplete, cfg) - }) - - f := cmd.Flags() - f.IntVar(&client.Version, "revision", 0, "get the named release with revision") - flag := f.Lookup("revision") - completion.RegisterFlagCompletionFunc(flag, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { + cmd.Flags().IntVar(&client.Version, "revision", 0, "get the named release with revision") + err := cmd.RegisterFlagCompletionFunc("revision", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) == 1 { return compListRevisions(cfg, args[0]) } - return nil, completion.BashCompDirectiveNoFileComp + return nil, cobra.ShellCompDirectiveNoFileComp }) + if err != nil { + log.Fatal(err) + } + return cmd } diff --git a/cmd/helm/get_manifest.go b/cmd/helm/get_manifest.go index f332befd9..048bf36b0 100644 --- a/cmd/helm/get_manifest.go +++ b/cmd/helm/get_manifest.go @@ -19,11 +19,11 @@ package main import ( "fmt" "io" + "log" "github.com/spf13/cobra" "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/internal/completion" "helm.sh/helm/v3/pkg/action" ) @@ -43,6 +43,12 @@ func newGetManifestCmd(cfg *action.Configuration, out io.Writer) *cobra.Command Short: "download the manifest for a named release", Long: getManifestHelp, Args: require.ExactArgs(1), + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + return compListReleases(toComplete, cfg) + }, RunE: func(cmd *cobra.Command, args []string) error { res, err := client.Run(args[0]) if err != nil { @@ -53,23 +59,17 @@ func newGetManifestCmd(cfg *action.Configuration, out io.Writer) *cobra.Command }, } - // Function providing dynamic auto-completion - completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) != 0 { - return nil, completion.BashCompDirectiveNoFileComp - } - return compListReleases(toComplete, cfg) - }) - - f := cmd.Flags() - f.IntVar(&client.Version, "revision", 0, "get the named release with revision") - flag := f.Lookup("revision") - completion.RegisterFlagCompletionFunc(flag, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { + cmd.Flags().IntVar(&client.Version, "revision", 0, "get the named release with revision") + err := cmd.RegisterFlagCompletionFunc("revision", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) == 1 { return compListRevisions(cfg, args[0]) } - return nil, completion.BashCompDirectiveNoFileComp + return nil, cobra.ShellCompDirectiveNoFileComp }) + if err != nil { + log.Fatal(err) + } + return cmd } diff --git a/cmd/helm/get_notes.go b/cmd/helm/get_notes.go index 4491bd9ba..88d494fc4 100644 --- a/cmd/helm/get_notes.go +++ b/cmd/helm/get_notes.go @@ -19,11 +19,11 @@ package main import ( "fmt" "io" + "log" "github.com/spf13/cobra" "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/internal/completion" "helm.sh/helm/v3/pkg/action" ) @@ -39,6 +39,12 @@ func newGetNotesCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { Short: "download the notes for a named release", Long: getNotesHelp, Args: require.ExactArgs(1), + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + return compListReleases(toComplete, cfg) + }, RunE: func(cmd *cobra.Command, args []string) error { res, err := client.Run(args[0]) if err != nil { @@ -51,23 +57,18 @@ func newGetNotesCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { }, } - // Function providing dynamic auto-completion - completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) != 0 { - return nil, completion.BashCompDirectiveNoFileComp - } - return compListReleases(toComplete, cfg) - }) - f := cmd.Flags() f.IntVar(&client.Version, "revision", 0, "get the named release with revision") - flag := f.Lookup("revision") - completion.RegisterFlagCompletionFunc(flag, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { + err := cmd.RegisterFlagCompletionFunc("revision", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) == 1 { return compListRevisions(cfg, args[0]) } - return nil, completion.BashCompDirectiveNoFileComp + return nil, cobra.ShellCompDirectiveNoFileComp }) + if err != nil { + log.Fatal(err) + } + return cmd } diff --git a/cmd/helm/get_values.go b/cmd/helm/get_values.go index a8c5acc5e..fa7b10fed 100644 --- a/cmd/helm/get_values.go +++ b/cmd/helm/get_values.go @@ -19,11 +19,11 @@ package main import ( "fmt" "io" + "log" "github.com/spf13/cobra" "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/internal/completion" "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/cli/output" ) @@ -46,6 +46,12 @@ func newGetValuesCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { Short: "download the values file for a named release", Long: getValuesHelp, Args: require.ExactArgs(1), + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + return compListReleases(toComplete, cfg) + }, RunE: func(cmd *cobra.Command, args []string) error { vals, err := client.Run(args[0]) if err != nil { @@ -55,23 +61,19 @@ func newGetValuesCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { }, } - // Function providing dynamic auto-completion - completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) != 0 { - return nil, completion.BashCompDirectiveNoFileComp - } - return compListReleases(toComplete, cfg) - }) - f := cmd.Flags() f.IntVar(&client.Version, "revision", 0, "get the named release with revision") - flag := f.Lookup("revision") - completion.RegisterFlagCompletionFunc(flag, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { + err := cmd.RegisterFlagCompletionFunc("revision", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) == 1 { return compListRevisions(cfg, args[0]) } - return nil, completion.BashCompDirectiveNoFileComp + return nil, cobra.ShellCompDirectiveNoFileComp }) + + if err != nil { + log.Fatal(err) + } + f.BoolVarP(&client.AllValues, "all", "a", false, "dump all (computed) values") bindOutputFlag(cmd, &outfmt) diff --git a/cmd/helm/history.go b/cmd/helm/history.go index 3ef542e58..b49c52990 100644 --- a/cmd/helm/history.go +++ b/cmd/helm/history.go @@ -26,7 +26,6 @@ import ( "github.com/spf13/cobra" "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/internal/completion" "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/cli/output" @@ -61,6 +60,12 @@ func newHistoryCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { Short: "fetch release history", Aliases: []string{"hist"}, Args: require.ExactArgs(1), + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + return compListReleases(toComplete, cfg) + }, RunE: func(cmd *cobra.Command, args []string) error { history, err := getHistory(client, args[0]) if err != nil { @@ -71,14 +76,6 @@ func newHistoryCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { }, } - // Function providing dynamic auto-completion - completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) != 0 { - return nil, completion.BashCompDirectiveNoFileComp - } - return compListReleases(toComplete, cfg) - }) - f := cmd.Flags() f.IntVar(&client.Max, "max", 256, "maximum number of revision to include in history") bindOutputFlag(cmd, &outfmt) @@ -187,7 +184,7 @@ func min(x, y int) int { return y } -func compListRevisions(cfg *action.Configuration, releaseName string) ([]string, completion.BashCompDirective) { +func compListRevisions(cfg *action.Configuration, releaseName string) ([]string, cobra.ShellCompDirective) { client := action.NewHistory(cfg) var revisions []string @@ -195,7 +192,7 @@ func compListRevisions(cfg *action.Configuration, releaseName string) ([]string, for _, release := range hist { revisions = append(revisions, strconv.Itoa(release.Version)) } - return revisions, completion.BashCompDirectiveDefault + return revisions, cobra.ShellCompDirectiveDefault } - return nil, completion.BashCompDirectiveError + return nil, cobra.ShellCompDirectiveError } diff --git a/cmd/helm/install.go b/cmd/helm/install.go index 44f7336c0..b489c262d 100644 --- a/cmd/helm/install.go +++ b/cmd/helm/install.go @@ -26,7 +26,6 @@ import ( "github.com/spf13/pflag" "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/internal/completion" "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/chart/loader" @@ -114,6 +113,9 @@ func newInstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { Short: "install a chart", Long: installDesc, Args: require.MinimumNArgs(1), + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return compInstall(args, toComplete, client) + }, RunE: func(_ *cobra.Command, args []string) error { rel, err := runInstall(args, client, valueOpts, out) if err != nil { @@ -124,11 +126,6 @@ func newInstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { }, } - // Function providing dynamic auto-completion - completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - return compInstall(args, toComplete, client) - }) - addInstallFlags(cmd.Flags(), client, valueOpts) bindOutputFlag(cmd, &outfmt) bindPostRenderFlag(cmd, &client.PostRenderer) @@ -242,7 +239,7 @@ func isChartInstallable(ch *chart.Chart) (bool, error) { } // Provide dynamic auto-completion for the install and template commands -func compInstall(args []string, toComplete string, client *action.Install) ([]string, completion.BashCompDirective) { +func compInstall(args []string, toComplete string, client *action.Install) ([]string, cobra.ShellCompDirective) { requiredArgs := 1 if client.GenerateName { requiredArgs = 0 @@ -250,5 +247,5 @@ func compInstall(args []string, toComplete string, client *action.Install) ([]st if len(args) == requiredArgs { return compListCharts(toComplete, true) } - return nil, completion.BashCompDirectiveNoFileComp + return nil, cobra.ShellCompDirectiveNoFileComp } diff --git a/cmd/helm/list.go b/cmd/helm/list.go index 08d6beb79..18e5ffe45 100644 --- a/cmd/helm/list.go +++ b/cmd/helm/list.go @@ -26,7 +26,6 @@ import ( "github.com/spf13/cobra" "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/internal/completion" "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/cli/output" "helm.sh/helm/v3/pkg/release" @@ -186,8 +185,8 @@ func (r *releaseListWriter) WriteYAML(out io.Writer) error { } // Provide dynamic auto-completion for release names -func compListReleases(toComplete string, cfg *action.Configuration) ([]string, completion.BashCompDirective) { - completion.CompDebugln(fmt.Sprintf("compListReleases with toComplete %s", toComplete)) +func compListReleases(toComplete string, cfg *action.Configuration) ([]string, cobra.ShellCompDirective) { + cobra.CompDebugln(fmt.Sprintf("compListReleases with toComplete %s", toComplete), settings.Debug) client := action.NewList(cfg) client.All = true @@ -197,7 +196,7 @@ func compListReleases(toComplete string, cfg *action.Configuration) ([]string, c client.SetStateMask() results, err := client.Run() if err != nil { - return nil, completion.BashCompDirectiveDefault + return nil, cobra.ShellCompDirectiveDefault } var choices []string @@ -205,5 +204,5 @@ func compListReleases(toComplete string, cfg *action.Configuration) ([]string, c choices = append(choices, res.Name) } - return choices, completion.BashCompDirectiveNoFileComp + return choices, cobra.ShellCompDirectiveNoFileComp } diff --git a/cmd/helm/load_plugins.go b/cmd/helm/load_plugins.go index 36de50135..e439c8407 100644 --- a/cmd/helm/load_plugins.go +++ b/cmd/helm/load_plugins.go @@ -32,7 +32,6 @@ import ( "github.com/spf13/cobra" "sigs.k8s.io/yaml" - "helm.sh/helm/v3/internal/completion" "helm.sh/helm/v3/pkg/plugin" ) @@ -97,6 +96,7 @@ func loadPlugins(baseCmd *cobra.Command, out io.Writer) { // This passes all the flags to the subcommand. DisableFlagParsing: true, } + // TODO: Make sure a command with this name does not already exist. baseCmd.AddCommand(c) @@ -105,7 +105,7 @@ func loadPlugins(baseCmd *cobra.Command, out io.Writer) { // We only do this when necessary (for the "completion" and "__complete" commands) to avoid the // risk of a rogue plugin affecting Helm's normal behavior. subCmd, _, err := baseCmd.Find(os.Args[1:]) - if (err == nil && (subCmd.Name() == "completion" || subCmd.Name() == completion.CompRequestCmd)) || + if (err == nil && (subCmd.Name() == "completion" || subCmd.Name() == cobra.ShellCompRequestCmd)) || /* for the tests */ subCmd == baseCmd.Root() { loadCompletionForPlugin(c, plug) } @@ -247,9 +247,9 @@ func addPluginCommands(plugin *plugin.Plugin, baseCmd *cobra.Command, cmds *plug // Only setup dynamic completion if there are no sub-commands. This avoids // calling plugin.complete at every completion, which greatly simplifies // development of plugin.complete for plugin developers. - completion.RegisterValidArgsFunc(baseCmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { + baseCmd.ValidArgsFunction = func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return pluginDynamicComp(plugin, cmd, args, toComplete) - }) + } } // Create fake flags. @@ -322,12 +322,12 @@ func loadFile(path string) (*pluginCommand, error) { // pluginDynamicComp call the plugin.complete script of the plugin (if available) // to obtain the dynamic completion choices. It must pass all the flags and sub-commands // specified in the command-line to the plugin.complete executable (except helm's global flags) -func pluginDynamicComp(plug *plugin.Plugin, cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { +func pluginDynamicComp(plug *plugin.Plugin, cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { md := plug.Metadata u, err := processParent(cmd, args) if err != nil { - return nil, completion.BashCompDirectiveError + return nil, cobra.ShellCompDirectiveError } // We will call the dynamic completion script of the plugin @@ -343,12 +343,12 @@ func pluginDynamicComp(plug *plugin.Plugin, cmd *cobra.Command, args []string, t } plugin.SetupPluginEnv(settings, md.Name, plug.Dir) - completion.CompDebugln(fmt.Sprintf("calling %s with args %v", main, argv)) + cobra.CompDebugln(fmt.Sprintf("calling %s with args %v", main, argv), settings.Debug) buf := new(bytes.Buffer) if err := callPluginExecutable(md.Name, main, argv, buf); err != nil { // The dynamic completion file is optional for a plugin, so this error is ok. - completion.CompDebugln(fmt.Sprintf("Unable to call %s: %v", main, err.Error())) - return nil, completion.BashCompDirectiveDefault + cobra.CompDebugln(fmt.Sprintf("Unable to call %s: %v", main, err.Error()), settings.Debug) + return nil, cobra.ShellCompDirectiveDefault } var completions []string @@ -361,12 +361,12 @@ func pluginDynamicComp(plug *plugin.Plugin, cmd *cobra.Command, args []string, t // Check if the last line of output is of the form :, which // indicates the BashCompletionDirective. - directive := completion.BashCompDirectiveDefault + directive := cobra.ShellCompDirectiveDefault if len(completions) > 0 { lastLine := completions[len(completions)-1] if len(lastLine) > 1 && lastLine[0] == ':' { if strInt, err := strconv.Atoi(lastLine[1:]); err == nil { - directive = completion.BashCompDirective(strInt) + directive = cobra.ShellCompDirective(strInt) completions = completions[:len(completions)-1] } } diff --git a/cmd/helm/plugin_uninstall.go b/cmd/helm/plugin_uninstall.go index 66cdaccdc..b2290fb9b 100644 --- a/cmd/helm/plugin_uninstall.go +++ b/cmd/helm/plugin_uninstall.go @@ -24,7 +24,6 @@ import ( "github.com/pkg/errors" "github.com/spf13/cobra" - "helm.sh/helm/v3/internal/completion" "helm.sh/helm/v3/pkg/plugin" ) @@ -39,6 +38,12 @@ func newPluginUninstallCmd(out io.Writer) *cobra.Command { Use: "uninstall ...", Aliases: []string{"rm", "remove"}, Short: "uninstall one or more Helm plugins", + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + return compListPlugins(toComplete), cobra.ShellCompDirectiveNoFileComp + }, PreRunE: func(cmd *cobra.Command, args []string) error { return o.complete(args) }, @@ -46,15 +51,6 @@ func newPluginUninstallCmd(out io.Writer) *cobra.Command { return o.run(out) }, } - - // Function providing dynamic auto-completion - completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) != 0 { - return nil, completion.BashCompDirectiveNoFileComp - } - return compListPlugins(toComplete), completion.BashCompDirectiveNoFileComp - }) - return cmd } diff --git a/cmd/helm/plugin_update.go b/cmd/helm/plugin_update.go index 23f840b4a..c46444e0d 100644 --- a/cmd/helm/plugin_update.go +++ b/cmd/helm/plugin_update.go @@ -24,7 +24,6 @@ import ( "github.com/pkg/errors" "github.com/spf13/cobra" - "helm.sh/helm/v3/internal/completion" "helm.sh/helm/v3/pkg/plugin" "helm.sh/helm/v3/pkg/plugin/installer" ) @@ -40,6 +39,12 @@ func newPluginUpdateCmd(out io.Writer) *cobra.Command { Use: "update ...", Aliases: []string{"up"}, Short: "update one or more Helm plugins", + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + return compListPlugins(toComplete), cobra.ShellCompDirectiveNoFileComp + }, PreRunE: func(cmd *cobra.Command, args []string) error { return o.complete(args) }, @@ -47,15 +52,6 @@ func newPluginUpdateCmd(out io.Writer) *cobra.Command { return o.run(out) }, } - - // Function providing dynamic auto-completion - completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) != 0 { - return nil, completion.BashCompDirectiveNoFileComp - } - return compListPlugins(toComplete), completion.BashCompDirectiveNoFileComp - }) - return cmd } diff --git a/cmd/helm/pull.go b/cmd/helm/pull.go index 16cd10467..d10c629db 100644 --- a/cmd/helm/pull.go +++ b/cmd/helm/pull.go @@ -23,7 +23,6 @@ import ( "github.com/spf13/cobra" "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/internal/completion" "helm.sh/helm/v3/pkg/action" ) @@ -51,6 +50,12 @@ func newPullCmd(out io.Writer) *cobra.Command { Aliases: []string{"fetch"}, Long: pullDesc, Args: require.MinimumNArgs(1), + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + return compListCharts(toComplete, false) + }, RunE: func(cmd *cobra.Command, args []string) error { client.Settings = settings if client.Version == "" && client.Devel { @@ -69,14 +74,6 @@ func newPullCmd(out io.Writer) *cobra.Command { }, } - // Function providing dynamic auto-completion - completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) != 0 { - return nil, completion.BashCompDirectiveNoFileComp - } - return compListCharts(toComplete, false) - }) - f := cmd.Flags() f.BoolVar(&client.Devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.") f.BoolVar(&client.Untar, "untar", false, "if set to true, will untar the chart after downloading it") diff --git a/cmd/helm/release_testing.go b/cmd/helm/release_testing.go index 036d96794..37cac6186 100644 --- a/cmd/helm/release_testing.go +++ b/cmd/helm/release_testing.go @@ -24,7 +24,6 @@ import ( "github.com/spf13/cobra" "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/internal/completion" "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/cli/output" ) @@ -46,6 +45,12 @@ func newReleaseTestCmd(cfg *action.Configuration, out io.Writer) *cobra.Command Short: "run tests for a release", Long: releaseTestHelp, Args: require.ExactArgs(1), + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + return compListReleases(toComplete, cfg) + }, RunE: func(cmd *cobra.Command, args []string) error { client.Namespace = settings.Namespace() rel, runErr := client.Run(args[0]) @@ -72,14 +77,6 @@ func newReleaseTestCmd(cfg *action.Configuration, out io.Writer) *cobra.Command }, } - // Function providing dynamic auto-completion - completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) != 0 { - return nil, completion.BashCompDirectiveNoFileComp - } - return compListReleases(toComplete, cfg) - }) - f := cmd.Flags() f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)") f.BoolVar(&outputLogs, "logs", false, "dump the logs from test pods (this runs after all tests are complete, but before any cleanup)") diff --git a/cmd/helm/repo_remove.go b/cmd/helm/repo_remove.go index 5dad4e5e0..e6e9cb681 100644 --- a/cmd/helm/repo_remove.go +++ b/cmd/helm/repo_remove.go @@ -26,7 +26,6 @@ import ( "github.com/spf13/cobra" "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/internal/completion" "helm.sh/helm/v3/pkg/helmpath" "helm.sh/helm/v3/pkg/repo" ) @@ -45,6 +44,9 @@ func newRepoRemoveCmd(out io.Writer) *cobra.Command { Aliases: []string{"rm"}, Short: "remove one or more chart repositories", Args: require.MinimumNArgs(1), + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return compListRepos(toComplete, args), cobra.ShellCompDirectiveNoFileComp + }, RunE: func(cmd *cobra.Command, args []string) error { o.repoFile = settings.RepositoryConfig o.repoCache = settings.RepositoryCache @@ -52,12 +54,6 @@ func newRepoRemoveCmd(out io.Writer) *cobra.Command { return o.run(out) }, } - - // Function providing dynamic auto-completion - completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - return compListRepos(toComplete, args), completion.BashCompDirectiveNoFileComp - }) - return cmd } diff --git a/cmd/helm/rollback.go b/cmd/helm/rollback.go index 745e910b2..3b336d0ff 100644 --- a/cmd/helm/rollback.go +++ b/cmd/helm/rollback.go @@ -25,7 +25,6 @@ import ( "github.com/spf13/cobra" "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/internal/completion" "helm.sh/helm/v3/pkg/action" ) @@ -47,6 +46,12 @@ func newRollbackCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { Short: "roll back a release to a previous revision", Long: rollbackDesc, Args: require.MinimumNArgs(1), + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + return compListReleases(toComplete, cfg) + }, RunE: func(cmd *cobra.Command, args []string) error { if len(args) > 1 { ver, err := strconv.Atoi(args[1]) @@ -65,14 +70,6 @@ func newRollbackCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { }, } - // Function providing dynamic auto-completion - completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) != 0 { - return nil, completion.BashCompDirectiveNoFileComp - } - return compListReleases(toComplete, cfg) - }) - f := cmd.Flags() f.BoolVar(&client.DryRun, "dry-run", false, "simulate a rollback") f.BoolVar(&client.Recreate, "recreate-pods", false, "performs pods restart for the resource if applicable") diff --git a/cmd/helm/root.go b/cmd/helm/root.go index 143745f29..69f5855d9 100644 --- a/cmd/helm/root.go +++ b/cmd/helm/root.go @@ -20,6 +20,7 @@ import ( "context" "fmt" "io" + "log" "strings" "github.com/spf13/cobra" @@ -27,7 +28,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/tools/clientcmd" - "helm.sh/helm/v3/internal/completion" "helm.sh/helm/v3/internal/experimental/registry" "helm.sh/helm/v3/pkg/action" ) @@ -70,24 +70,22 @@ By default, the default directories depend on the Operating System. The defaults func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string) *cobra.Command { cmd := &cobra.Command{ - Use: "helm", - Short: "The Helm package manager for Kubernetes.", - Long: globalUsage, - SilenceUsage: true, - BashCompletionFunction: completion.GetBashCustomFunction(), + Use: "helm", + Short: "The Helm package manager for Kubernetes.", + Long: globalUsage, + SilenceUsage: true, } flags := cmd.PersistentFlags() settings.AddFlags(flags) // Setup shell completion for the namespace flag - flag := flags.Lookup("namespace") - completion.RegisterFlagCompletionFunc(flag, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { + err := cmd.RegisterFlagCompletionFunc("namespace", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if client, err := actionConfig.KubernetesClientSet(); err == nil { // Choose a long enough timeout that the user notices somethings is not working // but short enough that the user is not made to wait very long to := int64(3) - completion.CompDebugln(fmt.Sprintf("About to call kube client for namespaces with timeout of: %d", to)) + cobra.CompDebugln(fmt.Sprintf("About to call kube client for namespaces with timeout of: %d", to), settings.Debug) nsNames := []string{} if namespaces, err := client.CoreV1().Namespaces().List(context.Background(), metav1.ListOptions{TimeoutSeconds: &to}); err == nil { @@ -96,16 +94,19 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string nsNames = append(nsNames, ns.Name) } } - return nsNames, completion.BashCompDirectiveNoFileComp + return nsNames, cobra.ShellCompDirectiveNoFileComp } } - return nil, completion.BashCompDirectiveDefault + return nil, cobra.ShellCompDirectiveDefault }) + if err != nil { + log.Fatal(err) + } + // Setup shell completion for the kube-context flag - flag = flags.Lookup("kube-context") - completion.RegisterFlagCompletionFunc(flag, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - completion.CompDebugln("About to get the different kube-contexts") + err = cmd.RegisterFlagCompletionFunc("kube-context", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + cobra.CompDebugln("About to get the different kube-contexts", settings.Debug) loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() if len(settings.KubeConfig) > 0 { @@ -120,11 +121,15 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string ctxs = append(ctxs, name) } } - return ctxs, completion.BashCompDirectiveNoFileComp + return ctxs, cobra.ShellCompDirectiveNoFileComp } - return nil, completion.BashCompDirectiveNoFileComp + return nil, cobra.ShellCompDirectiveNoFileComp }) + if err != nil { + log.Fatal(err) + } + // We can safely ignore any errors that flags.Parse encounters since // those errors will be caught later during the call to cmd.Execution. // This call is required to gather configuration information prior to @@ -164,9 +169,6 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string // Hidden documentation generator command: 'helm docs' newDocsCmd(out), - - // Setup the special hidden __complete command to allow for dynamic auto-completion - completion.NewCompleteCmd(settings, out), ) // Add *experimental* subcommands diff --git a/cmd/helm/search_repo.go b/cmd/helm/search_repo.go index c7f0da974..dd530379b 100644 --- a/cmd/helm/search_repo.go +++ b/cmd/helm/search_repo.go @@ -32,7 +32,6 @@ import ( "github.com/spf13/cobra" "helm.sh/helm/v3/cmd/helm/search" - "helm.sh/helm/v3/internal/completion" "helm.sh/helm/v3/pkg/cli/output" "helm.sh/helm/v3/pkg/helmpath" "helm.sh/helm/v3/pkg/repo" @@ -290,8 +289,8 @@ func compListChartsOfRepo(repoName string, prefix string) []string { // Provide dynamic auto-completion for commands that operate on charts (e.g., helm show) // When true, the includeFiles argument indicates that completion should include local files (e.g., local charts) -func compListCharts(toComplete string, includeFiles bool) ([]string, completion.BashCompDirective) { - completion.CompDebugln(fmt.Sprintf("compListCharts with toComplete %s", toComplete)) +func compListCharts(toComplete string, includeFiles bool) ([]string, cobra.ShellCompDirective) { + cobra.CompDebugln(fmt.Sprintf("compListCharts with toComplete %s", toComplete), settings.Debug) noSpace := false noFile := false @@ -312,7 +311,7 @@ func compListCharts(toComplete string, includeFiles bool) ([]string, completion. noSpace = true } } - completion.CompDebugln(fmt.Sprintf("Completions after repos: %v", completions)) + cobra.CompDebugln(fmt.Sprintf("Completions after repos: %v", completions), settings.Debug) // Now handle completions for url prefixes for _, url := range []string{"https://", "http://", "file://"} { @@ -328,7 +327,7 @@ func compListCharts(toComplete string, includeFiles bool) ([]string, completion. noSpace = true } } - completion.CompDebugln(fmt.Sprintf("Completions after urls: %v", completions)) + cobra.CompDebugln(fmt.Sprintf("Completions after urls: %v", completions), settings.Debug) // Finally, provide file completion if we need to. // We only do this if: @@ -347,22 +346,22 @@ func compListCharts(toComplete string, includeFiles bool) ([]string, completion. } } } - completion.CompDebugln(fmt.Sprintf("Completions after files: %v", completions)) + cobra.CompDebugln(fmt.Sprintf("Completions after files: %v", completions), settings.Debug) // If the user didn't provide any input to completion, // we provide a hint that a path can also be used if includeFiles && len(toComplete) == 0 { completions = append(completions, "./", "/") } - completion.CompDebugln(fmt.Sprintf("Completions after checking empty input: %v", completions)) + cobra.CompDebugln(fmt.Sprintf("Completions after checking empty input: %v", completions), settings.Debug) - directive := completion.BashCompDirectiveDefault + directive := cobra.ShellCompDirectiveDefault if noFile { - directive = directive | completion.BashCompDirectiveNoFileComp + directive = directive | cobra.ShellCompDirectiveNoFileComp } if noSpace { - directive = directive | completion.BashCompDirectiveNoSpace - // The completion.BashCompDirective flags do not work for zsh right now. + directive = directive | cobra.ShellCompDirectiveNoSpace + // The cobra.ShellCompDirective flags do not work for zsh right now. // We handle it ourselves instead. completions = compEnforceNoSpace(completions) } @@ -380,7 +379,7 @@ func compEnforceNoSpace(completions []string) []string { // We only do this if there is a single choice for completion. if len(completions) == 1 { completions = append(completions, completions[0]+".") - completion.CompDebugln(fmt.Sprintf("compEnforceNoSpace: completions now are %v", completions)) + cobra.CompDebugln(fmt.Sprintf("compEnforceNoSpace: completions now are %v", completions), settings.Debug) } return completions } diff --git a/cmd/helm/show.go b/cmd/helm/show.go index 91c35a53c..c78069a09 100644 --- a/cmd/helm/show.go +++ b/cmd/helm/show.go @@ -23,7 +23,6 @@ import ( "github.com/spf13/cobra" "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/internal/completion" "helm.sh/helm/v3/pkg/action" ) @@ -63,18 +62,19 @@ func newShowCmd(out io.Writer) *cobra.Command { } // Function providing dynamic auto-completion - validArgsFunc := func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { + validArgsFunc := func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) != 0 { - return nil, completion.BashCompDirectiveNoFileComp + return nil, cobra.ShellCompDirectiveNoFileComp } return compListCharts(toComplete, true) } all := &cobra.Command{ - Use: "all [CHART]", - Short: "show all information of the chart", - Long: showAllDesc, - Args: require.ExactArgs(1), + Use: "all [CHART]", + Short: "show all information of the chart", + Long: showAllDesc, + Args: require.ExactArgs(1), + ValidArgsFunction: validArgsFunc, RunE: func(cmd *cobra.Command, args []string) error { client.OutputFormat = action.ShowAll output, err := runShow(args, client) @@ -87,10 +87,11 @@ func newShowCmd(out io.Writer) *cobra.Command { } valuesSubCmd := &cobra.Command{ - Use: "values [CHART]", - Short: "show the chart's values", - Long: showValuesDesc, - Args: require.ExactArgs(1), + Use: "values [CHART]", + Short: "show the chart's values", + Long: showValuesDesc, + Args: require.ExactArgs(1), + ValidArgsFunction: validArgsFunc, RunE: func(cmd *cobra.Command, args []string) error { client.OutputFormat = action.ShowValues output, err := runShow(args, client) @@ -103,10 +104,11 @@ func newShowCmd(out io.Writer) *cobra.Command { } chartSubCmd := &cobra.Command{ - Use: "chart [CHART]", - Short: "show the chart's definition", - Long: showChartDesc, - Args: require.ExactArgs(1), + Use: "chart [CHART]", + Short: "show the chart's definition", + Long: showChartDesc, + Args: require.ExactArgs(1), + ValidArgsFunction: validArgsFunc, RunE: func(cmd *cobra.Command, args []string) error { client.OutputFormat = action.ShowChart output, err := runShow(args, client) @@ -119,10 +121,11 @@ func newShowCmd(out io.Writer) *cobra.Command { } readmeSubCmd := &cobra.Command{ - Use: "readme [CHART]", - Short: "show the chart's README", - Long: readmeChartDesc, - Args: require.ExactArgs(1), + Use: "readme [CHART]", + Short: "show the chart's README", + Long: readmeChartDesc, + Args: require.ExactArgs(1), + ValidArgsFunction: validArgsFunc, RunE: func(cmd *cobra.Command, args []string) error { client.OutputFormat = action.ShowReadme output, err := runShow(args, client) @@ -138,9 +141,6 @@ func newShowCmd(out io.Writer) *cobra.Command { for _, subCmd := range cmds { addShowFlags(subCmd, client) showCommand.AddCommand(subCmd) - - // Register the completion function for each subcommand - completion.RegisterValidArgsFunc(subCmd, validArgsFunc) } return showCommand diff --git a/cmd/helm/status.go b/cmd/helm/status.go index 2efb2006c..5913d08df 100644 --- a/cmd/helm/status.go +++ b/cmd/helm/status.go @@ -19,13 +19,13 @@ package main import ( "fmt" "io" + "log" "strings" "time" "github.com/spf13/cobra" "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/internal/completion" "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/chartutil" "helm.sh/helm/v3/pkg/cli/output" @@ -53,6 +53,12 @@ func newStatusCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { Short: "display the status of the named release", Long: statusHelp, Args: require.ExactArgs(1), + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + return compListReleases(toComplete, cfg) + }, RunE: func(cmd *cobra.Command, args []string) error { rel, err := client.Run(args[0]) if err != nil { @@ -66,25 +72,21 @@ func newStatusCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { }, } - // Function providing dynamic auto-completion - completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) != 0 { - return nil, completion.BashCompDirectiveNoFileComp - } - return compListReleases(toComplete, cfg) - }) - f := cmd.Flags() f.IntVar(&client.Version, "revision", 0, "if set, display the status of the named release with revision") - flag := f.Lookup("revision") - completion.RegisterFlagCompletionFunc(flag, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { + + err := cmd.RegisterFlagCompletionFunc("revision", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) == 1 { return compListRevisions(cfg, args[0]) } - return nil, completion.BashCompDirectiveNoFileComp + return nil, cobra.ShellCompDirectiveNoFileComp }) + if err != nil { + log.Fatal(err) + } + bindOutputFlag(cmd, &outfmt) return cmd diff --git a/cmd/helm/template.go b/cmd/helm/template.go index a4438b50c..0fc29e189 100644 --- a/cmd/helm/template.go +++ b/cmd/helm/template.go @@ -28,7 +28,6 @@ import ( "github.com/spf13/cobra" "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/internal/completion" "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/chartutil" "helm.sh/helm/v3/pkg/cli/values" @@ -56,6 +55,9 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { Short: "locally render templates", Long: templateDesc, Args: require.MinimumNArgs(1), + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return compInstall(args, toComplete, client) + }, RunE: func(_ *cobra.Command, args []string) error { client.DryRun = true client.ReleaseName = "RELEASE-NAME" @@ -136,11 +138,6 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { }, } - // Function providing dynamic auto-completion - completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - return compInstall(args, toComplete, client) - }) - f := cmd.Flags() addInstallFlags(f, client, valueOpts) f.StringArrayVarP(&showFiles, "show-only", "s", []string{}, "only show manifests rendered from the given templates") diff --git a/cmd/helm/testdata/output/output-comp.txt b/cmd/helm/testdata/output/output-comp.txt index de5f16f1d..be574756b 100644 --- a/cmd/helm/testdata/output/output-comp.txt +++ b/cmd/helm/testdata/output/output-comp.txt @@ -2,3 +2,4 @@ table json yaml :0 +Completion ended with directive: ShellCompDirectiveDefault diff --git a/cmd/helm/testdata/output/plugin_args_comp.txt b/cmd/helm/testdata/output/plugin_args_comp.txt index 8fb01cc23..007112d31 100644 --- a/cmd/helm/testdata/output/plugin_args_comp.txt +++ b/cmd/helm/testdata/output/plugin_args_comp.txt @@ -3,3 +3,4 @@ Namespace: default Num args received: 1 Args received: :4 +Completion ended with directive: ShellCompDirectiveNoFileComp diff --git a/cmd/helm/testdata/output/plugin_args_flag_comp.txt b/cmd/helm/testdata/output/plugin_args_flag_comp.txt index 92f0e58a8..c7a09e3fa 100644 --- a/cmd/helm/testdata/output/plugin_args_flag_comp.txt +++ b/cmd/helm/testdata/output/plugin_args_flag_comp.txt @@ -3,3 +3,4 @@ Namespace: default Num args received: 2 Args received: --myflag :4 +Completion ended with directive: ShellCompDirectiveNoFileComp diff --git a/cmd/helm/testdata/output/plugin_args_many_args_comp.txt b/cmd/helm/testdata/output/plugin_args_many_args_comp.txt index 86fa768bb..f3c386b6d 100644 --- a/cmd/helm/testdata/output/plugin_args_many_args_comp.txt +++ b/cmd/helm/testdata/output/plugin_args_many_args_comp.txt @@ -3,3 +3,4 @@ Namespace: mynamespace Num args received: 2 Args received: --myflag start :2 +Completion ended with directive: ShellCompDirectiveNoSpace diff --git a/cmd/helm/testdata/output/plugin_args_ns_comp.txt b/cmd/helm/testdata/output/plugin_args_ns_comp.txt index e12867daa..26cd79b98 100644 --- a/cmd/helm/testdata/output/plugin_args_ns_comp.txt +++ b/cmd/helm/testdata/output/plugin_args_ns_comp.txt @@ -3,3 +3,4 @@ Namespace: mynamespace Num args received: 1 Args received: :2 +Completion ended with directive: ShellCompDirectiveNoSpace diff --git a/cmd/helm/testdata/output/plugin_echo_bad_directive.txt b/cmd/helm/testdata/output/plugin_echo_bad_directive.txt index f4b86cd47..8038b9525 100644 --- a/cmd/helm/testdata/output/plugin_echo_bad_directive.txt +++ b/cmd/helm/testdata/output/plugin_echo_bad_directive.txt @@ -3,3 +3,4 @@ Namespace: default Num args received: 1 Args received: :0 +Completion ended with directive: ShellCompDirectiveDefault diff --git a/cmd/helm/testdata/output/plugin_echo_no_directive.txt b/cmd/helm/testdata/output/plugin_echo_no_directive.txt index 6266dd4d9..7001be0e9 100644 --- a/cmd/helm/testdata/output/plugin_echo_no_directive.txt +++ b/cmd/helm/testdata/output/plugin_echo_no_directive.txt @@ -3,3 +3,4 @@ Namespace: mynamespace Num args received: 1 Args received: :0 +Completion ended with directive: ShellCompDirectiveDefault diff --git a/cmd/helm/testdata/output/revision-comp.txt b/cmd/helm/testdata/output/revision-comp.txt index b4799f059..d06947d9d 100644 --- a/cmd/helm/testdata/output/revision-comp.txt +++ b/cmd/helm/testdata/output/revision-comp.txt @@ -3,3 +3,4 @@ 10 11 :0 +Completion ended with directive: ShellCompDirectiveDefault diff --git a/cmd/helm/testdata/output/revision-wrong-args-comp.txt b/cmd/helm/testdata/output/revision-wrong-args-comp.txt index b6f867176..8d9fad576 100644 --- a/cmd/helm/testdata/output/revision-wrong-args-comp.txt +++ b/cmd/helm/testdata/output/revision-wrong-args-comp.txt @@ -1 +1,2 @@ :4 +Completion ended with directive: ShellCompDirectiveNoFileComp diff --git a/cmd/helm/testdata/output/status-comp.txt b/cmd/helm/testdata/output/status-comp.txt index c97882964..8d4d21df7 100644 --- a/cmd/helm/testdata/output/status-comp.txt +++ b/cmd/helm/testdata/output/status-comp.txt @@ -1,3 +1,4 @@ aramis athos :4 +Completion ended with directive: ShellCompDirectiveNoFileComp diff --git a/cmd/helm/testdata/output/status-wrong-args-comp.txt b/cmd/helm/testdata/output/status-wrong-args-comp.txt index b6f867176..8d9fad576 100644 --- a/cmd/helm/testdata/output/status-wrong-args-comp.txt +++ b/cmd/helm/testdata/output/status-wrong-args-comp.txt @@ -1 +1,2 @@ :4 +Completion ended with directive: ShellCompDirectiveNoFileComp diff --git a/cmd/helm/uninstall.go b/cmd/helm/uninstall.go index 85fa822bd..509918e53 100644 --- a/cmd/helm/uninstall.go +++ b/cmd/helm/uninstall.go @@ -24,7 +24,6 @@ import ( "github.com/spf13/cobra" "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/internal/completion" "helm.sh/helm/v3/pkg/action" ) @@ -48,6 +47,12 @@ func newUninstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { Short: "uninstall a release", Long: uninstallDesc, Args: require.MinimumNArgs(1), + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + return compListReleases(toComplete, cfg) + }, RunE: func(cmd *cobra.Command, args []string) error { for i := 0; i < len(args); i++ { @@ -65,14 +70,6 @@ func newUninstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { }, } - // Function providing dynamic auto-completion - completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) != 0 { - return nil, completion.BashCompDirectiveNoFileComp - } - return compListReleases(toComplete, cfg) - }) - f := cmd.Flags() f.BoolVar(&client.DryRun, "dry-run", false, "simulate a uninstall") f.BoolVar(&client.DisableHooks, "no-hooks", false, "prevent hooks from running during uninstallation") diff --git a/cmd/helm/upgrade.go b/cmd/helm/upgrade.go index f8103b485..1db899eae 100644 --- a/cmd/helm/upgrade.go +++ b/cmd/helm/upgrade.go @@ -25,7 +25,6 @@ import ( "github.com/spf13/cobra" "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/internal/completion" "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/chart/loader" "helm.sh/helm/v3/pkg/cli/output" @@ -72,6 +71,15 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { Short: "upgrade a release", Long: upgradeDesc, Args: require.ExactArgs(2), + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) == 0 { + return compListReleases(toComplete, cfg) + } + if len(args) == 1 { + return compListCharts(toComplete, true) + } + return nil, cobra.ShellCompDirectiveNoFileComp + }, RunE: func(cmd *cobra.Command, args []string) error { client.Namespace = settings.Namespace() @@ -155,17 +163,6 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { }, } - // Function providing dynamic auto-completion - completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) == 0 { - return compListReleases(toComplete, cfg) - } - if len(args) == 1 { - return compListCharts(toComplete, true) - } - return nil, completion.BashCompDirectiveNoFileComp - }) - f := cmd.Flags() f.BoolVar(&createNamespace, "create-namespace", false, "if --install is set, create the release namespace if not present") f.BoolVarP(&client.Install, "install", "i", false, "if a release by this name doesn't already exist, run an install") diff --git a/internal/completion/complete.go b/internal/completion/complete.go deleted file mode 100644 index 545f5b0dd..000000000 --- a/internal/completion/complete.go +++ /dev/null @@ -1,399 +0,0 @@ -/* -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 completion - -import ( - "errors" - "fmt" - "io" - "log" - "os" - "strings" - - "github.com/spf13/cobra" - "github.com/spf13/pflag" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/pkg/cli" -) - -// ================================================================================== -// The below code supports dynamic shell completion in Go. -// This should ultimately be pushed down into Cobra. -// ================================================================================== - -// CompRequestCmd Hidden command to request completion results from the program. -// Used by the shell completion script. -const CompRequestCmd = "__complete" - -// Global map allowing to find completion functions for commands or flags. -var validArgsFunctions = map[interface{}]func(cmd *cobra.Command, args []string, toComplete string) ([]string, BashCompDirective){} - -// BashCompDirective is a bit map representing the different behaviors the shell -// can be instructed to have once completions have been provided. -type BashCompDirective int - -const ( - // BashCompDirectiveError indicates an error occurred and completions should be ignored. - BashCompDirectiveError BashCompDirective = 1 << iota - - // BashCompDirectiveNoSpace indicates that the shell should not add a space - // after the completion even if there is a single completion provided. - BashCompDirectiveNoSpace - - // BashCompDirectiveNoFileComp indicates that the shell should not provide - // file completion even when no completion is provided. - // This currently does not work for zsh or bash < 4 - BashCompDirectiveNoFileComp - - // BashCompDirectiveDefault indicates to let the shell perform its default - // behavior after completions have been provided. - BashCompDirectiveDefault BashCompDirective = 0 -) - -// GetBashCustomFunction returns the bash code to handle custom go completion -// This should eventually be provided by Cobra -func GetBashCustomFunction() string { - return fmt.Sprintf(` -__helm_custom_func() -{ - __helm_debug "${FUNCNAME[0]}: c is $c, words[@] is ${words[@]}, #words[@] is ${#words[@]}" - __helm_debug "${FUNCNAME[0]}: cur is ${cur}, cword is ${cword}, words is ${words}" - - local out requestComp lastParam lastChar - requestComp="${words[0]} %[1]s ${words[@]:1}" - - lastParam=${words[$((${#words[@]}-1))]} - lastChar=${lastParam:$((${#lastParam}-1)):1} - __helm_debug "${FUNCNAME[0]}: lastParam ${lastParam}, lastChar ${lastChar}" - - if [ -z "${cur}" ] && [ "${lastChar}" != "=" ]; then - # If the last parameter is complete (there is a space following it) - # We add an extra empty parameter so we can indicate this to the go method. - __helm_debug "${FUNCNAME[0]}: Adding extra empty parameter" - requestComp="${requestComp} \"\"" - fi - - __helm_debug "${FUNCNAME[0]}: calling ${requestComp}" - # Use eval to handle any environment variables and such - out=$(eval ${requestComp} 2>/dev/null) - - # Extract the directive int at the very end of the output following a : - directive=${out##*:} - # Remove the directive - out=${out%%:*} - if [ "${directive}" = "${out}" ]; then - # There is not directive specified - directive=0 - fi - __helm_debug "${FUNCNAME[0]}: the completion directive is: ${directive}" - __helm_debug "${FUNCNAME[0]}: the completions are: ${out[*]}" - - if [ $((${directive} & %[2]d)) -ne 0 ]; then - __helm_debug "${FUNCNAME[0]}: received error, completion failed" - else - if [ $((${directive} & %[3]d)) -ne 0 ]; then - if [[ $(type -t compopt) = "builtin" ]]; then - __helm_debug "${FUNCNAME[0]}: activating no space" - compopt -o nospace - fi - fi - if [ $((${directive} & %[4]d)) -ne 0 ]; then - if [[ $(type -t compopt) = "builtin" ]]; then - __helm_debug "${FUNCNAME[0]}: activating no file completion" - compopt +o default - fi - fi - - while IFS='' read -r comp; do - COMPREPLY+=("$comp") - done < <(compgen -W "${out[*]}" -- "$cur") - fi -} -`, CompRequestCmd, BashCompDirectiveError, BashCompDirectiveNoSpace, BashCompDirectiveNoFileComp) -} - -// RegisterValidArgsFunc should be called to register a function to provide argument completion for a command -func RegisterValidArgsFunc(cmd *cobra.Command, f func(cmd *cobra.Command, args []string, toComplete string) ([]string, BashCompDirective)) { - if _, exists := validArgsFunctions[cmd]; exists { - log.Fatal(fmt.Sprintf("RegisterValidArgsFunc: command '%s' already registered", cmd.Name())) - } - validArgsFunctions[cmd] = f -} - -// RegisterFlagCompletionFunc should be called to register a function to provide completion for a flag -func RegisterFlagCompletionFunc(flag *pflag.Flag, f func(cmd *cobra.Command, args []string, toComplete string) ([]string, BashCompDirective)) { - if _, exists := validArgsFunctions[flag]; exists { - log.Fatal(fmt.Sprintf("RegisterFlagCompletionFunc: flag '%s' already registered", flag.Name)) - } - validArgsFunctions[flag] = f - - // Make sure the completion script call the __helm_custom_func for the registered flag. - // This is essential to make the = form work. E.g., helm -n= or helm status --output= - if flag.Annotations == nil { - flag.Annotations = map[string][]string{} - } - flag.Annotations[cobra.BashCompCustom] = []string{"__helm_custom_func"} -} - -var debug = true - -// Returns a string listing the different directive enabled in the specified parameter -func (d BashCompDirective) string() string { - var directives []string - if d&BashCompDirectiveError != 0 { - directives = append(directives, "BashCompDirectiveError") - } - if d&BashCompDirectiveNoSpace != 0 { - directives = append(directives, "BashCompDirectiveNoSpace") - } - if d&BashCompDirectiveNoFileComp != 0 { - directives = append(directives, "BashCompDirectiveNoFileComp") - } - if len(directives) == 0 { - directives = append(directives, "BashCompDirectiveDefault") - } - - if d > BashCompDirectiveError+BashCompDirectiveNoSpace+BashCompDirectiveNoFileComp { - return fmt.Sprintf("ERROR: unexpected BashCompDirective value: %d", d) - } - return strings.Join(directives, ", ") -} - -// NewCompleteCmd add a special hidden command that an be used to request completions -func NewCompleteCmd(settings *cli.EnvSettings, out io.Writer) *cobra.Command { - debug = settings.Debug - return &cobra.Command{ - Use: fmt.Sprintf("%s [command-line]", CompRequestCmd), - DisableFlagsInUseLine: true, - Hidden: true, - DisableFlagParsing: true, - Args: require.MinimumNArgs(1), - Short: "Request shell completion choices for the specified command-line", - Long: fmt.Sprintf("%s is a special command that is used by the shell completion logic\n%s", - CompRequestCmd, "to request completion choices for the specified command-line."), - Run: func(cmd *cobra.Command, args []string) { - CompDebugln(fmt.Sprintf("%s was called with args %v", cmd.Name(), args)) - - // The last argument, which is not complete, should not be part of the list of arguments - toComplete := args[len(args)-1] - trimmedArgs := args[:len(args)-1] - - // Find the real command for which completion must be performed - finalCmd, finalArgs, err := cmd.Root().Find(trimmedArgs) - if err != nil { - // Unable to find the real command. E.g., helm invalidCmd - CompDebugln(fmt.Sprintf("Unable to find a command for arguments: %v", trimmedArgs)) - return - } - - CompDebugln(fmt.Sprintf("Found final command '%s', with finalArgs %v", finalCmd.Name(), finalArgs)) - - var flag *pflag.Flag - if !finalCmd.DisableFlagParsing { - // We only do flag completion if we are allowed to parse flags - // This is important for helm plugins which need to do their own flag completion. - flag, finalArgs, toComplete, err = checkIfFlagCompletion(finalCmd, finalArgs, toComplete) - if err != nil { - // Error while attempting to parse flags - CompErrorln(err.Error()) - return - } - } - - // Parse the flags and extract the arguments to prepare for calling the completion function - if err = finalCmd.ParseFlags(finalArgs); err != nil { - CompErrorln(fmt.Sprintf("Error while parsing flags from args %v: %s", finalArgs, err.Error())) - return - } - - // We only remove the flags from the arguments if DisableFlagParsing is not set. - // This is important for helm plugins, which need to receive all flags. - // The plugin completion code will do its own flag parsing. - if !finalCmd.DisableFlagParsing { - finalArgs = finalCmd.Flags().Args() - CompDebugln(fmt.Sprintf("Args without flags are '%v' with length %d", finalArgs, len(finalArgs))) - } - - // Find completion function for the flag or command - var key interface{} - var keyStr string - if flag != nil { - key = flag - keyStr = flag.Name - } else { - key = finalCmd - keyStr = finalCmd.Name() - } - completionFn, ok := validArgsFunctions[key] - if !ok { - CompErrorln(fmt.Sprintf("Dynamic completion not supported/needed for flag or command: %s", keyStr)) - return - } - - CompDebugln(fmt.Sprintf("Calling completion method for subcommand '%s' with args '%v' and toComplete '%s'", finalCmd.Name(), finalArgs, toComplete)) - completions, directive := completionFn(finalCmd, finalArgs, toComplete) - for _, comp := range completions { - // Print each possible completion to stdout for the completion script to consume. - fmt.Fprintln(out, comp) - } - - if directive > BashCompDirectiveError+BashCompDirectiveNoSpace+BashCompDirectiveNoFileComp { - directive = BashCompDirectiveDefault - } - - // As the last printout, print the completion directive for the - // completion script to parse. - // The directive integer must be that last character following a single : - // The completion script expects :directive - fmt.Fprintf(out, ":%d\n", directive) - - // Print some helpful info to stderr for the user to understand. - // Output from stderr should be ignored from the completion script. - fmt.Fprintf(os.Stderr, "Completion ended with directive: %s\n", directive.string()) - }, - } -} - -func isFlag(arg string) bool { - return len(arg) > 0 && arg[0] == '-' -} - -func checkIfFlagCompletion(finalCmd *cobra.Command, args []string, lastArg string) (*pflag.Flag, []string, string, error) { - var flagName string - trimmedArgs := args - flagWithEqual := false - if isFlag(lastArg) { - if index := strings.Index(lastArg, "="); index >= 0 { - flagName = strings.TrimLeft(lastArg[:index], "-") - lastArg = lastArg[index+1:] - flagWithEqual = true - } else { - return nil, nil, "", errors.New("Unexpected completion request for flag") - } - } - - if len(flagName) == 0 { - if len(args) > 0 { - prevArg := args[len(args)-1] - if isFlag(prevArg) { - // If the flag contains an = it means it has already been fully processed - if index := strings.Index(prevArg, "="); index < 0 { - flagName = strings.TrimLeft(prevArg, "-") - - // Remove the uncompleted flag or else Cobra could complain about - // an invalid value for that flag e.g., helm status --output j - trimmedArgs = args[:len(args)-1] - } - } - } - } - - if len(flagName) == 0 { - // Not doing flag completion - return nil, trimmedArgs, lastArg, nil - } - - flag := findFlag(finalCmd, flagName) - if flag == nil { - // Flag not supported by this command, nothing to complete - err := fmt.Errorf("Subcommand '%s' does not support flag '%s'", finalCmd.Name(), flagName) - return nil, nil, "", err - } - - if !flagWithEqual { - if len(flag.NoOptDefVal) != 0 { - // We had assumed dealing with a two-word flag but the flag is a boolean flag. - // In that case, there is no value following it, so we are not really doing flag completion. - // Reset everything to do argument completion. - trimmedArgs = args - flag = nil - } - } - - return flag, trimmedArgs, lastArg, nil -} - -func findFlag(cmd *cobra.Command, name string) *pflag.Flag { - flagSet := cmd.Flags() - if len(name) == 1 { - // First convert the short flag into a long flag - // as the cmd.Flag() search only accepts long flags - if short := flagSet.ShorthandLookup(name); short != nil { - CompDebugln(fmt.Sprintf("checkIfFlagCompletion: found flag '%s' which we will change to '%s'", name, short.Name)) - name = short.Name - } else { - set := cmd.InheritedFlags() - if short = set.ShorthandLookup(name); short != nil { - CompDebugln(fmt.Sprintf("checkIfFlagCompletion: found inherited flag '%s' which we will change to '%s'", name, short.Name)) - name = short.Name - } else { - return nil - } - } - } - return cmd.Flag(name) -} - -// CompDebug prints the specified string to the same file as where the -// completion script prints its logs. -// Note that completion printouts should never be on stdout as they would -// be wrongly interpreted as actual completion choices by the completion script. -func CompDebug(msg string) { - msg = fmt.Sprintf("[Debug] %s", msg) - - // Such logs are only printed when the user has set the environment - // variable BASH_COMP_DEBUG_FILE to the path of some file to be used. - if path := os.Getenv("BASH_COMP_DEBUG_FILE"); path != "" { - f, err := os.OpenFile(path, - os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) - if err == nil { - defer f.Close() - f.WriteString(msg) - } - } - - if debug { - // Must print to stderr for this not to be read by the completion script. - fmt.Fprintln(os.Stderr, msg) - } -} - -// CompDebugln prints the specified string with a newline at the end -// to the same file as where the completion script prints its logs. -// Such logs are only printed when the user has set the environment -// variable BASH_COMP_DEBUG_FILE to the path of some file to be used. -func CompDebugln(msg string) { - CompDebug(fmt.Sprintf("%s\n", msg)) -} - -// CompError prints the specified completion message to stderr. -func CompError(msg string) { - msg = fmt.Sprintf("[Error] %s", msg) - - CompDebug(msg) - - // If not already printed by the call to CompDebug(). - if !debug { - // Must print to stderr for this not to be read by the completion script. - fmt.Fprintln(os.Stderr, msg) - } -} - -// CompErrorln prints the specified completion message to stderr with a newline at the end. -func CompErrorln(msg string) { - CompError(fmt.Sprintf("%s\n", msg)) -} From 9249530b7739a738e16d5e46195c82df63f00127 Mon Sep 17 00:00:00 2001 From: ZouYu Date: Fri, 12 Jun 2020 09:29:58 +0800 Subject: [PATCH 141/281] Add unit test case Signed-off-by: ZouYu --- internal/urlutil/urlutil_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/urlutil/urlutil_test.go b/internal/urlutil/urlutil_test.go index 8e99c1bfb..82acc40fe 100644 --- a/internal/urlutil/urlutil_test.go +++ b/internal/urlutil/urlutil_test.go @@ -56,6 +56,9 @@ func TestEqual(t *testing.T) { {"/foo", "/foo", true}, {"/foo", "/foo/", true}, {"/foo/.", "/foo/", true}, + {"%/1234", "%/1234", true}, + {"%/1234", "%/123", false}, + {"/1234", "%/1234", false}, } { if tt.match != Equal(tt.a, tt.b) { t.Errorf("Expected %q==%q to be %t", tt.a, tt.b, tt.match) From aa033196692ea9c4416fbb3859bebf05aded6bef Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Fri, 12 Jun 2020 08:39:26 -0700 Subject: [PATCH 142/281] fix(chartutil): do not set helpers.tpl filetype for vim Helpers filetype is not mustache Signed-off-by: Adam Reese --- pkg/chartutil/create.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/chartutil/create.go b/pkg/chartutil/create.go index 0e87c7b47..e74761b3f 100644 --- a/pkg/chartutil/create.go +++ b/pkg/chartutil/create.go @@ -386,8 +386,7 @@ const defaultNotes = `1. Get the application URL by running these commands: {{- end }} ` -const defaultHelpers = `{{/* vim: set filetype=mustache: */}} -{{/* +const defaultHelpers = `{{/* Expand the name of the chart. */}} {{- define ".name" -}} From f7c882d55e26958d03c34c9718a964b9a8180da2 Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Sun, 14 Jun 2020 11:29:29 -0400 Subject: [PATCH 143/281] feat(cmd): Subcommands for the completion command Making each shell a subcommand of the 'completion' has multiple advantages: - simplifies the code, - allows to have different flags for each shell, for example, a future `--no-descriptions` flag for fish only, - allows to tailor the help text per shell. Signed-off-by: Marc Khouzam --- cmd/helm/completion.go | 81 ++++++++++++++++++++++------------------ cmd/helm/load_plugins.go | 3 +- 2 files changed, 47 insertions(+), 37 deletions(-) diff --git a/cmd/helm/completion.go b/cmd/helm/completion.go index c1f7790bc..696021363 100644 --- a/cmd/helm/completion.go +++ b/cmd/helm/completion.go @@ -21,61 +21,70 @@ import ( "os" "path/filepath" - "github.com/pkg/errors" "github.com/spf13/cobra" + + "helm.sh/helm/v3/cmd/helm/require" ) const completionDesc = ` -Generate autocompletions script for Helm for the specified shell (bash or zsh). +Generate autocompletions script for Helm for the specified shell. +` +const bashCompDesc = ` +Generate the autocompletion script for Helm for the bash shell. -This command can generate shell autocompletions. e.g. +To load completions in your current shell session: +$ source <(helm completion bash) - $ helm completion bash +To load completions for every new session, execute once: +Linux: + $ helm completion bash > /etc/bash_completion.d/helm +MacOS: + $ helm completion bash > /usr/local/etc/bash_completion.d/helm +` -Can be sourced as such +const zshCompDesc = ` +Generate the autocompletion script for Helm for the zsh shell. - $ source <(helm completion bash) -` +To load completions in your current shell session: +$ source <(helm completion zsh) -var ( - completionShells = map[string]func(out io.Writer, cmd *cobra.Command) error{ - "bash": runCompletionBash, - "zsh": runCompletionZsh, - } -) +To load completions for every new session, execute once: +$ helm completion zsh > "${fpath[1]}/_helm" +` func newCompletionCmd(out io.Writer) *cobra.Command { - shells := []string{} - for s := range completionShells { - shells = append(shells, s) - } - cmd := &cobra.Command{ - Use: "completion SHELL", - Short: "generate autocompletions script for the specified shell (bash or zsh)", + Use: "completion", + Short: "generate autocompletions script for the specified shell", Long: completionDesc, + Args: require.NoArgs, + } + + bash := &cobra.Command{ + Use: "bash", + Short: "generate autocompletions script for bash", + Long: bashCompDesc, + Args: require.NoArgs, + DisableFlagsInUseLine: true, RunE: func(cmd *cobra.Command, args []string) error { - return runCompletion(out, cmd, args) + return runCompletionBash(out, cmd) }, - ValidArgs: shells, } - return cmd -} - -func runCompletion(out io.Writer, cmd *cobra.Command, args []string) error { - if len(args) == 0 { - return errors.New("shell not specified") - } - if len(args) > 1 { - return errors.New("too many arguments, expected only the shell type") - } - run, found := completionShells[args[0]] - if !found { - return errors.Errorf("unsupported shell type %q", args[0]) + zsh := &cobra.Command{ + Use: "zsh", + Short: "generate autocompletions script for zsh", + Long: zshCompDesc, + Args: require.NoArgs, + DisableFlagsInUseLine: true, + RunE: func(cmd *cobra.Command, args []string) error { + return runCompletionZsh(out, cmd) + }, } - return run(out, cmd) + cmd.AddCommand(bash, zsh) + + return cmd } func runCompletionBash(out io.Writer, cmd *cobra.Command) error { diff --git a/cmd/helm/load_plugins.go b/cmd/helm/load_plugins.go index e439c8407..a6e0c4eae 100644 --- a/cmd/helm/load_plugins.go +++ b/cmd/helm/load_plugins.go @@ -105,7 +105,8 @@ func loadPlugins(baseCmd *cobra.Command, out io.Writer) { // We only do this when necessary (for the "completion" and "__complete" commands) to avoid the // risk of a rogue plugin affecting Helm's normal behavior. subCmd, _, err := baseCmd.Find(os.Args[1:]) - if (err == nil && (subCmd.Name() == "completion" || subCmd.Name() == cobra.ShellCompRequestCmd)) || + if (err == nil && + ((subCmd.HasParent() && subCmd.Parent().Name() == "completion") || subCmd.Name() == cobra.ShellCompRequestCmd)) || /* for the tests */ subCmd == baseCmd.Root() { loadCompletionForPlugin(c, plug) } From 4c9972cb7260e6424890c8d839c53ddb5460a6c5 Mon Sep 17 00:00:00 2001 From: Eric Lemieux Date: Mon, 18 Nov 2019 10:19:53 -0500 Subject: [PATCH 144/281] Add HelmVersion to Capabilities Signed-off-by: Eric Lemieux --- pkg/chartutil/capabilities.go | 5 +++++ pkg/chartutil/capabilities_test.go | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/pkg/chartutil/capabilities.go b/pkg/chartutil/capabilities.go index ce968c5d7..adfe2363d 100644 --- a/pkg/chartutil/capabilities.go +++ b/pkg/chartutil/capabilities.go @@ -20,6 +20,8 @@ import ( apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" + + helmversion "helm.sh/helm/v3/internal/version" ) var ( @@ -34,6 +36,7 @@ var ( Minor: "18", }, APIVersions: DefaultVersionSet, + HelmVersion: helmversion.Get(), } ) @@ -43,6 +46,8 @@ type Capabilities struct { KubeVersion KubeVersion // APIversions are supported Kubernetes API versions. APIVersions VersionSet + // HelmVersion is the build information for this helm version + HelmVersion helmversion.BuildInfo } // KubeVersion is the Kubernetes version. diff --git a/pkg/chartutil/capabilities_test.go b/pkg/chartutil/capabilities_test.go index 416eea06d..489a472be 100644 --- a/pkg/chartutil/capabilities_test.go +++ b/pkg/chartutil/capabilities_test.go @@ -58,3 +58,11 @@ func TestDefaultCapabilities(t *testing.T) { t.Errorf("Expected default KubeVersion.Minor to be 16, got %q", kv.Minor) } } + +func TestDefaultCapabilitiesHelmVersion(t *testing.T) { + hv := DefaultCapabilities.HelmVersion + + if hv.Version != "v3.2" { + t.Errorf("Expected default HelmVerison to be v3.2, got %q", hv.Version) + } +} From b6bbe4f08bbb98eadd6c9cd726b08a5c639908b3 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Mon, 15 Jun 2020 16:39:39 -0600 Subject: [PATCH 145/281] Improve the extractor and add tests (#8317) Signed-off-by: Matt Butcher --- pkg/plugin/installer/http_installer.go | 61 ++++++++++++++++++++- pkg/plugin/installer/http_installer_test.go | 30 ++++++++++ 2 files changed, 89 insertions(+), 2 deletions(-) diff --git a/pkg/plugin/installer/http_installer.go b/pkg/plugin/installer/http_installer.go index c07cad80a..28e50b72b 100644 --- a/pkg/plugin/installer/http_installer.go +++ b/pkg/plugin/installer/http_installer.go @@ -21,10 +21,12 @@ import ( "compress/gzip" "io" "os" + "path" "path/filepath" "regexp" "strings" + securejoin "github.com/cyphar/filepath-securejoin" "github.com/pkg/errors" "helm.sh/helm/v3/internal/third_party/dep/fs" @@ -118,7 +120,7 @@ func (i *HTTPInstaller) Install() error { } if err := i.extractor.Extract(pluginData, i.CacheDir); err != nil { - return err + return errors.Wrap(err, "extracting files from archive") } if !isPlugin(i.CacheDir) { @@ -148,6 +150,58 @@ func (i HTTPInstaller) Path() string { return helmpath.DataPath("plugins", i.PluginName) } +// CleanJoin resolves dest as a subpath of root. +// +// This function runs several security checks on the path, generating an error if +// the supplied `dest` looks suspicious or would result in dubious behavior on the +// filesystem. +// +// CleanJoin assumes that any attempt by `dest` to break out of the CWD is an attempt +// to be malicious. (If you don't care about this, use the securejoin-filepath library.) +// It will emit an error if it detects paths that _look_ malicious, operating on the +// assumption that we don't actually want to do anything with files that already +// appear to be nefarious. +// +// - The character `:` is considered illegal because it is a separator on UNIX and a +// drive designator on Windows. +// - The path component `..` is considered suspicions, and therefore illegal +// - The character \ (backslash) is treated as a path separator and is converted to /. +// - Beginning a path with a path separator is illegal +// - Rudimentary symlink protects are offered by SecureJoin. +func cleanJoin(root, dest string) (string, error) { + + // On Windows, this is a drive separator. On UNIX-like, this is the path list separator. + // In neither case do we want to trust a TAR that contains these. + if strings.Contains(dest, ":") { + return "", errors.New("path contains ':', which is illegal") + } + + // The Go tar library does not convert separators for us. + // We assume here, as we do elsewhere, that `\\` means a Windows path. + dest = strings.ReplaceAll(dest, "\\", "/") + + // We want to alert the user that something bad was attempted. Cleaning it + // is not a good practice. + for _, part := range strings.Split(dest, "/") { + if part == ".." { + return "", errors.New("path contains '..', which is illegal") + } + } + + // If a path is absolute, the creator of the TAR is doing something shady. + if path.IsAbs(dest) { + return "", errors.New("path is absolute, which is illegal") + } + + // SecureJoin will do some cleaning, as well as some rudimentary checking of symlinks. + newpath, err := securejoin.SecureJoin(root, dest) + if err != nil { + return "", err + } + + return filepath.ToSlash(newpath), nil +} + // Extract extracts compressed archives // // Implements Extractor. @@ -171,7 +225,10 @@ func (g *TarGzExtractor) Extract(buffer *bytes.Buffer, targetDir string) error { return err } - path := filepath.Join(targetDir, header.Name) + path, err := cleanJoin(targetDir, header.Name) + if err != nil { + return err + } switch header.Typeflag { case tar.TypeDir: diff --git a/pkg/plugin/installer/http_installer_test.go b/pkg/plugin/installer/http_installer_test.go index 99470ace6..3eb92ee77 100644 --- a/pkg/plugin/installer/http_installer_test.go +++ b/pkg/plugin/installer/http_installer_test.go @@ -277,3 +277,33 @@ func TestExtract(t *testing.T) { } } + +func TestCleanJoin(t *testing.T) { + for i, fixture := range []struct { + path string + expect string + expectError bool + }{ + {"foo/bar.txt", "/tmp/foo/bar.txt", false}, + {"/foo/bar.txt", "", true}, + {"./foo/bar.txt", "/tmp/foo/bar.txt", false}, + {"./././././foo/bar.txt", "/tmp/foo/bar.txt", false}, + {"../../../../foo/bar.txt", "", true}, + {"foo/../../../../bar.txt", "", true}, + {"c:/foo/bar.txt", "/tmp/c:/foo/bar.txt", true}, + {"foo\\bar.txt", "/tmp/foo/bar.txt", false}, + {"c:\\foo\\bar.txt", "", true}, + } { + out, err := cleanJoin("/tmp", fixture.path) + if err != nil { + if !fixture.expectError { + t.Errorf("Test %d: Path was not cleaned: %s", i, err) + } + continue + } + if fixture.expect != out { + t.Errorf("Test %d: Expected %q but got %q", i, fixture.expect, out) + } + } + +} From fd99c9055d3d2bbe2b5a790d5d355f496c4a73f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20R=C3=BCger?= Date: Tue, 16 Jun 2020 14:34:00 +0200 Subject: [PATCH 146/281] chore(Makefile): Remove unused variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dep was replaced by go mod Signed-off-by: Manuel Rüger --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index 446fbc0ed..524421279 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,6 @@ TARGET_OBJS ?= darwin-amd64.tar.gz darwin-amd64.tar.gz.sha256 darwin-amd64.tar.g BINNAME ?= helm GOPATH = $(shell go env GOPATH) -DEP = $(GOPATH)/bin/dep GOX = $(GOPATH)/bin/gox GOIMPORTS = $(GOPATH)/bin/goimports ARCH = $(shell uname -p) From 561cc4280830af4a40f2e60c5f17fb3ee69b4e39 Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Fri, 12 Jun 2020 08:24:55 -0400 Subject: [PATCH 147/281] feat(comp): Provide completion for --version flag This commit allows to use shell completion to obtain the list of available versions of a chart referenced in a 'repo/chart' format. It applies to: - helm install - helm template - helm upgrade - helm show - helm pull The 'repo/chart' argument must be present for completion to be triggered (or else we don't yet know which chart to fetch the versions for). The completion can be slow for the 'stable' repo because its index file takes some time to parse. Signed-off-by: Marc Khouzam --- cmd/helm/flags.go | 27 +++++++++++++ cmd/helm/install.go | 20 +++++++++- cmd/helm/install_test.go | 31 +++++++++++++++ cmd/helm/pull.go | 12 ++++++ cmd/helm/pull_test.go | 26 +++++++++++++ cmd/helm/show.go | 12 ++++++ cmd/helm/show_test.go | 38 +++++++++++++++++++ cmd/helm/template.go | 2 +- cmd/helm/template_test.go | 30 +++++++++++++++ cmd/helm/testdata/output/version-comp.txt | 5 +++ .../testdata/output/version-invalid-comp.txt | 2 + cmd/helm/upgrade.go | 12 ++++++ cmd/helm/upgrade_test.go | 26 +++++++++++++ 13 files changed, 240 insertions(+), 3 deletions(-) create mode 100644 cmd/helm/testdata/output/version-comp.txt create mode 100644 cmd/helm/testdata/output/version-invalid-comp.txt diff --git a/cmd/helm/flags.go b/cmd/helm/flags.go index d4cd88fef..040773d7b 100644 --- a/cmd/helm/flags.go +++ b/cmd/helm/flags.go @@ -19,6 +19,7 @@ package main import ( "fmt" "log" + "path/filepath" "strings" "github.com/spf13/cobra" @@ -27,7 +28,9 @@ import ( "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/cli/output" "helm.sh/helm/v3/pkg/cli/values" + "helm.sh/helm/v3/pkg/helmpath" "helm.sh/helm/v3/pkg/postrender" + "helm.sh/helm/v3/pkg/repo" ) const outputFlag = "output" @@ -127,3 +130,27 @@ func (p postRenderer) Set(s string) error { *p.renderer = pr return nil } + +func compVersionFlag(chartRef string, toComplete string) ([]string, cobra.ShellCompDirective) { + chartInfo := strings.Split(chartRef, "/") + if len(chartInfo) != 2 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + repoName := chartInfo[0] + chartName := chartInfo[1] + + path := filepath.Join(settings.RepositoryCache, helmpath.CacheIndexFile(repoName)) + + var versions []string + if indexFile, err := repo.LoadIndexFile(path); err == nil { + for _, details := range indexFile.Entries[chartName] { + version := details.Metadata.Version + if strings.HasPrefix(version, toComplete) { + versions = append(versions, version) + } + } + } + + return versions, cobra.ShellCompDirectiveNoFileComp +} diff --git a/cmd/helm/install.go b/cmd/helm/install.go index b489c262d..9024add2f 100644 --- a/cmd/helm/install.go +++ b/cmd/helm/install.go @@ -19,6 +19,7 @@ package main import ( "fmt" "io" + "log" "time" "github.com/pkg/errors" @@ -126,14 +127,14 @@ func newInstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { }, } - addInstallFlags(cmd.Flags(), client, valueOpts) + addInstallFlags(cmd, cmd.Flags(), client, valueOpts) bindOutputFlag(cmd, &outfmt) bindPostRenderFlag(cmd, &client.PostRenderer) return cmd } -func addInstallFlags(f *pflag.FlagSet, client *action.Install, valueOpts *values.Options) { +func addInstallFlags(cmd *cobra.Command, f *pflag.FlagSet, client *action.Install, valueOpts *values.Options) { f.BoolVar(&client.CreateNamespace, "create-namespace", false, "create the release namespace if not present") f.BoolVar(&client.DryRun, "dry-run", false, "simulate an install") f.BoolVar(&client.DisableHooks, "no-hooks", false, "prevent hooks from running during install") @@ -151,6 +152,21 @@ func addInstallFlags(f *pflag.FlagSet, client *action.Install, valueOpts *values f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent") addValueOptionsFlags(f, valueOpts) addChartPathOptionsFlags(f, &client.ChartPathOptions) + + err := cmd.RegisterFlagCompletionFunc("version", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + requiredArgs := 2 + if client.GenerateName { + requiredArgs = 1 + } + if len(args) != requiredArgs { + return nil, cobra.ShellCompDirectiveNoFileComp + } + return compVersionFlag(args[requiredArgs-1], toComplete) + }) + + if err != nil { + log.Fatal(err) + } } func runInstall(args []string, client *action.Install, valueOpts *values.Options, out io.Writer) (*release.Release, error) { diff --git a/cmd/helm/install_test.go b/cmd/helm/install_test.go index 7a101940f..36de648f9 100644 --- a/cmd/helm/install_test.go +++ b/cmd/helm/install_test.go @@ -17,6 +17,7 @@ limitations under the License. package main import ( + "fmt" "testing" ) @@ -208,3 +209,33 @@ func TestInstall(t *testing.T) { func TestInstallOutputCompletion(t *testing.T) { outputFlagCompletionTest(t, "install") } + +func TestInstallVersionCompletion(t *testing.T) { + repoFile := "testdata/helmhome/helm/repositories.yaml" + repoCache := "testdata/helmhome/helm/repository" + + repoSetup := fmt.Sprintf("--repository-config %s --repository-cache %s", repoFile, repoCache) + + tests := []cmdTestCase{{ + name: "completion for install version flag with release name", + cmd: fmt.Sprintf("%s __complete install releasename testing/alpine --version ''", repoSetup), + golden: "output/version-comp.txt", + }, { + name: "completion for install version flag with generate-name", + cmd: fmt.Sprintf("%s __complete install --generate-name testing/alpine --version ''", repoSetup), + golden: "output/version-comp.txt", + }, { + name: "completion for install version flag too few args", + cmd: fmt.Sprintf("%s __complete install testing/alpine --version ''", repoSetup), + golden: "output/version-invalid-comp.txt", + }, { + name: "completion for install version flag too many args", + cmd: fmt.Sprintf("%s __complete install releasename testing/alpine badarg --version ''", repoSetup), + golden: "output/version-invalid-comp.txt", + }, { + name: "completion for install version flag invalid chart", + cmd: fmt.Sprintf("%s __complete install releasename invalid/invalid --version ''", repoSetup), + golden: "output/version-invalid-comp.txt", + }} + runTestCmd(t, tests) +} diff --git a/cmd/helm/pull.go b/cmd/helm/pull.go index d10c629db..3f62bf0c7 100644 --- a/cmd/helm/pull.go +++ b/cmd/helm/pull.go @@ -19,6 +19,7 @@ package main import ( "fmt" "io" + "log" "github.com/spf13/cobra" @@ -82,5 +83,16 @@ func newPullCmd(out io.Writer) *cobra.Command { f.StringVarP(&client.DestDir, "destination", "d", ".", "location to write the chart. If this and tardir are specified, tardir is appended to this") addChartPathOptionsFlags(f, &client.ChartPathOptions) + err := cmd.RegisterFlagCompletionFunc("version", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 1 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + return compVersionFlag(args[0], toComplete) + }) + + if err != nil { + log.Fatal(err) + } + return cmd } diff --git a/cmd/helm/pull_test.go b/cmd/helm/pull_test.go index d4661f928..435df51f1 100644 --- a/cmd/helm/pull_test.go +++ b/cmd/helm/pull_test.go @@ -195,3 +195,29 @@ func TestPullCmd(t *testing.T) { }) } } + +func TestPullVersionCompletion(t *testing.T) { + repoFile := "testdata/helmhome/helm/repositories.yaml" + repoCache := "testdata/helmhome/helm/repository" + + repoSetup := fmt.Sprintf("--repository-config %s --repository-cache %s", repoFile, repoCache) + + tests := []cmdTestCase{{ + name: "completion for pull version flag", + cmd: fmt.Sprintf("%s __complete pull testing/alpine --version ''", repoSetup), + golden: "output/version-comp.txt", + }, { + name: "completion for pull version flag too few args", + cmd: fmt.Sprintf("%s __complete pull --version ''", repoSetup), + golden: "output/version-invalid-comp.txt", + }, { + name: "completion for pull version flag too many args", + cmd: fmt.Sprintf("%s __complete pull testing/alpine badarg --version ''", repoSetup), + golden: "output/version-invalid-comp.txt", + }, { + name: "completion for pull version flag invalid chart", + cmd: fmt.Sprintf("%s __complete pull invalid/invalid --version ''", repoSetup), + golden: "output/version-invalid-comp.txt", + }} + runTestCmd(t, tests) +} diff --git a/cmd/helm/show.go b/cmd/helm/show.go index c78069a09..c122d8476 100644 --- a/cmd/helm/show.go +++ b/cmd/helm/show.go @@ -19,6 +19,7 @@ package main import ( "fmt" "io" + "log" "github.com/spf13/cobra" @@ -151,6 +152,17 @@ func addShowFlags(subCmd *cobra.Command, client *action.Show) { f.BoolVar(&client.Devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored") addChartPathOptionsFlags(f, &client.ChartPathOptions) + + err := subCmd.RegisterFlagCompletionFunc("version", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 1 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + return compVersionFlag(args[0], toComplete) + }) + + if err != nil { + log.Fatal(err) + } } func runShow(args []string, client *action.Show) (string, error) { diff --git a/cmd/helm/show_test.go b/cmd/helm/show_test.go index 00d7c8145..6c550282f 100644 --- a/cmd/helm/show_test.go +++ b/cmd/helm/show_test.go @@ -80,3 +80,41 @@ func TestShowPreReleaseChart(t *testing.T) { }) } } + +func TestShowVersionCompletion(t *testing.T) { + repoFile := "testdata/helmhome/helm/repositories.yaml" + repoCache := "testdata/helmhome/helm/repository" + + repoSetup := fmt.Sprintf("--repository-config %s --repository-cache %s", repoFile, repoCache) + + tests := []cmdTestCase{{ + name: "completion for show version flag", + cmd: fmt.Sprintf("%s __complete show chart testing/alpine --version ''", repoSetup), + golden: "output/version-comp.txt", + }, { + name: "completion for show version flag too few args", + cmd: fmt.Sprintf("%s __complete show chart --version ''", repoSetup), + golden: "output/version-invalid-comp.txt", + }, { + name: "completion for show version flag too many args", + cmd: fmt.Sprintf("%s __complete show chart testing/alpine badarg --version ''", repoSetup), + golden: "output/version-invalid-comp.txt", + }, { + name: "completion for show version flag invalid chart", + cmd: fmt.Sprintf("%s __complete show chart invalid/invalid --version ''", repoSetup), + golden: "output/version-invalid-comp.txt", + }, { + name: "completion for show version flag with all", + cmd: fmt.Sprintf("%s __complete show all testing/alpine --version ''", repoSetup), + golden: "output/version-comp.txt", + }, { + name: "completion for show version flag with readme", + cmd: fmt.Sprintf("%s __complete show readme testing/alpine --version ''", repoSetup), + golden: "output/version-comp.txt", + }, { + name: "completion for show version flag with values", + cmd: fmt.Sprintf("%s __complete show values testing/alpine --version ''", repoSetup), + golden: "output/version-comp.txt", + }} + runTestCmd(t, tests) +} diff --git a/cmd/helm/template.go b/cmd/helm/template.go index 0fc29e189..83ad61f22 100644 --- a/cmd/helm/template.go +++ b/cmd/helm/template.go @@ -139,7 +139,7 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { } f := cmd.Flags() - addInstallFlags(f, client, valueOpts) + addInstallFlags(cmd, f, client, valueOpts) 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") diff --git a/cmd/helm/template_test.go b/cmd/helm/template_test.go index 92dd9825e..f8cd31347 100644 --- a/cmd/helm/template_test.go +++ b/cmd/helm/template_test.go @@ -124,3 +124,33 @@ func TestTemplateCmd(t *testing.T) { } runTestCmd(t, tests) } + +func TestTemplateVersionCompletion(t *testing.T) { + repoFile := "testdata/helmhome/helm/repositories.yaml" + repoCache := "testdata/helmhome/helm/repository" + + repoSetup := fmt.Sprintf("--repository-config %s --repository-cache %s", repoFile, repoCache) + + tests := []cmdTestCase{{ + name: "completion for template version flag with release name", + cmd: fmt.Sprintf("%s __complete template releasename testing/alpine --version ''", repoSetup), + golden: "output/version-comp.txt", + }, { + name: "completion for template version flag with generate-name", + cmd: fmt.Sprintf("%s __complete template --generate-name testing/alpine --version ''", repoSetup), + golden: "output/version-comp.txt", + }, { + name: "completion for template version flag too few args", + cmd: fmt.Sprintf("%s __complete template testing/alpine --version ''", repoSetup), + golden: "output/version-invalid-comp.txt", + }, { + name: "completion for template version flag too many args", + cmd: fmt.Sprintf("%s __complete template releasename testing/alpine badarg --version ''", repoSetup), + golden: "output/version-invalid-comp.txt", + }, { + name: "completion for template version flag invalid chart", + cmd: fmt.Sprintf("%s __complete template releasename invalid/invalid --version ''", repoSetup), + golden: "output/version-invalid-comp.txt", + }} + runTestCmd(t, tests) +} diff --git a/cmd/helm/testdata/output/version-comp.txt b/cmd/helm/testdata/output/version-comp.txt new file mode 100644 index 000000000..098e2cec2 --- /dev/null +++ b/cmd/helm/testdata/output/version-comp.txt @@ -0,0 +1,5 @@ +0.3.0-rc.1 +0.2.0 +0.1.0 +:4 +Completion ended with directive: ShellCompDirectiveNoFileComp diff --git a/cmd/helm/testdata/output/version-invalid-comp.txt b/cmd/helm/testdata/output/version-invalid-comp.txt new file mode 100644 index 000000000..8d9fad576 --- /dev/null +++ b/cmd/helm/testdata/output/version-invalid-comp.txt @@ -0,0 +1,2 @@ +:4 +Completion ended with directive: ShellCompDirectiveNoFileComp diff --git a/cmd/helm/upgrade.go b/cmd/helm/upgrade.go index 1db899eae..dc3bc9a58 100644 --- a/cmd/helm/upgrade.go +++ b/cmd/helm/upgrade.go @@ -19,6 +19,7 @@ package main import ( "fmt" "io" + "log" "time" "github.com/pkg/errors" @@ -188,5 +189,16 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { bindOutputFlag(cmd, &outfmt) bindPostRenderFlag(cmd, &client.PostRenderer) + err := cmd.RegisterFlagCompletionFunc("version", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 2 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + return compVersionFlag(args[1], toComplete) + }) + + if err != nil { + log.Fatal(err) + } + return cmd } diff --git a/cmd/helm/upgrade_test.go b/cmd/helm/upgrade_test.go index 6f260ae57..7e88bc0df 100644 --- a/cmd/helm/upgrade_test.go +++ b/cmd/helm/upgrade_test.go @@ -381,3 +381,29 @@ func prepareMockRelease(releaseName string, t *testing.T) (func(n string, v int, func TestUpgradeOutputCompletion(t *testing.T) { outputFlagCompletionTest(t, "upgrade") } + +func TestUpgradeVersionCompletion(t *testing.T) { + repoFile := "testdata/helmhome/helm/repositories.yaml" + repoCache := "testdata/helmhome/helm/repository" + + repoSetup := fmt.Sprintf("--repository-config %s --repository-cache %s", repoFile, repoCache) + + tests := []cmdTestCase{{ + name: "completion for upgrade version flag", + cmd: fmt.Sprintf("%s __complete upgrade releasename testing/alpine --version ''", repoSetup), + golden: "output/version-comp.txt", + }, { + name: "completion for upgrade version flag too few args", + cmd: fmt.Sprintf("%s __complete upgrade releasename --version ''", repoSetup), + golden: "output/version-invalid-comp.txt", + }, { + name: "completion for upgrade version flag too many args", + cmd: fmt.Sprintf("%s __complete upgrade releasename testing/alpine badarg --version ''", repoSetup), + golden: "output/version-invalid-comp.txt", + }, { + name: "completion for upgrade version flag invalid chart", + cmd: fmt.Sprintf("%s __complete upgrade releasename invalid/invalid --version ''", repoSetup), + golden: "output/version-invalid-comp.txt", + }} + runTestCmd(t, tests) +} From 569ad27f2bc16b7b407e8a464e70e70cac9a43a8 Mon Sep 17 00:00:00 2001 From: vitt-bagal <31851690+vitt-bagal@users.noreply.github.com> Date: Mon, 22 Jun 2020 20:50:54 +0530 Subject: [PATCH 148/281] remove s390x arch check Signed-off-by: vitthalb@us.ibm.com --- scripts/get-helm-3 | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/get-helm-3 b/scripts/get-helm-3 index 22a93aa0e..ce9411edc 100755 --- a/scripts/get-helm-3 +++ b/scripts/get-helm-3 @@ -33,7 +33,6 @@ initArch() { x86_64) ARCH="amd64";; i686) ARCH="386";; i386) ARCH="386";; - s390x) ARCH="s390x";; esac } From 148d94bcf781bdbf4c735ad0defdee15961389ec Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Tue, 23 Jun 2020 11:07:48 -0700 Subject: [PATCH 149/281] version bump Signed-off-by: Matthew Fisher --- .../helm/repository/test-name-charts.txt | 0 .../helm/repository/test-name-index.yaml | 3 + .../testdata/output/schema-negative-cli.txt | 2 +- cmd/helm/testdata/output/schema-negative.txt | 2 +- .../output/subchart-schema-cli-negative.txt | 2 +- go.mod | 32 +-- go.sum | 244 +++++++++++++++--- pkg/chartutil/jsonschema_test.go | 2 +- 8 files changed, 238 insertions(+), 49 deletions(-) create mode 100644 cmd/helm/testdata/helmhome/helm/repository/test-name-charts.txt create mode 100644 cmd/helm/testdata/helmhome/helm/repository/test-name-index.yaml diff --git a/cmd/helm/testdata/helmhome/helm/repository/test-name-charts.txt b/cmd/helm/testdata/helmhome/helm/repository/test-name-charts.txt new file mode 100644 index 000000000..e69de29bb diff --git a/cmd/helm/testdata/helmhome/helm/repository/test-name-index.yaml b/cmd/helm/testdata/helmhome/helm/repository/test-name-index.yaml new file mode 100644 index 000000000..895e79d39 --- /dev/null +++ b/cmd/helm/testdata/helmhome/helm/repository/test-name-index.yaml @@ -0,0 +1,3 @@ +apiVersion: v1 +entries: {} +generated: "2020-06-23T10:01:59.2530763-07:00" diff --git a/cmd/helm/testdata/output/schema-negative-cli.txt b/cmd/helm/testdata/output/schema-negative-cli.txt index 26bc92b1b..d6f096e14 100644 --- a/cmd/helm/testdata/output/schema-negative-cli.txt +++ b/cmd/helm/testdata/output/schema-negative-cli.txt @@ -1,4 +1,4 @@ Error: values don't meet the specifications of the schema(s) in the following chart(s): empty: -- age: Must be greater than or equal to 0/1 +- age: Must be greater than or equal to 0 diff --git a/cmd/helm/testdata/output/schema-negative.txt b/cmd/helm/testdata/output/schema-negative.txt index 2ea97b7d0..f7c89dd56 100644 --- a/cmd/helm/testdata/output/schema-negative.txt +++ b/cmd/helm/testdata/output/schema-negative.txt @@ -1,5 +1,5 @@ Error: values don't meet the specifications of the schema(s) in the following chart(s): empty: - (root): employmentInfo is required -- age: Must be greater than or equal to 0/1 +- age: Must be greater than or equal to 0 diff --git a/cmd/helm/testdata/output/subchart-schema-cli-negative.txt b/cmd/helm/testdata/output/subchart-schema-cli-negative.txt index 86f6e87a2..c0883a8e8 100644 --- a/cmd/helm/testdata/output/subchart-schema-cli-negative.txt +++ b/cmd/helm/testdata/output/subchart-schema-cli-negative.txt @@ -1,4 +1,4 @@ Error: values don't meet the specifications of the schema(s) in the following chart(s): subchart-with-schema: -- age: Must be greater than or equal to 0/1 +- age: Must be greater than or equal to 0 diff --git a/go.mod b/go.mod index f23d152a2..08eae92fa 100644 --- a/go.mod +++ b/go.mod @@ -7,10 +7,10 @@ require ( github.com/DATA-DOG/go-sqlmock v1.4.1 github.com/Masterminds/semver/v3 v3.1.0 github.com/Masterminds/sprig/v3 v3.1.0 - github.com/Masterminds/squirrel v1.2.0 + github.com/Masterminds/squirrel v1.4.0 github.com/Masterminds/vcs v1.13.1 - github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496 - github.com/containerd/containerd v1.3.2 + github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 + github.com/containerd/containerd v1.3.4 github.com/cyphar/filepath-securejoin v0.2.2 github.com/deislabs/oras v0.8.1 github.com/docker/distribution v2.7.1+incompatible @@ -21,26 +21,26 @@ require ( github.com/gofrs/flock v0.7.1 github.com/gosuri/uitable v0.0.4 github.com/jmoiron/sqlx v1.2.0 - github.com/lib/pq v1.3.0 + github.com/lib/pq v1.7.0 github.com/mattn/go-shellwords v1.0.10 github.com/mitchellh/copystructure v1.0.0 - github.com/opencontainers/go-digest v1.0.0-rc1 + github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.0.1 github.com/pkg/errors v0.9.1 - github.com/rubenv/sql-migrate v0.0.0-20200212082348-64f95ea68aa3 - github.com/sirupsen/logrus v1.4.2 + github.com/rubenv/sql-migrate v0.0.0-20200616145509-8d140a17f351 + github.com/sirupsen/logrus v1.6.0 github.com/spf13/cobra v1.0.0 github.com/spf13/pflag v1.0.5 - github.com/stretchr/testify v1.5.1 - github.com/xeipuuv/gojsonschema v1.1.0 - golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 - k8s.io/api v0.18.2 - k8s.io/apiextensions-apiserver v0.18.2 - k8s.io/apimachinery v0.18.2 - k8s.io/cli-runtime v0.18.2 - k8s.io/client-go v0.18.2 + github.com/stretchr/testify v1.6.1 + github.com/xeipuuv/gojsonschema v1.2.0 + golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 + k8s.io/api v0.18.4 + k8s.io/apiextensions-apiserver v0.18.4 + k8s.io/apimachinery v0.18.4 + k8s.io/cli-runtime v0.18.4 + k8s.io/client-go v0.18.4 k8s.io/klog v1.0.0 - k8s.io/kubectl v0.18.2 + k8s.io/kubectl v0.18.4 sigs.k8s.io/yaml v1.2.0 ) diff --git a/go.sum b/go.sum index 2908f3eb8..0d0ef8c83 100644 --- a/go.sum +++ b/go.sum @@ -25,6 +25,7 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/DATA-DOG/go-sqlmock v1.4.1 h1:ThlnYciV1iM/V0OSF/dtkqWb6xo5qITT1TJBG1MRDJM= github.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd h1:sjQovDkwrZp8u+gxLtPgKGjk5hCxuy2hrRejBTA9xFU= github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= @@ -33,8 +34,8 @@ github.com/Masterminds/semver/v3 v3.1.0 h1:Y2lUDsFKVRSYGojLJ1yLxSXdMmMYTYls0rCvo github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/sprig/v3 v3.1.0 h1:j7GpgZ7PdFqNsmncycTHsLmVPf5/3wJtlgW9TNDYD9Y= github.com/Masterminds/sprig/v3 v3.1.0/go.mod h1:ONGMf7UfYGAbMXCZmQLy8x3lCDIPrEZE/rU8pmrbihA= -github.com/Masterminds/squirrel v1.2.0 h1:K1NhbTO21BWG47IVR0OnIZuE0LZcXAYqywrC3Ko53KI= -github.com/Masterminds/squirrel v1.2.0/go.mod h1:yaPeOnPG5ZRwL9oKdTsO/prlkPbXWZlRVMQ/gGlzIuA= +github.com/Masterminds/squirrel v1.4.0 h1:he5i/EXixZxrBUWcxzDYMiju9WZ3ld/l7QBNuo/eN3w= +github.com/Masterminds/squirrel v1.4.0/go.mod h1:yaPeOnPG5ZRwL9oKdTsO/prlkPbXWZlRVMQ/gGlzIuA= github.com/Masterminds/vcs v1.13.1 h1:NL3G1X7/7xduQtA2sJLpVpfHTNBALVNSjob6KEjPXNQ= github.com/Masterminds/vcs v1.13.1/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5 h1:ygIc8M6trr62pF5DucadTWGdEB4mEyvzi0e2nbcmcyA= @@ -52,21 +53,37 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496 h1:zV3ejI06GQ59hwDQAvmK1qxOQGB3WuVTRoY0okPTAv0= -github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= +github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0S6Vi7/lbWECcX0j45yZReDZ56BQsrVBOEEY= +github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= +github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= +github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y= github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= @@ -82,23 +99,35 @@ github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembj github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= +github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= +github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f h1:tSNMc+rJDfmYntojat8lljbt1mgKNpTxUZJsSzJ9Y1s= github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.3.2 h1:ForxmXkA6tPIvffbrDAcPUIB32QgXkt2XFj+F0UxetA= github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.4 h1:3o0smo5SKY7H6AJCmJhsnCjR2/V2T8VmiHt7seN2/kI= +github.com/containerd/containerd v1.3.4/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20200107194136-26c1120b8d41 h1:kIFnQBO7rQ0XkMe6xEwbybYHBEaWmh/f++laI6Emt7M= github.com/containerd/continuity v0.0.0-20200107194136-26c1120b8d41/go.mod h1:Dq467ZllaHgAtVp4p1xUQWBrFXR9s/wyoTpG8zOJGkY= +github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448 h1:PUD50EuOMkXVcpBIA/R95d56duJR9VxhwncsFbNnxW4= github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= +github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de h1:dlfGmNcE3jDAecLqwKPMNX6nk2qh1c1Vg1/YTzpOOF4= github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd h1:JNn81o/xG+8NEo3bC/vx9pbi/g2WI8mtP2/nXzu297Y= github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -113,6 +142,7 @@ github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfc github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= @@ -153,13 +183,19 @@ github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QL github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v4.2.0+incompatible h1:fUDGZCv/7iAN7u0puUVhvKCcsR6vRfwrJatElLBEf0I= github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M= github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= @@ -168,6 +204,8 @@ github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZM github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= +github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7 h1:LofdAjjjqCSXMwLGgOgnE+rdPuvX9DxCqaHwKy7i/ko= @@ -179,8 +217,11 @@ github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0 github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= @@ -245,9 +286,12 @@ github.com/gobuffalo/packr/v2 v2.7.1/go.mod h1:qYEvAazPaVxy7Y7KR0W8qYEE+RymX74kE github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= +github.com/godror/godror v0.13.3/go.mod h1:2ouUT4kdhUBk7TAkHWD4SN0CdI0pgEQbo8FVHhbSKWg= github.com/gofrs/flock v0.7.1 h1:DP+LD/t0njgoPBvT5MJLeliUIVQR03hiKR6vezdwHlc= github.com/gofrs/flock v0.7.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= @@ -258,6 +302,8 @@ github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903 h1:LbsanbbD6LieF github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -265,6 +311,7 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAONeMXrxql8uvOKuAZSu8aM5RUGv+1C6IJaEho= github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995/go.mod h1:lJgMEyOkYFkPcDKwRXegd+iM6E7matEszMG5HhwytU8= github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk= @@ -273,6 +320,7 @@ github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= @@ -281,6 +329,7 @@ github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -292,10 +341,14 @@ github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTV github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/391+sT5o= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33 h1:893HsJqtxp9z1SF76gg6hY70hRY1wVlTSnC/h1yUDCo= github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.2 h1:zoNxOV7WjqXptQOVngLmcSQgXmgk4NMz1HibBchjl/I= github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= @@ -307,25 +360,44 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= @@ -344,6 +416,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -357,11 +431,14 @@ github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhR github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU= -github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.7.0 h1:h93mCPfUSkaul3Ka/VG8uZdmW1uMHDGxzu0NWHuJmHY= +github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= +github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= +github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= +github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -376,6 +453,7 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-oci8 v0.0.7/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= @@ -386,12 +464,18 @@ github.com/mattn/go-sqlite3 v1.12.0 h1:u/x3mp++qUxvYfulZ4HKOvVO0JWhk7HtE8lWhbGz/ github.com/mattn/go-sqlite3 v1.12.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f h1:2+myh5ml7lgEU/51gbeLHfKGNfgEQQIWrlbdaOsidbQ= github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= @@ -409,84 +493,128 @@ github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8m github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= +github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= +github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.2/go.mod h1:rSAaSIOAGT9odnlyGlUfAJaoc5w2fSBUmeGDbRWPxyQ= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y= github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700 h1:eNUVfm/RFLIi1G7flU5/ZRTHvd4kcVuzfRnL6OFlzCI= github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= +github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= +github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc= github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= +github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.3.0 h1:miYCvYqFXtl/J9FIy8eNpBfYthAEFg+Ys0XyUVEcDsc= +github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.7.0 h1:L+1lyG48J1zAQXA3RBX/nG/B3gjlHq0zTt2tlbJLyCY= +github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8= github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.4.0 h1:LUa41nrWTQNGhzdsZ5lTnkwbNjj6rXTdazA1cSdjkOY= github.com/rogpeppe/go-internal v1.4.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rubenv/sql-migrate v0.0.0-20200212082348-64f95ea68aa3 h1:xkBtI5JktwbW/vf4vopBbhYsRFTGfQWHYXzC0/qYwxI= -github.com/rubenv/sql-migrate v0.0.0-20200212082348-64f95ea68aa3/go.mod h1:rtQlpHw+eR6UrqaS3kX1VYeaCxzCVdimDS7g5Ln4pPc= +github.com/rubenv/sql-migrate v0.0.0-20200616145509-8d140a17f351 h1:HXr/qUllAWv9riaI4zh2eXWKmCSDqVS/XH1MRHLKRwk= +github.com/rubenv/sql-migrate v0.0.0-20200616145509-8d140a17f351/go.mod h1:DCgfY80j8GYL7MLEfvcpSFvjD0L5yZq/aZUJmhZklyg= github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= @@ -495,9 +623,13 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= @@ -520,8 +652,12 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -529,6 +665,9 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8 h1:zLV6q4e8Jv9EHjNg/iHfzwDkCve6Ua5jCygptrtXHvI= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -537,14 +676,15 @@ github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGr github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= -github.com/xeipuuv/gojsonschema v1.1.0 h1:ngVtJC9TY/lg0AA/1k48FYhBrhRoFlEmWzsehpNAaZg= -github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= @@ -562,42 +702,62 @@ go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mI go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2 h1:75k/FF0Q2YM8QYo07VPddOLBslDt1MZOdEslOHvmzAs= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d h1:9FCpayM9Egr1baVnV1SX0H87m+XB0B8S0hAMi99X/3U= golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo= golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 h1:bXoxMPcSLOq08zI3/c5dEBT6lE4eh+jOh886GHrn6V8= golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -628,8 +788,10 @@ golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -641,11 +803,14 @@ golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3 h1:7TYNF4UdlohbFwpNH04CoPMp1cHUZgO1Ebq5r2hIjfo= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7 h1:HmbHVPwrPEKPGLAcHSrMe6+hqSUlvZU0rab6x5EXfGU= golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191220142924-d4481acd189f h1:68K/z8GLUxV76xGSqwTWw2gyk/jwn79LUL43rES2g8o= +golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -655,7 +820,10 @@ golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -668,14 +836,21 @@ golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= @@ -685,12 +860,16 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= @@ -706,6 +885,7 @@ gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qS gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/gorp.v1 v1.7.2 h1:j3DWlAyGVv8whO7AcIWznQ2Yj7yJkn34B8s63GViAAw= gopkg.in/gorp.v1 v1.7.2/go.mod h1:Wo3h+DBQZIxATwftsglhdD/62zRFPhGhTiu5jUJmCaw= @@ -716,6 +896,7 @@ gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -723,37 +904,41 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -k8s.io/api v0.18.2 h1:wG5g5ZmSVgm5B+eHMIbI9EGATS2L8Z72rda19RIEgY8= -k8s.io/api v0.18.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78= -k8s.io/apiextensions-apiserver v0.18.2 h1:I4v3/jAuQC+89L3Z7dDgAiN4EOjN6sbm6iBqQwHTah8= -k8s.io/apiextensions-apiserver v0.18.2/go.mod h1:q3faSnRGmYimiocj6cHQ1I3WpLqmDgJFlKL37fC4ZvY= -k8s.io/apimachinery v0.18.2 h1:44CmtbmkzVDAhCpRVSiP2R5PPrC2RtlIv/MoB8xpdRA= -k8s.io/apimachinery v0.18.2/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= -k8s.io/apiserver v0.18.2/go.mod h1:Xbh066NqrZO8cbsoenCwyDJ1OSi8Ag8I2lezeHxzwzw= -k8s.io/cli-runtime v0.18.2 h1:JiTN5RgkFNTiMxHBRyrl6n26yKWAuNRlei1ZJALUmC8= -k8s.io/cli-runtime v0.18.2/go.mod h1:yfFR2sQQzDsV0VEKGZtrJwEy4hLZ2oj4ZIfodgxAHWQ= -k8s.io/client-go v0.18.2 h1:aLB0iaD4nmwh7arT2wIn+lMnAq7OswjaejkQ8p9bBYE= -k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU= -k8s.io/code-generator v0.18.2/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc= -k8s.io/component-base v0.18.2 h1:SJweNZAGcUvsypLGNPNGeJ9UgPZQ6+bW+gEHe8uyh/Y= -k8s.io/component-base v0.18.2/go.mod h1:kqLlMuhJNHQ9lz8Z7V5bxUUtjFZnrypArGl58gmDfUM= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +k8s.io/api v0.18.4 h1:8x49nBRxuXGUlDlwlWd3RMY1SayZrzFfxea3UZSkFw4= +k8s.io/api v0.18.4/go.mod h1:lOIQAKYgai1+vz9J7YcDZwC26Z0zQewYOGWdyIPUUQ4= +k8s.io/apiextensions-apiserver v0.18.4 h1:Y3HGERmS8t9u12YNUFoOISqefaoGRuTc43AYCLzWmWE= +k8s.io/apiextensions-apiserver v0.18.4/go.mod h1:NYeyeYq4SIpFlPxSAB6jHPIdvu3hL0pc36wuRChybio= +k8s.io/apimachinery v0.18.4 h1:ST2beySjhqwJoIFk6p7Hp5v5O0hYY6Gngq/gUYXTPIA= +k8s.io/apimachinery v0.18.4/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko= +k8s.io/apiserver v0.18.4/go.mod h1:q+zoFct5ABNnYkGIaGQ3bcbUNdmPyOCoEBcg51LChY8= +k8s.io/cli-runtime v0.18.4 h1:IUx7quIOb4gbQ4M+B1ksF/PTBovQuL5tXWzplX3t+FM= +k8s.io/cli-runtime v0.18.4/go.mod h1:9/hS/Cuf7NVzWR5F/5tyS6xsnclxoPLVtwhnkJG1Y4g= +k8s.io/client-go v0.18.4 h1:un55V1Q/B3JO3A76eS0kUSywgGK/WR3BQ8fHQjNa6Zc= +k8s.io/client-go v0.18.4/go.mod h1:f5sXwL4yAZRkAtzOxRWUhA/N8XzGCb+nPZI8PfobZ9g= +k8s.io/code-generator v0.18.4/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c= +k8s.io/component-base v0.18.4 h1:Kr53Fp1iCGNsl9Uv4VcRvLy7YyIqi9oaJOQ7SXtKI98= +k8s.io/component-base v0.18.4/go.mod h1:7jr/Ef5PGmKwQhyAz/pjByxJbC58mhKAhiaDu0vXfPk= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= -k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c h1:/KUFqjjqAcY4Us6luF5RDNZ16KJtb49HfR3ZHB9qYXM= -k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= -k8s.io/kubectl v0.18.2 h1:9jnGSOC2DDVZmMUTMi0D1aed438mfQcgqa5TAzVjA1k= -k8s.io/kubectl v0.18.2/go.mod h1:OdgFa3AlsPKRpFFYE7ICTwulXOcMGXHTc+UKhHKvrb4= +k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6 h1:Oh3Mzx5pJ+yIumsAD0MOECPVeXsVot0UkiaCGVyfGQY= +k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= +k8s.io/kubectl v0.18.4 h1:l9DUYPTEMs1+qNtoqPpTyaJOosvj7l7tQqphCO1K52s= +k8s.io/kubectl v0.18.4/go.mod h1:EzB+nfeUWk6fm6giXQ8P4Fayw3dsN+M7Wjy23mTRtB0= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= -k8s.io/metrics v0.18.2/go.mod h1:qga8E7QfYNR9Q89cSCAjinC9pTZ7yv1XSVGUB0vJypg= +k8s.io/metrics v0.18.4/go.mod h1:luze4fyI9JG4eLDZy0kFdYEebqNfi0QrG4xNEbPkHOs= k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89 h1:d4vVOjXm687F1iLSP2q3lyPPuyvTUt3aVoBpi2DqRsU= k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0= @@ -766,4 +951,5 @@ sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI= diff --git a/pkg/chartutil/jsonschema_test.go b/pkg/chartutil/jsonschema_test.go index 33f009259..a0acd5a7f 100644 --- a/pkg/chartutil/jsonschema_test.go +++ b/pkg/chartutil/jsonschema_test.go @@ -56,7 +56,7 @@ func TestValidateAgainstSingleSchemaNegative(t *testing.T) { } expectedErrString := `- (root): employmentInfo is required -- age: Must be greater than or equal to 0/1 +- age: Must be greater than or equal to 0 ` if errString != expectedErrString { t.Errorf("Error string :\n`%s`\ndoes not match expected\n`%s`", errString, expectedErrString) From 4f136861d397ec37b18c92ceee8027333ba1bc24 Mon Sep 17 00:00:00 2001 From: ShenXinkang <610716076@qq.com> Date: Tue, 9 Jun 2020 13:57:43 +0800 Subject: [PATCH 150/281] fix template command use --show-only flags error in windows environment Signed-off-by: ShenXinkang <610716076@qq.com> --- cmd/helm/template.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cmd/helm/template.go b/cmd/helm/template.go index 83ad61f22..9ea61b17a 100644 --- a/cmd/helm/template.go +++ b/cmd/helm/template.go @@ -102,6 +102,8 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { var manifestsToRender []string for _, f := range showFiles { missing := true + // Use linux-style filepath separators to unify user's input path + f = filepath.ToSlash(f) for _, manifestKey := range manifestsKeys { manifest := splitManifests[manifestKey] submatch := manifestNameRegex.FindStringSubmatch(manifest) @@ -112,7 +114,9 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { // manifest.Name is rendered using linux-style filepath separators on Windows as // well as macOS/linux. manifestPathSplit := strings.Split(manifestName, "/") - manifestPath := filepath.Join(manifestPathSplit...) + // manifest.Path is connected using linux-style filepath separators on Windows as + // well as macOS/linux + manifestPath := strings.Join(manifestPathSplit, "/") // if the filepath provided matches a manifest path in the // chart, render that manifest From 05beedd671027b227b44968e6a961be5b200a465 Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Sun, 28 Jun 2020 19:53:29 -0400 Subject: [PATCH 151/281] feat(comp): Complete revision for rollback command Signed-off-by: Marc Khouzam --- cmd/helm/get_all.go | 2 +- cmd/helm/get_hooks.go | 2 +- cmd/helm/get_manifest.go | 2 +- cmd/helm/get_notes.go | 2 +- cmd/helm/get_values.go | 2 +- cmd/helm/history.go | 10 ++++-- cmd/helm/rollback.go | 11 ++++-- cmd/helm/rollback_test.go | 36 +++++++++++++++++++ cmd/helm/status.go | 2 +- cmd/helm/testdata/output/revision-comp.txt | 4 +-- cmd/helm/testdata/output/rollback-comp.txt | 4 +++ .../output/rollback-wrong-args-comp.txt | 2 ++ 12 files changed, 65 insertions(+), 14 deletions(-) create mode 100644 cmd/helm/testdata/output/rollback-comp.txt create mode 100644 cmd/helm/testdata/output/rollback-wrong-args-comp.txt diff --git a/cmd/helm/get_all.go b/cmd/helm/get_all.go index 1ebc7e387..a5037e4df 100644 --- a/cmd/helm/get_all.go +++ b/cmd/helm/get_all.go @@ -67,7 +67,7 @@ func newGetAllCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { f.IntVar(&client.Version, "revision", 0, "get the named release with revision") err := cmd.RegisterFlagCompletionFunc("revision", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) == 1 { - return compListRevisions(cfg, args[0]) + return compListRevisions(toComplete, cfg, args[0]) } return nil, cobra.ShellCompDirectiveNoFileComp }) diff --git a/cmd/helm/get_hooks.go b/cmd/helm/get_hooks.go index 6453c30eb..8b78653b5 100644 --- a/cmd/helm/get_hooks.go +++ b/cmd/helm/get_hooks.go @@ -62,7 +62,7 @@ func newGetHooksCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { cmd.Flags().IntVar(&client.Version, "revision", 0, "get the named release with revision") err := cmd.RegisterFlagCompletionFunc("revision", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) == 1 { - return compListRevisions(cfg, args[0]) + return compListRevisions(toComplete, cfg, args[0]) } return nil, cobra.ShellCompDirectiveNoFileComp }) diff --git a/cmd/helm/get_manifest.go b/cmd/helm/get_manifest.go index 048bf36b0..8ffeb3676 100644 --- a/cmd/helm/get_manifest.go +++ b/cmd/helm/get_manifest.go @@ -62,7 +62,7 @@ func newGetManifestCmd(cfg *action.Configuration, out io.Writer) *cobra.Command cmd.Flags().IntVar(&client.Version, "revision", 0, "get the named release with revision") err := cmd.RegisterFlagCompletionFunc("revision", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) == 1 { - return compListRevisions(cfg, args[0]) + return compListRevisions(toComplete, cfg, args[0]) } return nil, cobra.ShellCompDirectiveNoFileComp }) diff --git a/cmd/helm/get_notes.go b/cmd/helm/get_notes.go index 88d494fc4..a9d29ce49 100644 --- a/cmd/helm/get_notes.go +++ b/cmd/helm/get_notes.go @@ -61,7 +61,7 @@ func newGetNotesCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { f.IntVar(&client.Version, "revision", 0, "get the named release with revision") err := cmd.RegisterFlagCompletionFunc("revision", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) == 1 { - return compListRevisions(cfg, args[0]) + return compListRevisions(toComplete, cfg, args[0]) } return nil, cobra.ShellCompDirectiveNoFileComp }) diff --git a/cmd/helm/get_values.go b/cmd/helm/get_values.go index fa7b10fed..c8c87c033 100644 --- a/cmd/helm/get_values.go +++ b/cmd/helm/get_values.go @@ -65,7 +65,7 @@ func newGetValuesCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { f.IntVar(&client.Version, "revision", 0, "get the named release with revision") err := cmd.RegisterFlagCompletionFunc("revision", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) == 1 { - return compListRevisions(cfg, args[0]) + return compListRevisions(toComplete, cfg, args[0]) } return nil, cobra.ShellCompDirectiveNoFileComp }) diff --git a/cmd/helm/history.go b/cmd/helm/history.go index b49c52990..f55eea9fd 100644 --- a/cmd/helm/history.go +++ b/cmd/helm/history.go @@ -20,6 +20,7 @@ import ( "fmt" "io" "strconv" + "strings" "time" "github.com/gosuri/uitable" @@ -184,15 +185,18 @@ func min(x, y int) int { return y } -func compListRevisions(cfg *action.Configuration, releaseName string) ([]string, cobra.ShellCompDirective) { +func compListRevisions(toComplete string, cfg *action.Configuration, releaseName string) ([]string, cobra.ShellCompDirective) { client := action.NewHistory(cfg) var revisions []string if hist, err := client.Run(releaseName); err == nil { for _, release := range hist { - revisions = append(revisions, strconv.Itoa(release.Version)) + version := strconv.Itoa(release.Version) + if strings.HasPrefix(version, toComplete) { + revisions = append(revisions, version) + } } - return revisions, cobra.ShellCompDirectiveDefault + return revisions, cobra.ShellCompDirectiveNoFileComp } return nil, cobra.ShellCompDirectiveError } diff --git a/cmd/helm/rollback.go b/cmd/helm/rollback.go index 3b336d0ff..4849913a1 100644 --- a/cmd/helm/rollback.go +++ b/cmd/helm/rollback.go @@ -47,10 +47,15 @@ func newRollbackCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { Long: rollbackDesc, Args: require.MinimumNArgs(1), ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - if len(args) != 0 { - return nil, cobra.ShellCompDirectiveNoFileComp + if len(args) == 0 { + return compListReleases(toComplete, cfg) } - return compListReleases(toComplete, cfg) + + if len(args) == 1 { + return compListRevisions(toComplete, cfg, args[0]) + } + + return nil, cobra.ShellCompDirectiveNoFileComp }, RunE: func(cmd *cobra.Command, args []string) error { if len(args) > 1 { diff --git a/cmd/helm/rollback_test.go b/cmd/helm/rollback_test.go index fdc627b5f..c11a7fca6 100644 --- a/cmd/helm/rollback_test.go +++ b/cmd/helm/rollback_test.go @@ -68,3 +68,39 @@ func TestRollbackCmd(t *testing.T) { }} runTestCmd(t, tests) } + +func TestRollbackRevisionCompletion(t *testing.T) { + mk := func(name string, vers int, status release.Status) *release.Release { + return release.Mock(&release.MockReleaseOptions{ + Name: name, + Version: vers, + Status: status, + }) + } + + releases := []*release.Release{ + mk("musketeers", 11, release.StatusDeployed), + mk("musketeers", 10, release.StatusSuperseded), + mk("musketeers", 9, release.StatusSuperseded), + mk("musketeers", 8, release.StatusSuperseded), + mk("carabins", 1, release.StatusSuperseded), + } + + tests := []cmdTestCase{{ + name: "completion for release parameter", + cmd: "__complete rollback ''", + rels: releases, + golden: "output/rollback-comp.txt", + }, { + name: "completion for revision parameter", + cmd: "__complete rollback musketeers ''", + rels: releases, + golden: "output/revision-comp.txt", + }, { + name: "completion for with too many args", + cmd: "__complete rollback musketeers 11 ''", + rels: releases, + golden: "output/rollback-wrong-args-comp.txt", + }} + runTestCmd(t, tests) +} diff --git a/cmd/helm/status.go b/cmd/helm/status.go index 5913d08df..abd32a9c2 100644 --- a/cmd/helm/status.go +++ b/cmd/helm/status.go @@ -78,7 +78,7 @@ func newStatusCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { err := cmd.RegisterFlagCompletionFunc("revision", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) == 1 { - return compListRevisions(cfg, args[0]) + return compListRevisions(toComplete, cfg, args[0]) } return nil, cobra.ShellCompDirectiveNoFileComp }) diff --git a/cmd/helm/testdata/output/revision-comp.txt b/cmd/helm/testdata/output/revision-comp.txt index d06947d9d..50f7a9092 100644 --- a/cmd/helm/testdata/output/revision-comp.txt +++ b/cmd/helm/testdata/output/revision-comp.txt @@ -2,5 +2,5 @@ 9 10 11 -:0 -Completion ended with directive: ShellCompDirectiveDefault +:4 +Completion ended with directive: ShellCompDirectiveNoFileComp diff --git a/cmd/helm/testdata/output/rollback-comp.txt b/cmd/helm/testdata/output/rollback-comp.txt new file mode 100644 index 000000000..f7741af12 --- /dev/null +++ b/cmd/helm/testdata/output/rollback-comp.txt @@ -0,0 +1,4 @@ +carabins +musketeers +:4 +Completion ended with directive: ShellCompDirectiveNoFileComp diff --git a/cmd/helm/testdata/output/rollback-wrong-args-comp.txt b/cmd/helm/testdata/output/rollback-wrong-args-comp.txt new file mode 100644 index 000000000..8d9fad576 --- /dev/null +++ b/cmd/helm/testdata/output/rollback-wrong-args-comp.txt @@ -0,0 +1,2 @@ +:4 +Completion ended with directive: ShellCompDirectiveNoFileComp From 7ec501155d082d2e2cc314d7fd03729b5873f21c Mon Sep 17 00:00:00 2001 From: Guangwen Feng Date: Mon, 29 Jun 2020 15:20:23 +0800 Subject: [PATCH 152/281] Fix golint issue Signed-off-by: Guangwen Feng --- internal/experimental/registry/client_opts.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/experimental/registry/client_opts.go b/internal/experimental/registry/client_opts.go index 76b527492..e2f742aec 100644 --- a/internal/experimental/registry/client_opts.go +++ b/internal/experimental/registry/client_opts.go @@ -61,7 +61,7 @@ func ClientOptCache(cache *Cache) ClientOption { } } -// ClientOptCache returns a function that sets the cache setting on a client options set +// ClientOptCredentialsFile returns a function that sets the cache setting on a client options set func ClientOptCredentialsFile(credentialsFile string) ClientOption { return func(client *Client) { client.credentialsFile = credentialsFile From c6a00e63ef638c14be5560534c339484e35eb7ba Mon Sep 17 00:00:00 2001 From: zouyu Date: Mon, 29 Jun 2020 16:36:27 +0800 Subject: [PATCH 153/281] Fix some go-lint warnings Signed-off-by: zouyu --- pkg/postrender/postrender.go | 2 +- pkg/storage/storage.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/postrender/postrender.go b/pkg/postrender/postrender.go index 76f0f5a74..3af384290 100644 --- a/pkg/postrender/postrender.go +++ b/pkg/postrender/postrender.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -// package postrender contains an interface that can be implemented for custom +// Package postrender contains an interface that can be implemented for custom // post-renderers and an exec implementation that can be used for arbitrary // binaries and scripts package postrender diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go index c195120cd..2dfa3f615 100644 --- a/pkg/storage/storage.go +++ b/pkg/storage/storage.go @@ -27,7 +27,7 @@ import ( "helm.sh/helm/v3/pkg/storage/driver" ) -// The type field of the Kubernetes storage object which stores the Helm release +// HelmStorageType is the type field of the Kubernetes storage object which stores the Helm release // version. It is modified slightly replacing the '/': sh.helm/release.v1 // Note: The version 'v1' is incremented if the release object metadata is // modified between major releases. From 7e9a83184c32864d3d296f3439bb8ffbc1c0d3a3 Mon Sep 17 00:00:00 2001 From: Peter Engelbert <36644727+pmengelbert@users.noreply.github.com> Date: Mon, 29 Jun 2020 16:55:01 -0500 Subject: [PATCH 154/281] Determine chart digest by manifest (#8249) Currently, whenever the chart is printed, the digest of the .tar.gz content layer is printed as the digest. The manifest digest is important for OCI purposes, particularly in pushing to a registry. Resolves #8248. Signed-off-by: Peter Engelbert --- internal/experimental/registry/client.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/experimental/registry/client.go b/internal/experimental/registry/client.go index 540eddfa3..5756030c0 100644 --- a/internal/experimental/registry/client.go +++ b/internal/experimental/registry/client.go @@ -240,7 +240,7 @@ func (c *Client) PrintChartTable() error { // printCacheRefSummary prints out chart ref summary func (c *Client) printCacheRefSummary(r *CacheRefSummary) { fmt.Fprintf(c.out, "ref: %s\n", r.Name) - fmt.Fprintf(c.out, "digest: %s\n", r.Digest.Hex()) + fmt.Fprintf(c.out, "digest: %s\n", r.Manifest.Digest.Hex()) fmt.Fprintf(c.out, "size: %s\n", byteCountBinary(r.Size)) fmt.Fprintf(c.out, "name: %s\n", r.Chart.Metadata.Name) fmt.Fprintf(c.out, "version: %s\n", r.Chart.Metadata.Version) @@ -257,7 +257,7 @@ func (c *Client) getChartTableRows() ([][]interface{}, error) { refsMap[r.Name] = map[string]string{ "name": r.Chart.Metadata.Name, "version": r.Chart.Metadata.Version, - "digest": shortDigest(r.Digest.Hex()), + "digest": shortDigest(r.Manifest.Digest.Hex()), "size": byteCountBinary(r.Size), "created": timeAgo(r.CreatedAt), } From 863588ca69209153863f72327fe8c818c963904a Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Tue, 9 Jun 2020 11:44:04 -0700 Subject: [PATCH 155/281] fix(cmd): display warnings on stderr The warnings introduced when a chart has been deprecated is displayed on standard out. This is a regression for users piping the output of `helm template` from a deprecated chart to `kubectl`. This changes the error message to display on standard error instead. Signed-off-by: Matthew Fisher --- cmd/helm/helm.go | 5 +++++ cmd/helm/install.go | 3 +-- cmd/helm/testdata/output/deprecated-chart.txt | 1 - cmd/helm/upgrade.go | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index fcc7315f5..d4e82bb01 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -56,6 +56,11 @@ func debug(format string, v ...interface{}) { } } +func warning(format string, v ...interface{}) { + format = fmt.Sprintf("WARNING: %s\n", format) + fmt.Fprintf(os.Stderr, format, v...) +} + func initKubeLogs() { pflag.CommandLine.SetNormalizeFunc(wordSepNormalizeFunc) gofs := flag.NewFlagSet("klog", flag.ExitOnError) diff --git a/cmd/helm/install.go b/cmd/helm/install.go index 9024add2f..cdb38a10a 100644 --- a/cmd/helm/install.go +++ b/cmd/helm/install.go @@ -17,7 +17,6 @@ limitations under the License. package main import ( - "fmt" "io" "log" "time" @@ -207,7 +206,7 @@ func runInstall(args []string, client *action.Install, valueOpts *values.Options } if chartRequested.Metadata.Deprecated { - fmt.Fprintln(out, "WARNING: This chart is deprecated") + warning("This chart is deprecated") } if req := chartRequested.Metadata.Dependencies; req != nil { diff --git a/cmd/helm/testdata/output/deprecated-chart.txt b/cmd/helm/testdata/output/deprecated-chart.txt index e5be2c3f1..039d6aef6 100644 --- a/cmd/helm/testdata/output/deprecated-chart.txt +++ b/cmd/helm/testdata/output/deprecated-chart.txt @@ -1,4 +1,3 @@ -WARNING: This chart is deprecated NAME: aeneas LAST DEPLOYED: Fri Sep 2 22:04:05 1977 NAMESPACE: default diff --git a/cmd/helm/upgrade.go b/cmd/helm/upgrade.go index dc3bc9a58..c992e2f6b 100644 --- a/cmd/helm/upgrade.go +++ b/cmd/helm/upgrade.go @@ -148,7 +148,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { } if ch.Metadata.Deprecated { - fmt.Fprintln(out, "WARNING: This chart is deprecated") + warning("This chart is deprecated") } rel, err := client.Run(args[0], ch, vals) From 5396df2e282c61ffb1fc8fa65240c36a0216055f Mon Sep 17 00:00:00 2001 From: zwwhdls Date: Sun, 8 Dec 2019 00:30:16 +0800 Subject: [PATCH 156/281] fix #6116 Signed-off-by: zwwhdls --- pkg/strvals/parser.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pkg/strvals/parser.go b/pkg/strvals/parser.go index c735412e9..3c585cbea 100644 --- a/pkg/strvals/parser.go +++ b/pkg/strvals/parser.go @@ -305,8 +305,15 @@ func (t *parser) listItem(list []interface{}, i int) ([]interface{}, error) { if err != nil { return list, errors.Wrap(err, "error parsing index") } + var crtList []interface{} + if len(list) > i { + // If nested list already exists, take the value of list to next cycle. + crtList = list[i].([]interface{}) + } else { + crtList = list + } // Now we need to get the value after the ]. - list2, err := t.listItem(list, i) + list2, err := t.listItem(crtList, i) if err != nil { return list, err } From c41c72cee980b8763a3701450b4b5da8ebf343a2 Mon Sep 17 00:00:00 2001 From: zwwhdls Date: Sun, 8 Dec 2019 00:41:35 +0800 Subject: [PATCH 157/281] add test case Signed-off-by: zwwhdls --- pkg/strvals/parser_test.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/pkg/strvals/parser_test.go b/pkg/strvals/parser_test.go index 742256153..7c6e6f73b 100644 --- a/pkg/strvals/parser_test.go +++ b/pkg/strvals/parser_test.go @@ -427,7 +427,7 @@ func TestParseInto(t *testing.T) { "inner2": "value2", }, } - input := "outer.inner1=value1,outer.inner3=value3,outer.inner4=4" + input := "outer.inner1=value1,outer.inner3=value3,outer.inner4=4,listOuter[0][0].type=listValue" expect := map[string]interface{}{ "outer": map[string]interface{}{ "inner1": "value1", @@ -435,12 +435,22 @@ func TestParseInto(t *testing.T) { "inner3": "value3", "inner4": 4, }, + "listOuter": [][]interface{}{{map[string]string{ + "type": "listValue", + "status": "alive", + }}, + }, } if err := ParseInto(input, got); err != nil { t.Fatal(err) } + input2 := "listOuter[0][0].status=alive" + if err := ParseInto(input2, got); err != nil { + t.Fatal(err) + } + y1, err := yaml.Marshal(expect) if err != nil { t.Fatal(err) From 4532485fd03a8cb56186c93ffeba285431073429 Mon Sep 17 00:00:00 2001 From: zwwhdls Date: Sun, 8 Dec 2019 10:37:46 +0800 Subject: [PATCH 158/281] fix another extreme case Signed-off-by: zwwhdls --- pkg/strvals/parser.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pkg/strvals/parser.go b/pkg/strvals/parser.go index 3c585cbea..cc5c509da 100644 --- a/pkg/strvals/parser.go +++ b/pkg/strvals/parser.go @@ -301,7 +301,7 @@ func (t *parser) listItem(list []interface{}, i int) ([]interface{}, error) { } case last == '[': // now we have a nested list. Read the index and handle. - i, err := t.keyIndex() + nextI, err := t.keyIndex() if err != nil { return list, errors.Wrap(err, "error parsing index") } @@ -309,15 +309,18 @@ func (t *parser) listItem(list []interface{}, i int) ([]interface{}, error) { if len(list) > i { // If nested list already exists, take the value of list to next cycle. crtList = list[i].([]interface{}) - } else { - crtList = list } // Now we need to get the value after the ]. +<<<<<<< HEAD list2, err := t.listItem(crtList, i) if err != nil { return list, err } return setIndex(list, i, list2) +======= + list2, err := t.listItem(crtList, nextI) + return setIndex(list, i, list2), err +>>>>>>> fix another extreme case case last == '.': // We have a nested object. Send to t.key inner := map[string]interface{}{} From 1b39857ac32165db1ad64f66a03d74f1fa09a3f7 Mon Sep 17 00:00:00 2001 From: zwwhdls Date: Sun, 8 Dec 2019 11:30:11 +0800 Subject: [PATCH 159/281] add test case Signed-off-by: zwwhdls --- pkg/strvals/parser.go | 5 +- pkg/strvals/parser_test.go | 137 ++++++++++++++++++++++++++++--------- 2 files changed, 107 insertions(+), 35 deletions(-) diff --git a/pkg/strvals/parser.go b/pkg/strvals/parser.go index cc5c509da..45aa65eac 100644 --- a/pkg/strvals/parser.go +++ b/pkg/strvals/parser.go @@ -308,7 +308,10 @@ func (t *parser) listItem(list []interface{}, i int) ([]interface{}, error) { var crtList []interface{} if len(list) > i { // If nested list already exists, take the value of list to next cycle. - crtList = list[i].([]interface{}) + existed := list[i] + if existed != nil { + crtList = list[i].([]interface{}) + } } // Now we need to get the value after the ]. <<<<<<< HEAD diff --git a/pkg/strvals/parser_test.go b/pkg/strvals/parser_test.go index 7c6e6f73b..cef98ba0a 100644 --- a/pkg/strvals/parser_test.go +++ b/pkg/strvals/parser_test.go @@ -421,49 +421,118 @@ func TestParseSet(t *testing.T) { } func TestParseInto(t *testing.T) { - got := map[string]interface{}{ - "outer": map[string]interface{}{ - "inner1": "overwrite", - "inner2": "value2", + tests := []struct { + input string + input2 string + got map[string]interface{} + expect map[string]interface{} + err bool + }{ + { + input: "outer.inner1=value1,outer.inner3=value3,outer.inner4=4", + got: map[string]interface{}{ + "outer": map[string]interface{}{ + "inner1": "overwrite", + "inner2": "value2", + }, + }, + expect: map[string]interface{}{ + "outer": map[string]interface{}{ + "inner1": "value1", + "inner2": "value2", + "inner3": "value3", + "inner4": 4, + }}, + err: false, }, - } - input := "outer.inner1=value1,outer.inner3=value3,outer.inner4=4,listOuter[0][0].type=listValue" - expect := map[string]interface{}{ - "outer": map[string]interface{}{ - "inner1": "value1", - "inner2": "value2", - "inner3": "value3", - "inner4": 4, + { + input: "listOuter[0][0].type=listValue", + input2: "listOuter[0][0].status=alive", + got: map[string]interface{}{}, + expect: map[string]interface{}{ + "listOuter": [][]interface{}{{map[string]string{ + "type": "listValue", + "status": "alive", + }}}, + }, + err: false, }, - "listOuter": [][]interface{}{{map[string]string{ - "type": "listValue", - "status": "alive", - }}, + { + input: "listOuter[0][0].type=listValue", + input2: "listOuter[1][0].status=alive", + got: map[string]interface{}{}, + expect: map[string]interface{}{ + "listOuter": [][]interface{}{ + { + map[string]string{"type": "listValue"}, + }, + { + map[string]string{"status": "alive"}, + }, + }, + }, + err: false, + }, + { + input: "listOuter[0][1][0].type=listValue", + input2: "listOuter[0][0][1].status=alive", + got: map[string]interface{}{ + "listOuter": []interface{}{ + []interface{}{ + []interface{}{ + map[string]string{"exited": "old"}, + }, + }, + }, + }, + expect: map[string]interface{}{ + "listOuter": [][][]interface{}{ + { + { + map[string]string{"exited": "old"}, + map[string]string{"status": "alive"}, + }, + { + map[string]string{"type": "listValue"}, + }, + }, + }, + }, + err: false, }, } + for _, tt := range tests { + if err := ParseInto(tt.input, tt.got); err != nil { + t.Fatal(err) + } + if tt.err { + t.Errorf("%s: Expected error. Got nil", tt.input) + } - if err := ParseInto(input, got); err != nil { - t.Fatal(err) - } - - input2 := "listOuter[0][0].status=alive" - if err := ParseInto(input2, got); err != nil { - t.Fatal(err) - } + if tt.input2 != "" { + if err := ParseInto(tt.input2, tt.got); err != nil { + t.Fatal(err) + } + if tt.err { + t.Errorf("%s: Expected error. Got nil", tt.input2) + } + } - 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) - } + y1, err := yaml.Marshal(tt.expect) + if err != nil { + t.Fatal(err) + } + y2, err := yaml.Marshal(tt.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) + if string(y1) != string(y2) { + t.Errorf("%s: Expected:\n%s\nGot:\n%s", tt.input, y1, y2) + } } } + func TestParseIntoString(t *testing.T) { got := map[string]interface{}{ "outer": map[string]interface{}{ From d58a984878ea290e04a135ef037b75e13b7b8df5 Mon Sep 17 00:00:00 2001 From: zwwhdls Date: Wed, 1 Jul 2020 21:29:20 +0800 Subject: [PATCH 160/281] fix conflict Signed-off-by: zwwhdls --- pkg/strvals/parser.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/pkg/strvals/parser.go b/pkg/strvals/parser.go index 45aa65eac..457b99f94 100644 --- a/pkg/strvals/parser.go +++ b/pkg/strvals/parser.go @@ -314,16 +314,11 @@ func (t *parser) listItem(list []interface{}, i int) ([]interface{}, error) { } } // Now we need to get the value after the ]. -<<<<<<< HEAD - list2, err := t.listItem(crtList, i) + list2, err := t.listItem(crtList, nextI) if err != nil { return list, err } return setIndex(list, i, list2) -======= - list2, err := t.listItem(crtList, nextI) - return setIndex(list, i, list2), err ->>>>>>> fix another extreme case case last == '.': // We have a nested object. Send to t.key inner := map[string]interface{}{} From 4ad76e1be278bdf8b842c3775f34301cbed08b72 Mon Sep 17 00:00:00 2001 From: Bridget Kromhout Date: Mon, 6 Jul 2020 14:57:41 -0500 Subject: [PATCH 161/281] Updating for today's actual milestone practices Signed-off-by: Bridget Kromhout --- CONTRIBUTING.md | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e27fa7d19..e4493d24c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -96,22 +96,16 @@ that we're already aware of. It is also worth asking on the Slack channels. ## Milestones -We use milestones to track progress of releases. There are also 2 special milestones used for -helping us keep work organized: `Upcoming - Minor` and `Upcoming - Major` - -`Upcoming - Minor` is used for keeping track of issues that aren't assigned to a specific release -but could easily be addressed in a minor release. `Upcoming - Major` keeps track of issues that will -need to be addressed in a major release. For example, if the current version is `3.2.0` an issue/PR -could fall in to one of 4 different active milestones: `3.2.1`, `3.3.0`, `Upcoming - Minor`, or -`Upcoming - Major`. If an issue pertains to a specific upcoming bug or minor release, it would go -into `3.2.1` or `3.3.0`. If the issue/PR does not have a specific milestone yet, but it is likely -that it will land in a `3.X` release, it should go into `Upcoming - Minor`. If the issue/PR is a -large functionality add or change and/or it breaks compatibility, then it should be added to the -`Upcoming - Major` milestone. An issue that we are not sure we will be doing will not be added to -any milestone. - -A milestone (and hence release) is considered done when all outstanding issues/PRs have been closed -or moved to another milestone. +We use milestones to track progress of specific planned releases. + +For example, if the latest currently-released version is `3.2.1`, an issue/PR which pertains to a +specific upcoming bugfix or feature release could fall into one of two different active milestones: +`3.2.2` or `3.3.0`. + +An issue that we are not sure we will be addressing will not be added to any milestone. + +A milestone (and hence release) can be closed when all outstanding issues/PRs have been closed +or moved to another milestone and the associated release has been published. ## Semantic Versioning From 3d05f96b7e505e1f36c3f0f2842d021d879f3231 Mon Sep 17 00:00:00 2001 From: Bridget Kromhout Date: Mon, 6 Jul 2020 19:43:01 -0500 Subject: [PATCH 162/281] Adding v4 link Signed-off-by: Bridget Kromhout --- CONTRIBUTING.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e4493d24c..a8028dd01 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -102,7 +102,9 @@ For example, if the latest currently-released version is `3.2.1`, an issue/PR wh specific upcoming bugfix or feature release could fall into one of two different active milestones: `3.2.2` or `3.3.0`. -An issue that we are not sure we will be addressing will not be added to any milestone. +Issues and PRs which are deemed backwards-incompatible may be added to the discussion items for +Helm 4 with [label:v4.x](https://github.com/helm/helm/labels/v4.x). An issue or PR that we are not +sure we will be addressing will not be added to any milestone. A milestone (and hence release) can be closed when all outstanding issues/PRs have been closed or moved to another milestone and the associated release has been published. From ceff32d5f8aa173549426625b608137a47981447 Mon Sep 17 00:00:00 2001 From: DongGang Date: Tue, 7 Jul 2020 23:44:22 +0800 Subject: [PATCH 163/281] fix(template):Issue:helm template with --output-dir (#8156) * fix(template):Issue:helm template with --output-dir doesn't write template with a hook to file Close #7836 Signed-off-by: Dong Gang * fix go file style Signed-off-by: Dong Gang * fix go file style Signed-off-by: Dong Gang --- cmd/helm/template.go | 6 ------ pkg/action/action.go | 20 +++++++++++++++++++- pkg/action/install.go | 2 +- pkg/action/install_test.go | 6 ++++++ pkg/action/upgrade.go | 2 +- 5 files changed, 27 insertions(+), 9 deletions(-) diff --git a/cmd/helm/template.go b/cmd/helm/template.go index 9ea61b17a..5a8a25102 100644 --- a/cmd/helm/template.go +++ b/cmd/helm/template.go @@ -80,12 +80,6 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { var manifests bytes.Buffer 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) - } - } - // if we have a list of files to render, then check that each of the // provided files exists in the chart. if len(showFiles) > 0 { diff --git a/pkg/action/action.go b/pkg/action/action.go index bb9ef5f71..698ebd23f 100644 --- a/pkg/action/action.go +++ b/pkg/action/action.go @@ -97,7 +97,7 @@ type Configuration struct { // renderResources renders the templates in a chart // // TODO: This function is badly in need of a refactor. -func (c *Configuration) renderResources(ch *chart.Chart, values chartutil.Values, releaseName, outputDir string, subNotes, useReleaseName, includeCrds bool, pr postrender.PostRenderer, dryRun bool) ([]*release.Hook, *bytes.Buffer, string, error) { +func (c *Configuration) renderResources(ch *chart.Chart, values chartutil.Values, releaseName, outputDir string, subNotes, useReleaseName, includeCrds bool, disableHooks bool, pr postrender.PostRenderer, dryRun bool) ([]*release.Hook, *bytes.Buffer, string, error) { hs := []*release.Hook{} b := bytes.NewBuffer(nil) @@ -210,6 +210,24 @@ func (c *Configuration) renderResources(ch *chart.Chart, values chartutil.Values } } + if !disableHooks && len(hs) > 0 { + for _, h := range hs { + if outputDir == "" { + fmt.Fprintf(b, "---\n# Source: %s\n%s\n", h.Path, h.Manifest) + } else { + newDir := outputDir + if useReleaseName { + newDir = filepath.Join(outputDir, releaseName) + } + err = writeToFile(newDir, h.Path, h.Manifest, fileWritten[h.Path]) + if err != nil { + return hs, b, "", err + } + fileWritten[h.Path] = true + } + } + } + if pr != nil { b, err = pr.Run(b) if err != nil { diff --git a/pkg/action/install.go b/pkg/action/install.go index 00fb208b0..48a3aeeca 100644 --- a/pkg/action/install.go +++ b/pkg/action/install.go @@ -236,7 +236,7 @@ func (i *Install) Run(chrt *chart.Chart, vals map[string]interface{}) (*release. rel := i.createRelease(chrt, vals) var manifestDoc *bytes.Buffer - rel.Hooks, manifestDoc, rel.Info.Notes, err = i.cfg.renderResources(chrt, valuesToRender, i.ReleaseName, i.OutputDir, i.SubNotes, i.UseReleaseName, i.IncludeCRDs, i.PostRenderer, i.DryRun) + rel.Hooks, manifestDoc, rel.Info.Notes, err = i.cfg.renderResources(chrt, valuesToRender, i.ReleaseName, i.OutputDir, i.SubNotes, i.UseReleaseName, i.IncludeCRDs, i.DisableHooks, i.PostRenderer, i.DryRun) // Even for errors, attach this if available if manifestDoc != nil { rel.Manifest = manifestDoc.String() diff --git a/pkg/action/install_test.go b/pkg/action/install_test.go index 6c4012cfd..4366889ce 100644 --- a/pkg/action/install_test.go +++ b/pkg/action/install_test.go @@ -499,6 +499,9 @@ func TestInstallReleaseOutputDir(t *testing.T) { _, err = os.Stat(filepath.Join(dir, "hello/templates/with-partials")) is.NoError(err) + _, err = os.Stat(filepath.Join(dir, "hello/templates/hooks")) + is.NoError(err) + _, err = os.Stat(filepath.Join(dir, "hello/templates/rbac")) is.NoError(err) @@ -539,6 +542,9 @@ func TestInstallOutputDirWithReleaseName(t *testing.T) { _, err = os.Stat(filepath.Join(newDir, "hello/templates/with-partials")) is.NoError(err) + _, err = os.Stat(filepath.Join(newDir, "hello/templates/hooks")) + is.NoError(err) + _, err = os.Stat(filepath.Join(newDir, "hello/templates/rbac")) is.NoError(err) diff --git a/pkg/action/upgrade.go b/pkg/action/upgrade.go index fc289dbab..921a0eba5 100644 --- a/pkg/action/upgrade.go +++ b/pkg/action/upgrade.go @@ -217,7 +217,7 @@ func (u *Upgrade) prepareUpgrade(name string, chart *chart.Chart, vals map[strin return nil, nil, err } - hooks, manifestDoc, notesTxt, err := u.cfg.renderResources(chart, valuesToRender, "", "", u.SubNotes, false, false, u.PostRenderer, u.DryRun) + hooks, manifestDoc, notesTxt, err := u.cfg.renderResources(chart, valuesToRender, "", "", u.SubNotes, false, false, false, u.PostRenderer, u.DryRun) if err != nil { return nil, nil, err } From fc4a11c131822df4dcb4c8a1e5a1448cc109c39d Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Tue, 7 Jul 2020 10:39:45 -0700 Subject: [PATCH 164/281] bump version to v3.3 Signed-off-by: Matthew Fisher (cherry picked from commit 5c2dfaad847df2ac8f289d278186d048f446c70c) --- cmd/helm/testdata/output/version-client-shorthand.txt | 2 +- cmd/helm/testdata/output/version-client.txt | 2 +- cmd/helm/testdata/output/version-short.txt | 2 +- cmd/helm/testdata/output/version-template.txt | 2 +- cmd/helm/testdata/output/version.txt | 2 +- internal/version/version.go | 2 +- pkg/chartutil/capabilities_test.go | 4 ++-- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cmd/helm/testdata/output/version-client-shorthand.txt b/cmd/helm/testdata/output/version-client-shorthand.txt index d613309fe..910493bc4 100644 --- a/cmd/helm/testdata/output/version-client-shorthand.txt +++ b/cmd/helm/testdata/output/version-client-shorthand.txt @@ -1 +1 @@ -version.BuildInfo{Version:"v3.2", GitCommit:"", GitTreeState:"", GoVersion:""} +version.BuildInfo{Version:"v3.3", GitCommit:"", GitTreeState:"", GoVersion:""} diff --git a/cmd/helm/testdata/output/version-client.txt b/cmd/helm/testdata/output/version-client.txt index d613309fe..910493bc4 100644 --- a/cmd/helm/testdata/output/version-client.txt +++ b/cmd/helm/testdata/output/version-client.txt @@ -1 +1 @@ -version.BuildInfo{Version:"v3.2", GitCommit:"", GitTreeState:"", GoVersion:""} +version.BuildInfo{Version:"v3.3", GitCommit:"", GitTreeState:"", GoVersion:""} diff --git a/cmd/helm/testdata/output/version-short.txt b/cmd/helm/testdata/output/version-short.txt index 4d5034cea..a6c626024 100644 --- a/cmd/helm/testdata/output/version-short.txt +++ b/cmd/helm/testdata/output/version-short.txt @@ -1 +1 @@ -v3.2 +v3.3 diff --git a/cmd/helm/testdata/output/version-template.txt b/cmd/helm/testdata/output/version-template.txt index 7c09e8d57..48c6d2b04 100644 --- a/cmd/helm/testdata/output/version-template.txt +++ b/cmd/helm/testdata/output/version-template.txt @@ -1 +1 @@ -Version: v3.2 \ No newline at end of file +Version: v3.3 \ No newline at end of file diff --git a/cmd/helm/testdata/output/version.txt b/cmd/helm/testdata/output/version.txt index d613309fe..910493bc4 100644 --- a/cmd/helm/testdata/output/version.txt +++ b/cmd/helm/testdata/output/version.txt @@ -1 +1 @@ -version.BuildInfo{Version:"v3.2", GitCommit:"", GitTreeState:"", GoVersion:""} +version.BuildInfo{Version:"v3.3", GitCommit:"", GitTreeState:"", GoVersion:""} diff --git a/internal/version/version.go b/internal/version/version.go index baa65a028..712aae640 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -30,7 +30,7 @@ var ( // Increment major number for new feature additions and behavioral changes. // Increment minor number for bug fixes and performance enhancements. // Increment patch number for critical fixes to existing releases. - version = "v3.2" + version = "v3.3" // metadata is extra build time data metadata = "" diff --git a/pkg/chartutil/capabilities_test.go b/pkg/chartutil/capabilities_test.go index 489a472be..ae9624bca 100644 --- a/pkg/chartutil/capabilities_test.go +++ b/pkg/chartutil/capabilities_test.go @@ -62,7 +62,7 @@ func TestDefaultCapabilities(t *testing.T) { func TestDefaultCapabilitiesHelmVersion(t *testing.T) { hv := DefaultCapabilities.HelmVersion - if hv.Version != "v3.2" { - t.Errorf("Expected default HelmVerison to be v3.2, got %q", hv.Version) + if hv.Version != "v3.3" { + t.Errorf("Expected default HelmVerison to be v3.3, got %q", hv.Version) } } From ebe6abd660eda59b497d683d418e658b323ca4ea Mon Sep 17 00:00:00 2001 From: Michelle Noorali Date: Tue, 7 Jul 2020 19:18:00 -0400 Subject: [PATCH 165/281] chore(OWNERS): move michelleN to emeritus Thank you. Signed-off-by: Michelle Noorali --- OWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OWNERS b/OWNERS index d7dac5514..e7c953077 100644 --- a/OWNERS +++ b/OWNERS @@ -6,7 +6,6 @@ maintainers: - jdolitsky - marckhouzam - mattfarina - - michelleN - prydonius - SlickNik - technosophos @@ -14,6 +13,7 @@ maintainers: - viglesiasce emeritus: - jascott1 + - michelleN - migmartri - nebril - seh From 637a5c649469935e9d0e69873190dc014ce4d9cf Mon Sep 17 00:00:00 2001 From: wawa0210 Date: Mon, 29 Jun 2020 23:51:57 +0800 Subject: [PATCH 166/281] Environment variable for setting the max history for an environment Signed-off-by: wawa0210 --- cmd/helm/root.go | 1 + cmd/helm/upgrade.go | 2 +- pkg/cli/environment.go | 19 +++++++++++++++++++ pkg/cli/environment_test.go | 38 ++++++++++++++++++++++--------------- 4 files changed, 44 insertions(+), 16 deletions(-) diff --git a/cmd/helm/root.go b/cmd/helm/root.go index 20dd1d93b..449009cb5 100644 --- a/cmd/helm/root.go +++ b/cmd/helm/root.go @@ -50,6 +50,7 @@ Environment variables: | $HELM_DATA_HOME | set an alternative location for storing Helm data. | | $HELM_DRIVER | set the backend storage driver. Values are: configmap, secret, memory, postgres | | $HELM_DRIVER_SQL_CONNECTION_STRING | set the connection string the SQL storage driver should use. | +| $HELM_MAX_HISTORY | set the maximum number of helm release history. | | $HELM_NO_PLUGINS | disable plugins. Set HELM_NO_PLUGINS=1 to disable plugins. | | $KUBECONFIG | set an alternative Kubernetes configuration file (default "~/.kube/config") | diff --git a/cmd/helm/upgrade.go b/cmd/helm/upgrade.go index c992e2f6b..dbddaa368 100644 --- a/cmd/helm/upgrade.go +++ b/cmd/helm/upgrade.go @@ -180,7 +180,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { f.BoolVar(&client.ReuseValues, "reuse-values", false, "when upgrading, reuse the last release's values and merge in any overrides from the command line via --set and -f. If '--reset-values' is specified, this is ignored") f.BoolVar(&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.BoolVar(&client.Atomic, "atomic", false, "if set, upgrade process rolls back changes made in case of failed upgrade. The --wait flag will be set automatically if --atomic is used") - f.IntVar(&client.MaxHistory, "history-max", 10, "limit the maximum number of revisions saved per release. Use 0 for no limit") + f.IntVar(&client.MaxHistory, "history-max", settings.MaxHistory, "limit the maximum number of revisions saved per release. Use 0 for no limit") f.BoolVar(&client.CleanupOnFail, "cleanup-on-fail", false, "allow deletion of new resources created in this upgrade when upgrade fails") f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent") f.StringVar(&client.Description, "description", "", "add a custom description") diff --git a/pkg/cli/environment.go b/pkg/cli/environment.go index d62f57a55..a9994f03d 100644 --- a/pkg/cli/environment.go +++ b/pkg/cli/environment.go @@ -33,6 +33,9 @@ import ( "helm.sh/helm/v3/pkg/helmpath" ) +// defaultMaxHistory sets the maximum number of releases to 0: unlimited +const defaultMaxHistory = 10 + // EnvSettings describes all of the environment settings. type EnvSettings struct { namespace string @@ -56,11 +59,14 @@ type EnvSettings struct { RepositoryCache string // PluginsDirectory is the path to the plugins directory. PluginsDirectory string + // MaxHistory is the max release history maintained. + MaxHistory int } func New() *EnvSettings { env := &EnvSettings{ namespace: os.Getenv("HELM_NAMESPACE"), + MaxHistory: envIntOr("HELM_MAX_HISTORY", defaultMaxHistory), KubeContext: os.Getenv("HELM_KUBECONTEXT"), KubeToken: os.Getenv("HELM_KUBETOKEN"), KubeAPIServer: os.Getenv("HELM_KUBEAPISERVER"), @@ -102,6 +108,18 @@ func envOr(name, def string) string { return def } +func envIntOr(name string, def int) int { + if name == "" { + return def + } + envVal := envOr(name, strconv.Itoa(def)) + ret, err := strconv.Atoi(envVal) + if err != nil { + return def + } + return ret +} + func (s *EnvSettings) EnvVars() map[string]string { envvars := map[string]string{ "HELM_BIN": os.Args[0], @@ -114,6 +132,7 @@ func (s *EnvSettings) EnvVars() map[string]string { "HELM_REPOSITORY_CACHE": s.RepositoryCache, "HELM_REPOSITORY_CONFIG": s.RepositoryConfig, "HELM_NAMESPACE": s.Namespace(), + "HELM_MAX_HISTORY": strconv.Itoa(s.MaxHistory), // broken, these are populated from helm flags and not kubeconfig. "HELM_KUBECONTEXT": s.KubeContext, diff --git a/pkg/cli/environment_test.go b/pkg/cli/environment_test.go index fadc2981e..3234a133b 100644 --- a/pkg/cli/environment_test.go +++ b/pkg/cli/environment_test.go @@ -35,29 +35,34 @@ func TestEnvSettings(t *testing.T) { // expected values ns, kcontext string debug bool + maxhistory int }{ { - name: "defaults", - ns: "default", + name: "defaults", + ns: "default", + maxhistory: defaultMaxHistory, }, { - name: "with flags set", - args: "--debug --namespace=myns", - ns: "myns", - debug: true, + name: "with flags set", + args: "--debug --namespace=myns", + ns: "myns", + debug: true, + maxhistory: defaultMaxHistory, }, { - name: "with envvars set", - envvars: map[string]string{"HELM_DEBUG": "1", "HELM_NAMESPACE": "yourns"}, - ns: "yourns", - debug: true, + name: "with envvars set", + envvars: map[string]string{"HELM_DEBUG": "1", "HELM_NAMESPACE": "yourns", "HELM_MAX_HISTORY": "5"}, + ns: "yourns", + maxhistory: 5, + debug: true, }, { - name: "with flags and envvars set", - args: "--debug --namespace=myns", - envvars: map[string]string{"HELM_DEBUG": "1", "HELM_NAMESPACE": "yourns"}, - ns: "myns", - debug: true, + name: "with flags and envvars set", + args: "--debug --namespace=myns", + envvars: map[string]string{"HELM_DEBUG": "1", "HELM_NAMESPACE": "yourns"}, + ns: "myns", + debug: true, + maxhistory: defaultMaxHistory, }, } @@ -84,6 +89,9 @@ func TestEnvSettings(t *testing.T) { if settings.KubeContext != tt.kcontext { t.Errorf("expected kube-context %q, got %q", tt.kcontext, settings.KubeContext) } + if settings.MaxHistory != tt.maxhistory { + t.Errorf("expected maxHistory %d, got %d", tt.maxhistory, settings.MaxHistory) + } }) } } From 2750e4d78181b614776d82031f6ddfece8d020e2 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Thu, 9 Jul 2020 14:31:51 -0600 Subject: [PATCH 167/281] Lint dependencies (#7970) * feat: add dependency tests Signed-off-by: Matt Butcher * replaced on-disk fixtures with in-memory fixtures Signed-off-by: Matt Butcher --- cmd/helm/lint_test.go | 2 +- ...hart-with-bad-subcharts-with-subcharts.txt | 6 +- .../output/lint-chart-with-bad-subcharts.txt | 4 +- .../charts/bad-subchart/Chart.yaml | 2 +- pkg/chartutil/save_test.go | 6 +- pkg/lint/lint.go | 1 + pkg/lint/lint_test.go | 12 ++- pkg/lint/rules/dependencies.go | 82 +++++++++++++++ pkg/lint/rules/dependencies_test.go | 99 +++++++++++++++++++ 9 files changed, 202 insertions(+), 12 deletions(-) create mode 100644 pkg/lint/rules/dependencies.go create mode 100644 pkg/lint/rules/dependencies_test.go diff --git a/cmd/helm/lint_test.go b/cmd/helm/lint_test.go index 7638acb84..9079935d4 100644 --- a/cmd/helm/lint_test.go +++ b/cmd/helm/lint_test.go @@ -27,7 +27,7 @@ func TestLintCmdWithSubchartsFlag(t *testing.T) { name: "lint good chart with bad subcharts", cmd: fmt.Sprintf("lint %s", testChart), golden: "output/lint-chart-with-bad-subcharts.txt", - wantError: false, + wantError: true, }, { name: "lint good chart with bad subcharts using --with-subcharts flag", cmd: fmt.Sprintf("lint --with-subcharts %s", testChart), diff --git a/cmd/helm/testdata/output/lint-chart-with-bad-subcharts-with-subcharts.txt b/cmd/helm/testdata/output/lint-chart-with-bad-subcharts-with-subcharts.txt index cda011b57..e77aa387f 100644 --- a/cmd/helm/testdata/output/lint-chart-with-bad-subcharts-with-subcharts.txt +++ b/cmd/helm/testdata/output/lint-chart-with-bad-subcharts-with-subcharts.txt @@ -1,6 +1,8 @@ ==> Linting testdata/testcharts/chart-with-bad-subcharts [INFO] Chart.yaml: icon is recommended [WARNING] templates/: directory not found +[ERROR] : unable to load chart + error unpacking bad-subchart in chart-with-bad-subcharts: validation: chart.metadata.name is required ==> Linting testdata/testcharts/chart-with-bad-subcharts/charts/bad-subchart [ERROR] Chart.yaml: name is required @@ -8,9 +10,11 @@ [ERROR] Chart.yaml: version is required [INFO] Chart.yaml: icon is recommended [WARNING] templates/: directory not found +[ERROR] : unable to load chart + validation: chart.metadata.name is required ==> Linting testdata/testcharts/chart-with-bad-subcharts/charts/good-subchart [INFO] Chart.yaml: icon is recommended [WARNING] templates/: directory not found -Error: 3 chart(s) linted, 1 chart(s) failed +Error: 3 chart(s) linted, 2 chart(s) failed diff --git a/cmd/helm/testdata/output/lint-chart-with-bad-subcharts.txt b/cmd/helm/testdata/output/lint-chart-with-bad-subcharts.txt index 51f3f3718..265e555f7 100644 --- a/cmd/helm/testdata/output/lint-chart-with-bad-subcharts.txt +++ b/cmd/helm/testdata/output/lint-chart-with-bad-subcharts.txt @@ -1,5 +1,7 @@ ==> Linting testdata/testcharts/chart-with-bad-subcharts [INFO] Chart.yaml: icon is recommended [WARNING] templates/: directory not found +[ERROR] : unable to load chart + error unpacking bad-subchart in chart-with-bad-subcharts: validation: chart.metadata.name is required -1 chart(s) linted, 0 chart(s) failed +Error: 1 chart(s) linted, 1 chart(s) failed diff --git a/cmd/helm/testdata/testcharts/chart-with-bad-subcharts/charts/bad-subchart/Chart.yaml b/cmd/helm/testdata/testcharts/chart-with-bad-subcharts/charts/bad-subchart/Chart.yaml index 0daa5c188..a6754b24f 100644 --- a/cmd/helm/testdata/testcharts/chart-with-bad-subcharts/charts/bad-subchart/Chart.yaml +++ b/cmd/helm/testdata/testcharts/chart-with-bad-subcharts/charts/bad-subchart/Chart.yaml @@ -1 +1 @@ -description: Bad subchart \ No newline at end of file +description: Bad subchart diff --git a/pkg/chartutil/save_test.go b/pkg/chartutil/save_test.go index 306c13cee..3a45b2992 100644 --- a/pkg/chartutil/save_test.go +++ b/pkg/chartutil/save_test.go @@ -30,15 +30,13 @@ import ( "testing" "time" + "helm.sh/helm/v3/internal/test/ensure" "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/chart/loader" ) func TestSave(t *testing.T) { - tmp, err := ioutil.TempDir("", "helm-") - if err != nil { - t.Fatal(err) - } + tmp := ensure.TempDir(t) defer os.RemoveAll(tmp) for _, dest := range []string{tmp, path.Join(tmp, "newdir")} { diff --git a/pkg/lint/lint.go b/pkg/lint/lint.go index 223ead75a..67e76bd3d 100644 --- a/pkg/lint/lint.go +++ b/pkg/lint/lint.go @@ -32,5 +32,6 @@ func All(basedir string, values map[string]interface{}, namespace string, strict rules.Chartfile(&linter) rules.ValuesWithOverrides(&linter, values) rules.Templates(&linter, values, namespace, strict) + rules.Dependencies(&linter) return linter } diff --git a/pkg/lint/lint_test.go b/pkg/lint/lint_test.go index e7ff4cd7a..29ed67026 100644 --- a/pkg/lint/lint_test.go +++ b/pkg/lint/lint_test.go @@ -38,12 +38,12 @@ const goodChartDir = "rules/testdata/goodone" func TestBadChart(t *testing.T) { m := All(badChartDir, values, namespace, strict).Messages - if len(m) != 7 { + if len(m) != 8 { 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 bool + // There should be one INFO, 2 WARNINGs and 2 ERROR messages, check for them + var i, w, e, e2, e3, e4, e5, e6 bool for _, msg := range m { if msg.Severity == support.InfoSev { if strings.Contains(msg.Err.Error(), "icon is recommended") { @@ -74,9 +74,13 @@ func TestBadChart(t *testing.T) { if strings.Contains(msg.Err.Error(), "dependencies are not valid in the Chart file with apiVersion") { e5 = true } + // This comes from the dependency check, which loads dependency info from the Chart.yaml + if strings.Contains(msg.Err.Error(), "unable to load chart") { + e6 = true + } } } - if !e || !e2 || !e3 || !e4 || !e5 || !w || !i { + if !e || !e2 || !e3 || !e4 || !e5 || !w || !i || !e6 { t.Errorf("Didn't find all the expected errors, got %#v", m) } } diff --git a/pkg/lint/rules/dependencies.go b/pkg/lint/rules/dependencies.go new file mode 100644 index 000000000..abecd1feb --- /dev/null +++ b/pkg/lint/rules/dependencies.go @@ -0,0 +1,82 @@ +/* +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 rules // import "helm.sh/helm/v3/pkg/lint/rules" + +import ( + "fmt" + "strings" + + "github.com/pkg/errors" + + "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/chart/loader" + "helm.sh/helm/v3/pkg/lint/support" +) + +// Dependencies runs lints against a chart's dependencies +// +// See https://github.com/helm/helm/issues/7910 +func Dependencies(linter *support.Linter) { + c, err := loader.LoadDir(linter.ChartDir) + if !linter.RunLinterRule(support.ErrorSev, "", validateChartFormat(err)) { + return + } + + linter.RunLinterRule(support.ErrorSev, linter.ChartDir, validateDependencyInMetadata(c)) + linter.RunLinterRule(support.WarningSev, linter.ChartDir, validateDependencyInChartsDir(c)) +} + +func validateChartFormat(chartError error) error { + if chartError != nil { + return errors.Errorf("unable to load chart\n\t%s", chartError) + } + return nil +} + +func validateDependencyInChartsDir(c *chart.Chart) (err error) { + dependencies := map[string]struct{}{} + missing := []string{} + for _, dep := range c.Dependencies() { + dependencies[dep.Metadata.Name] = struct{}{} + } + for _, dep := range c.Metadata.Dependencies { + if _, ok := dependencies[dep.Name]; !ok { + missing = append(missing, dep.Name) + } + } + if len(missing) > 0 { + err = fmt.Errorf("chart directory is missing these dependencies: %s", strings.Join(missing, ",")) + } + return err +} + +func validateDependencyInMetadata(c *chart.Chart) (err error) { + dependencies := map[string]struct{}{} + missing := []string{} + for _, dep := range c.Metadata.Dependencies { + dependencies[dep.Name] = struct{}{} + } + for _, dep := range c.Dependencies() { + if _, ok := dependencies[dep.Metadata.Name]; !ok { + missing = append(missing, dep.Metadata.Name) + } + } + if len(missing) > 0 { + err = fmt.Errorf("chart metadata is missing these dependencies: %s", strings.Join(missing, ",")) + } + return err +} diff --git a/pkg/lint/rules/dependencies_test.go b/pkg/lint/rules/dependencies_test.go new file mode 100644 index 000000000..075190eac --- /dev/null +++ b/pkg/lint/rules/dependencies_test.go @@ -0,0 +1,99 @@ +/* +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 rules + +import ( + "os" + "path/filepath" + "testing" + + "helm.sh/helm/v3/internal/test/ensure" + "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/chartutil" + "helm.sh/helm/v3/pkg/lint/support" +) + +func chartWithBadDependencies() chart.Chart { + badChartDeps := chart.Chart{ + Metadata: &chart.Metadata{ + Name: "badchart", + Version: "0.1.0", + APIVersion: "v2", + Dependencies: []*chart.Dependency{ + { + Name: "sub2", + }, + { + Name: "sub3", + }, + }, + }, + } + + badChartDeps.SetDependencies( + &chart.Chart{ + Metadata: &chart.Metadata{ + Name: "sub1", + Version: "0.1.0", + APIVersion: "v2", + }, + }, + &chart.Chart{ + Metadata: &chart.Metadata{ + Name: "sub2", + Version: "0.1.0", + APIVersion: "v2", + }, + }, + ) + return badChartDeps +} + +func TestValidateDependencyInChartsDir(t *testing.T) { + c := chartWithBadDependencies() + + if err := validateDependencyInChartsDir(&c); err == nil { + t.Error("chart should have been flagged for missing deps in chart directory") + } +} + +func TestValidateDependencyInMetadata(t *testing.T) { + c := chartWithBadDependencies() + + if err := validateDependencyInMetadata(&c); err == nil { + t.Errorf("chart should have been flagged for missing deps in chart metadata") + } +} + +func TestDependencies(t *testing.T) { + tmp := ensure.TempDir(t) + defer os.RemoveAll(tmp) + + c := chartWithBadDependencies() + err := chartutil.SaveDir(&c, tmp) + if err != nil { + t.Fatal(err) + } + linter := support.Linter{ChartDir: filepath.Join(tmp, c.Metadata.Name)} + + Dependencies(&linter) + if l := len(linter.Messages); l != 2 { + t.Errorf("expected 2 linter errors for bad chart dependencies. Got %d.", l) + for i, msg := range linter.Messages { + t.Logf("Message: %d, Error: %#v", i, msg) + } + } +} From 971c9215d19ea5ab63f6e3eca971e186442564d6 Mon Sep 17 00:00:00 2001 From: Jack Weldon Date: Fri, 10 Jul 2020 15:44:39 -0400 Subject: [PATCH 168/281] feat(helm): prompt for password with helm repo add Previously in Helm 2, 'helm repo add' would prompt for your password if you only provided the --username flag. This helps prevent someone's credentials from being logged in their shell's history, from showing up in process lists, and from being seen by others nearby. Closes #7174 Signed-off-by: Jack Weldon --- cmd/helm/repo_add.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/cmd/helm/repo_add.go b/cmd/helm/repo_add.go index 9c67641e5..3abb58334 100644 --- a/cmd/helm/repo_add.go +++ b/cmd/helm/repo_add.go @@ -24,11 +24,13 @@ import ( "os" "path/filepath" "strings" + "syscall" "time" "github.com/gofrs/flock" "github.com/pkg/errors" "github.com/spf13/cobra" + "golang.org/x/crypto/ssh/terminal" "sigs.k8s.io/yaml" "helm.sh/helm/v3/cmd/helm/require" @@ -114,6 +116,16 @@ func (o *repoAddOptions) run(out io.Writer) error { return errors.Errorf("repository name (%s) already exists, please specify a different name", o.name) } + if o.username != "" && o.password == "" { + fmt.Fprint(out, "Password:") + password, err := terminal.ReadPassword(syscall.Stdin) + fmt.Fprintln(out) + if err != nil { + return err + } + o.password = string(password) + } + c := repo.Entry{ Name: o.name, URL: o.url, From ac1a25517b5ee5c13556a2d4eff313a2909ee497 Mon Sep 17 00:00:00 2001 From: Jack Weldon Date: Fri, 10 Jul 2020 17:59:20 -0400 Subject: [PATCH 169/281] address PR comment, adding whitespace for formatting Signed-off-by: Jack Weldon --- 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 3abb58334..2fc697ff0 100644 --- a/cmd/helm/repo_add.go +++ b/cmd/helm/repo_add.go @@ -117,7 +117,7 @@ func (o *repoAddOptions) run(out io.Writer) error { } if o.username != "" && o.password == "" { - fmt.Fprint(out, "Password:") + fmt.Fprint(out, "Password: ") password, err := terminal.ReadPassword(syscall.Stdin) fmt.Fprintln(out) if err != nil { From 7ba8343b8d0edc1a3de96ee15003b072c7e72627 Mon Sep 17 00:00:00 2001 From: Jack Weldon Date: Fri, 10 Jul 2020 19:51:38 -0400 Subject: [PATCH 170/281] fix windows build failure caused by #8431 No longer using the 'syscall' package, as further reading into this issue has shown that 'syscall' is deprecated/locked down. Additional issues posted on Golang's github indicates that the newer preferred mechanism to get the file descriptor for stdin is: int(os.Stdin.Fd()) Signed-off-by: Jack Weldon --- cmd/helm/repo_add.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/helm/repo_add.go b/cmd/helm/repo_add.go index 2fc697ff0..418a549c6 100644 --- a/cmd/helm/repo_add.go +++ b/cmd/helm/repo_add.go @@ -24,7 +24,6 @@ import ( "os" "path/filepath" "strings" - "syscall" "time" "github.com/gofrs/flock" @@ -117,8 +116,9 @@ func (o *repoAddOptions) run(out io.Writer) error { } if o.username != "" && o.password == "" { + fd := int(os.Stdin.Fd()) fmt.Fprint(out, "Password: ") - password, err := terminal.ReadPassword(syscall.Stdin) + password, err := terminal.ReadPassword(fd) fmt.Fprintln(out) if err != nil { return err From 8217aba4a6cf04bac538ba2178603ec1378dbaef Mon Sep 17 00:00:00 2001 From: Hidde Beydals Date: Mon, 13 Jul 2020 10:53:26 +0200 Subject: [PATCH 171/281] fix(kube): use logger instead of fmt.Printf Signed-off-by: Hidde Beydals --- pkg/kube/client.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/kube/client.go b/pkg/kube/client.go index f908611db..62e1b104b 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -542,14 +542,14 @@ func (c *Client) waitForPodSuccess(obj runtime.Object, name string) (bool, error switch o.Status.Phase { case v1.PodSucceeded: - fmt.Printf("Pod %s succeeded\n", o.Name) + c.Log("Pod %s succeeded", o.Name) return true, nil case v1.PodFailed: return true, errors.Errorf("pod %s failed", o.Name) case v1.PodPending: - fmt.Printf("Pod %s pending\n", o.Name) + c.Log("Pod %s pending", o.Name) case v1.PodRunning: - fmt.Printf("Pod %s running\n", o.Name) + c.Log("Pod %s running", o.Name) } return false, nil From 57bd8a71b0002b00a13b6a4bfa17e45839b73795 Mon Sep 17 00:00:00 2001 From: Dong Gang Date: Mon, 13 Jul 2020 18:59:09 +0800 Subject: [PATCH 172/281] feature(show): support jsonpath output for `helm show value` Signed-off-by: Dong Gang --- cmd/helm/show.go | 3 +++ pkg/action/show.go | 24 +++++++++++++++++------- pkg/action/show_test.go | 14 ++++++++++++++ 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/cmd/helm/show.go b/cmd/helm/show.go index c122d8476..b374a3594 100644 --- a/cmd/helm/show.go +++ b/cmd/helm/show.go @@ -151,6 +151,9 @@ func addShowFlags(subCmd *cobra.Command, client *action.Show) { f := subCmd.Flags() f.BoolVar(&client.Devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored") + if subCmd.Name() == "values" { + f.StringVar(&client.JsonPathTemplate, "jsonpath", "", "use jsonpath output format") + } addChartPathOptionsFlags(f, &client.ChartPathOptions) err := subCmd.RegisterFlagCompletionFunc("version", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { diff --git a/pkg/action/show.go b/pkg/action/show.go index 9baa9cf43..a5544492b 100644 --- a/pkg/action/show.go +++ b/pkg/action/show.go @@ -20,11 +20,12 @@ import ( "fmt" "strings" + "helm.sh/helm/v3/pkg/chartutil" + "k8s.io/cli-runtime/pkg/printers" "sigs.k8s.io/yaml" "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/chart/loader" - "helm.sh/helm/v3/pkg/chartutil" ) // ShowOutputFormat is the format of the output of `helm show` @@ -52,9 +53,10 @@ func (o ShowOutputFormat) String() string { // It provides the implementation of 'helm show' and its respective subcommands. type Show struct { ChartPathOptions - Devel bool - OutputFormat ShowOutputFormat - chart *chart.Chart // for testing + Devel bool + OutputFormat ShowOutputFormat + JsonPathTemplate string + chart *chart.Chart // for testing } // NewShow creates a new Show object with the given configuration. @@ -87,9 +89,17 @@ func (s *Show) Run(chartpath string) (string, error) { if s.OutputFormat == ShowAll { fmt.Fprintln(&out, "---") } - for _, f := range s.chart.Raw { - if f.Name == chartutil.ValuesfileName { - fmt.Fprintln(&out, string(f.Data)) + if s.JsonPathTemplate != "" { + printer, err := printers.NewJSONPathPrinter(s.JsonPathTemplate) + if err != nil { + return "", fmt.Errorf("error parsing jsonpath %s, %v\n", s.JsonPathTemplate, err) + } + printer.Execute(&out, s.chart.Values) + } else { + for _, f := range s.chart.Raw { + if f.Name == chartutil.ValuesfileName { + fmt.Fprintln(&out, string(f.Data)) + } } } } diff --git a/pkg/action/show_test.go b/pkg/action/show_test.go index 7be9daaa5..0251a528d 100644 --- a/pkg/action/show_test.go +++ b/pkg/action/show_test.go @@ -69,3 +69,17 @@ func TestShowNoValues(t *testing.T) { t.Errorf("expected empty values buffer, got %s", output) } } + +func TestShowValuesByJsonPathFormat(t *testing.T) { + client := NewShow(ShowValues) + client.JsonPathTemplate = "{$.nestedKey.simpleKey}" + client.chart = buildChart(withSampleValues()) + output, err := client.Run("") + if err != nil { + t.Fatal(err) + } + expect := "simpleValue" + if output != expect { + t.Errorf("Expected\n%q\nGot\n%q\n", expect, output) + } +} From 1dfe66aa85bf09cd1f09271c2810c5deb9f22454 Mon Sep 17 00:00:00 2001 From: Dong Gang Date: Mon, 13 Jul 2020 19:11:40 +0800 Subject: [PATCH 173/281] fix the code style error Signed-off-by: Dong Gang --- cmd/helm/show.go | 2 +- pkg/action/show.go | 10 +++++----- pkg/action/show_test.go | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cmd/helm/show.go b/cmd/helm/show.go index b374a3594..60ed0ab8e 100644 --- a/cmd/helm/show.go +++ b/cmd/helm/show.go @@ -152,7 +152,7 @@ func addShowFlags(subCmd *cobra.Command, client *action.Show) { f.BoolVar(&client.Devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored") if subCmd.Name() == "values" { - f.StringVar(&client.JsonPathTemplate, "jsonpath", "", "use jsonpath output format") + f.StringVar(&client.JSONPathTemplate, "jsonpath", "", "use jsonpath output format") } addChartPathOptionsFlags(f, &client.ChartPathOptions) diff --git a/pkg/action/show.go b/pkg/action/show.go index a5544492b..73d989884 100644 --- a/pkg/action/show.go +++ b/pkg/action/show.go @@ -20,12 +20,12 @@ import ( "fmt" "strings" - "helm.sh/helm/v3/pkg/chartutil" "k8s.io/cli-runtime/pkg/printers" "sigs.k8s.io/yaml" "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/chart/loader" + "helm.sh/helm/v3/pkg/chartutil" ) // ShowOutputFormat is the format of the output of `helm show` @@ -55,7 +55,7 @@ type Show struct { ChartPathOptions Devel bool OutputFormat ShowOutputFormat - JsonPathTemplate string + JSONPathTemplate string chart *chart.Chart // for testing } @@ -89,10 +89,10 @@ func (s *Show) Run(chartpath string) (string, error) { if s.OutputFormat == ShowAll { fmt.Fprintln(&out, "---") } - if s.JsonPathTemplate != "" { - printer, err := printers.NewJSONPathPrinter(s.JsonPathTemplate) + if s.JSONPathTemplate != "" { + printer, err := printers.NewJSONPathPrinter(s.JSONPathTemplate) if err != nil { - return "", fmt.Errorf("error parsing jsonpath %s, %v\n", s.JsonPathTemplate, err) + return "", fmt.Errorf("error parsing jsonpath %s, %v", s.JSONPathTemplate, err) } printer.Execute(&out, s.chart.Values) } else { diff --git a/pkg/action/show_test.go b/pkg/action/show_test.go index 0251a528d..a2efdc8ac 100644 --- a/pkg/action/show_test.go +++ b/pkg/action/show_test.go @@ -72,7 +72,7 @@ func TestShowNoValues(t *testing.T) { func TestShowValuesByJsonPathFormat(t *testing.T) { client := NewShow(ShowValues) - client.JsonPathTemplate = "{$.nestedKey.simpleKey}" + client.JSONPathTemplate = "{$.nestedKey.simpleKey}" client.chart = buildChart(withSampleValues()) output, err := client.Run("") if err != nil { From fe40c4bf844feee87c3b3c1163b7f5f40dc9a793 Mon Sep 17 00:00:00 2001 From: Bridget Kromhout Date: Mon, 13 Jul 2020 15:31:26 -0500 Subject: [PATCH 174/281] Clarify comments to match practice Signed-off-by: Bridget Kromhout --- internal/version/version.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/version/version.go b/internal/version/version.go index 712aae640..6154ff3a4 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -25,11 +25,9 @@ import ( var ( // version is the current version of Helm. // Update this whenever making a new release. - // The version is of the format Major.Minor.Patch[-Prerelease][+BuildMetadata] // // Increment major number for new feature additions and behavioral changes. // Increment minor number for bug fixes and performance enhancements. - // Increment patch number for critical fixes to existing releases. version = "v3.3" // metadata is extra build time data From 5684c864908819ad43dfa6efb6f474611d721e7c Mon Sep 17 00:00:00 2001 From: Bridget Kromhout Date: Mon, 13 Jul 2020 15:37:12 -0500 Subject: [PATCH 175/281] Fixing version and spelling errors Signed-off-by: Bridget Kromhout --- pkg/chartutil/capabilities_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/chartutil/capabilities_test.go b/pkg/chartutil/capabilities_test.go index ae9624bca..66eeee755 100644 --- a/pkg/chartutil/capabilities_test.go +++ b/pkg/chartutil/capabilities_test.go @@ -55,7 +55,7 @@ func TestDefaultCapabilities(t *testing.T) { t.Errorf("Expected default KubeVersion.Major to be 1, got %q", kv.Major) } if kv.Minor != "18" { - t.Errorf("Expected default KubeVersion.Minor to be 16, got %q", kv.Minor) + t.Errorf("Expected default KubeVersion.Minor to be 18, got %q", kv.Minor) } } @@ -63,6 +63,6 @@ func TestDefaultCapabilitiesHelmVersion(t *testing.T) { hv := DefaultCapabilities.HelmVersion if hv.Version != "v3.3" { - t.Errorf("Expected default HelmVerison to be v3.3, got %q", hv.Version) + t.Errorf("Expected default HelmVersion to be v3.3, got %q", hv.Version) } } From 9a4f4ec64b8092d2ba3d7493001837df4737e36c Mon Sep 17 00:00:00 2001 From: Cristian Klein Date: Thu, 2 Jan 2020 00:23:10 +0100 Subject: [PATCH 176/281] fix(helm): Avoid corrupting storage via a lock If two `helm upgrade`s are executed at the exact same time, then one of the invocations will fail with "already exists". If one `helm upgrade` is executed and a second one is started while the first is in `pending-upgrade`, then the second invocation will create a new release. Effectively, two helm invocations will simultaneously change the state of Kubernetes resources -- which is scary -- then two releases will be in `deployed` state -- which can cause other issues. This commit fixes the corrupted storage problem, by introducting a poor person's lock. If the last release is in a pending state, then helm will abort. If the last release is in a pending state, due to a previously killed helm, then the user is expected to do `helm rollback`. Closes #7274 Signed-off-by: Cristian Klein --- pkg/action/action.go | 2 ++ pkg/action/upgrade.go | 5 +++++ pkg/release/status.go | 5 +++++ 3 files changed, 12 insertions(+) diff --git a/pkg/action/action.go b/pkg/action/action.go index 698ebd23f..fec86cd42 100644 --- a/pkg/action/action.go +++ b/pkg/action/action.go @@ -60,6 +60,8 @@ var ( errInvalidRevision = errors.New("invalid release revision") // errInvalidName indicates that an invalid release name was provided errInvalidName = errors.New("invalid release name, must match regex ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])+$ and the length must not longer than 53") + // errPending indicates that another instance of Helm is already applying an operation on a release. + errPending = errors.New("another operation (install/upgrade/rollback) is in progress") ) // ValidName is a regular expression for resource names. diff --git a/pkg/action/upgrade.go b/pkg/action/upgrade.go index 921a0eba5..949aebcae 100644 --- a/pkg/action/upgrade.go +++ b/pkg/action/upgrade.go @@ -170,6 +170,11 @@ func (u *Upgrade) prepareUpgrade(name string, chart *chart.Chart, vals map[strin return nil, nil, err } + // Concurrent `helm upgrade`s will either fail here with `errPending` or when creating the release with "already exists". This should act as a pessimistic lock. + if lastRelease.Info.Status.IsPending() { + return nil, nil, errPending + } + var currentRelease *release.Release if lastRelease.Info.Status == release.StatusDeployed { // no need to retrieve the last deployed release from storage as the last release is deployed diff --git a/pkg/release/status.go b/pkg/release/status.go index 49b0f1544..e0e3ed62a 100644 --- a/pkg/release/status.go +++ b/pkg/release/status.go @@ -42,3 +42,8 @@ const ( ) func (x Status) String() string { return string(x) } + +// IsPending determines if this status is a state or a transition. +func (x Status) IsPending() bool { + return x == StatusPendingInstall || x == StatusPendingUpgrade || x == StatusPendingRollback +} From 20fb7bac4e9001751a0b04c71006b4bb506378cf Mon Sep 17 00:00:00 2001 From: Cristian Klein Date: Thu, 9 Jan 2020 00:38:47 +0100 Subject: [PATCH 177/281] fix(helm): Added test for concurrent upgrades Signed-off-by: Cristian Klein --- pkg/action/upgrade_test.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/pkg/action/upgrade_test.go b/pkg/action/upgrade_test.go index f25d115c4..f16de6479 100644 --- a/pkg/action/upgrade_test.go +++ b/pkg/action/upgrade_test.go @@ -253,3 +253,23 @@ func TestUpgradeRelease_ReuseValues(t *testing.T) { is.Equal(expectedValues, updatedRes.Config) }) } + +func TestUpgradeRelease_Pending(t *testing.T) { + req := require.New(t) + + upAction := upgradeAction(t) + rel := releaseStub() + rel.Name = "come-fail-away" + rel.Info.Status = release.StatusDeployed + upAction.cfg.Releases.Create(rel) + rel2 := releaseStub() + rel2.Name = "come-fail-away" + rel2.Info.Status = release.StatusPendingUpgrade + rel2.Version = 2 + upAction.cfg.Releases.Create(rel2) + + vals := map[string]interface{}{} + + _, err := upAction.Run(rel.Name, buildChart(), vals) + req.Contains(err.Error(), "progress", err) +} From 08517a9ec0abbf238d56ec97de80e6ecc8946ab6 Mon Sep 17 00:00:00 2001 From: Bridget Kromhout Date: Mon, 13 Jul 2020 16:04:15 -0500 Subject: [PATCH 178/281] Correct make target in Makefile Signed-off-by: Bridget Kromhout --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 524421279..f8325480a 100644 --- a/Makefile +++ b/Makefile @@ -183,7 +183,7 @@ clean: .PHONY: release-notes release-notes: @if [ ! -d "./_dist" ]; then \ - echo "please run 'make fetch-release' first" && \ + echo "please run 'make fetch-dist' first" && \ exit 1; \ fi @if [ -z "${PREVIOUS_RELEASE}" ]; then \ From 0d70c63396083f868963797632715f06c9b90ca9 Mon Sep 17 00:00:00 2001 From: Cristian Klein Date: Mon, 13 Jul 2020 23:41:25 +0200 Subject: [PATCH 179/281] fix(helm): Update test during pending install Signed-off-by: Cristian Klein --- cmd/helm/testdata/output/upgrade-with-pending-install.txt | 1 + cmd/helm/upgrade_test.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 cmd/helm/testdata/output/upgrade-with-pending-install.txt diff --git a/cmd/helm/testdata/output/upgrade-with-pending-install.txt b/cmd/helm/testdata/output/upgrade-with-pending-install.txt new file mode 100644 index 000000000..57a8e7873 --- /dev/null +++ b/cmd/helm/testdata/output/upgrade-with-pending-install.txt @@ -0,0 +1 @@ +Error: UPGRADE FAILED: another operation (install/upgrade/rollback) is in progress diff --git a/cmd/helm/upgrade_test.go b/cmd/helm/upgrade_test.go index 7e88bc0df..64daf2934 100644 --- a/cmd/helm/upgrade_test.go +++ b/cmd/helm/upgrade_test.go @@ -158,7 +158,7 @@ func TestUpgradeCmd(t *testing.T) { { name: "upgrade a pending install release", cmd: fmt.Sprintf("upgrade funny-bunny '%s'", chartPath), - golden: "output/upgrade-with-bad-or-missing-existing-release.txt", + golden: "output/upgrade-with-pending-install.txt", wantError: true, rels: []*release.Release{relWithStatusMock("funny-bunny", 2, ch, release.StatusPendingInstall)}, }, From 4be3804c50e4a4260e8e49680e2826c66e32d898 Mon Sep 17 00:00:00 2001 From: wawa0210 Date: Thu, 9 Jul 2020 17:40:36 +0800 Subject: [PATCH 180/281] Rollback command support for max history Signed-off-by: wawa0210 --- cmd/helm/rollback.go | 1 + pkg/action/rollback.go | 3 +++ 2 files changed, 4 insertions(+) diff --git a/cmd/helm/rollback.go b/cmd/helm/rollback.go index 4849913a1..2cd6fa2cb 100644 --- a/cmd/helm/rollback.go +++ b/cmd/helm/rollback.go @@ -83,6 +83,7 @@ func newRollbackCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { 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.BoolVar(&client.CleanupOnFail, "cleanup-on-fail", false, "allow deletion of new resources created in this rollback when rollback fails") + f.IntVar(&client.MaxHistory, "history-max", settings.MaxHistory, "limit the maximum number of revisions saved per release. Use 0 for no limit") return cmd } diff --git a/pkg/action/rollback.go b/pkg/action/rollback.go index 81812983f..8773b6271 100644 --- a/pkg/action/rollback.go +++ b/pkg/action/rollback.go @@ -42,6 +42,7 @@ type Rollback struct { Recreate bool // will (if true) recreate pods after a rollback. Force bool // will (if true) force resource upgrade through uninstall/recreate if needed CleanupOnFail bool + MaxHistory int // MaxHistory limits the maximum number of revisions saved per release } // NewRollback creates a new Rollback object with the given configuration. @@ -57,6 +58,8 @@ func (r *Rollback) Run(name string) error { return err } + r.cfg.Releases.MaxHistory = r.MaxHistory + r.cfg.Log("preparing rollback of %s", name) currentRelease, targetRelease, err := r.prepareRollback(name) if err != nil { From c709e10b3204b823f7e00bae8ca59b6db65e9865 Mon Sep 17 00:00:00 2001 From: Bridget Kromhout Date: Tue, 14 Jul 2020 11:05:37 -0500 Subject: [PATCH 181/281] Reinstating comment that is still accurate Signed-off-by: Bridget Kromhout --- internal/version/version.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/version/version.go b/internal/version/version.go index 6154ff3a4..2941bb489 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -25,6 +25,7 @@ import ( var ( // version is the current version of Helm. // Update this whenever making a new release. + // The version is of the format Major.Minor.Patch[-Prerelease][+BuildMetadata] // // Increment major number for new feature additions and behavioral changes. // Increment minor number for bug fixes and performance enhancements. From 3ee7047827b0fc5b87fb2d8bde6bb64fa93fcf73 Mon Sep 17 00:00:00 2001 From: Dong Gang Date: Wed, 15 Jul 2020 11:09:01 +0800 Subject: [PATCH 182/281] polish the help text of flag Signed-off-by: Dong Gang --- cmd/helm/show.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/helm/show.go b/cmd/helm/show.go index 60ed0ab8e..fe5eaee26 100644 --- a/cmd/helm/show.go +++ b/cmd/helm/show.go @@ -152,7 +152,7 @@ func addShowFlags(subCmd *cobra.Command, client *action.Show) { f.BoolVar(&client.Devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored") if subCmd.Name() == "values" { - f.StringVar(&client.JSONPathTemplate, "jsonpath", "", "use jsonpath output format") + f.StringVar(&client.JSONPathTemplate, "jsonpath", "", "supply a JSONPath expression to filter the output") } addChartPathOptionsFlags(f, &client.ChartPathOptions) From 9777925a2ae1e98a3e7ec3923d0b56b7199c7806 Mon Sep 17 00:00:00 2001 From: Dong Gang Date: Thu, 16 Jul 2020 09:10:03 +0800 Subject: [PATCH 183/281] fix(create): update the hook name of test-connection pod Signed-off-by: Dong Gang --- pkg/chartutil/create.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/chartutil/create.go b/pkg/chartutil/create.go index e74761b3f..c036ce37d 100644 --- a/pkg/chartutil/create.go +++ b/pkg/chartutil/create.go @@ -457,7 +457,7 @@ metadata: labels: {{- include ".labels" . | nindent 4 }} annotations: - "helm.sh/hook": test-success + "helm.sh/hook": test spec: containers: - name: wget From 844e575d2a7090903c26e658037e3298ecff9479 Mon Sep 17 00:00:00 2001 From: Simon Shine Date: Fri, 17 Jul 2020 06:33:27 +0200 Subject: [PATCH 184/281] Alter whitespace in "Update Complete" output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most places say "Happy Helming!". But pkg/downloader/manager.go also says "⎈Happy Helming!⎈" Signed-off-by: Simon Shine --- cmd/helm/repo_update.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/helm/repo_update.go b/cmd/helm/repo_update.go index 5c1086e81..cbfc05b00 100644 --- a/cmd/helm/repo_update.go +++ b/cmd/helm/repo_update.go @@ -96,5 +96,5 @@ func updateCharts(repos []*repo.ChartRepository, out io.Writer) { }(re) } wg.Wait() - fmt.Fprintln(out, "Update Complete. ⎈ Happy Helming!⎈ ") + fmt.Fprintln(out, "Update Complete. ⎈Happy Helming!⎈") } From 9a13385022b0976a704c24c8d11e7b0f3561b931 Mon Sep 17 00:00:00 2001 From: Chris Wells Date: Sun, 19 Jul 2020 14:42:04 -0400 Subject: [PATCH 185/281] fix: Allow building in a path containing spaces Signed-off-by: Chris Wells --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index f8325480a..97f99fd86 100644 --- a/Makefile +++ b/Makefile @@ -60,7 +60,7 @@ all: build build: $(BINDIR)/$(BINNAME) $(BINDIR)/$(BINNAME): $(SRC) - GO111MODULE=on go build $(GOFLAGS) -tags '$(TAGS)' -ldflags '$(LDFLAGS)' -o $(BINDIR)/$(BINNAME) ./cmd/helm + GO111MODULE=on go build $(GOFLAGS) -tags '$(TAGS)' -ldflags '$(LDFLAGS)' -o '$(BINDIR)'/$(BINNAME) ./cmd/helm # ------------------------------------------------------------------------------ # test @@ -97,7 +97,7 @@ test-acceptance: TARGETS = linux/amd64 test-acceptance: build build-cross @if [ -d "${ACCEPTANCE_DIR}" ]; then \ cd ${ACCEPTANCE_DIR} && \ - ROBOT_RUN_TESTS=$(ACCEPTANCE_RUN_TESTS) ROBOT_HELM_PATH=$(BINDIR) make acceptance; \ + ROBOT_RUN_TESTS=$(ACCEPTANCE_RUN_TESTS) ROBOT_HELM_PATH='$(BINDIR)' make acceptance; \ else \ echo "You must clone the acceptance_testing repo under $(ACCEPTANCE_DIR)"; \ echo "You can find the acceptance_testing repo at https://github.com/helm/acceptance-testing"; \ @@ -178,7 +178,7 @@ checksum: .PHONY: clean clean: - @rm -rf $(BINDIR) ./_dist + @rm -rf '$(BINDIR)' ./_dist .PHONY: release-notes release-notes: From 52295490fd7a22c05058ad6eab794ccd4fdf3193 Mon Sep 17 00:00:00 2001 From: yxxhero Date: Tue, 21 Jul 2020 13:51:27 +0800 Subject: [PATCH 186/281] fix insecure-skip-tls-verify flag does't work on helm install, Keep FindChartInRepoURL and FindChartInAuthRepoURL functions signatures intact. Signed-off-by: yxxhero --- pkg/action/install.go | 4 ++-- pkg/action/pull.go | 2 +- pkg/repo/chartrepo.go | 22 +++++++++++++++------- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/pkg/action/install.go b/pkg/action/install.go index 48a3aeeca..383dd2c86 100644 --- a/pkg/action/install.go +++ b/pkg/action/install.go @@ -654,8 +654,8 @@ func (c *ChartPathOptions) LocateChart(name string, settings *cli.EnvSettings) ( dl.Verify = downloader.VerifyAlways } if c.RepoURL != "" { - chartURL, err := repo.FindChartInAuthRepoURL(c.RepoURL, c.Username, c.Password, name, version, - c.CertFile, c.KeyFile, c.CaFile, getter.All(settings)) + chartURL, err := repo.FindChartInAuthAndTLSRepoURL(c.RepoURL, c.Username, c.Password, name, version, + c.CertFile, c.KeyFile, c.CaFile, c.InsecureSkipTLSverify, getter.All(settings)) if err != nil { return "", err } diff --git a/pkg/action/pull.go b/pkg/action/pull.go index a46e98bae..220ca11b2 100644 --- a/pkg/action/pull.go +++ b/pkg/action/pull.go @@ -89,7 +89,7 @@ func (p *Pull) Run(chartRef string) (string, error) { } if p.RepoURL != "" { - chartURL, err := repo.FindChartInAuthRepoURL(p.RepoURL, p.Username, p.Password, chartRef, p.Version, p.CertFile, p.KeyFile, p.CaFile, getter.All(p.Settings)) + chartURL, err := repo.FindChartInAuthAndTLSRepoURL(p.RepoURL, p.Username, p.Password, chartRef, p.Version, p.CertFile, p.KeyFile, p.CaFile, p.InsecureSkipTLSverify, getter.All(p.Settings)) if err != nil { return out.String(), err } diff --git a/pkg/repo/chartrepo.go b/pkg/repo/chartrepo.go index c2c366a1e..266986a95 100644 --- a/pkg/repo/chartrepo.go +++ b/pkg/repo/chartrepo.go @@ -205,6 +205,13 @@ func FindChartInRepoURL(repoURL, chartName, chartVersion, certFile, keyFile, caF // without adding repo to repositories, like FindChartInRepoURL, // but it also receives credentials for the chart repository. func FindChartInAuthRepoURL(repoURL, username, password, chartName, chartVersion, certFile, keyFile, caFile string, getters getter.Providers) (string, error) { + return FindChartInAuthAndTLSRepoURL(repoURL, username, password, chartName, chartVersion, certFile, keyFile, caFile, false, getters) +} + +// FindChartInAuthRepoURL finds chart in chart repository pointed by repoURL +// without adding repo to repositories, like FindChartInRepoURL, +// but it also receives credentials and TLS verify flag for the chart repository. +func FindChartInAuthAndTLSRepoURL(repoURL, username, password, chartName, chartVersion, certFile, keyFile, caFile string, insecureSkipTLSverify bool, getters getter.Providers) (string, error) { // Download and write the index file to a temporary location buf := make([]byte, 20) @@ -212,13 +219,14 @@ func FindChartInAuthRepoURL(repoURL, username, password, chartName, chartVersion name := strings.ReplaceAll(base64.StdEncoding.EncodeToString(buf), "/", "-") c := Entry{ - URL: repoURL, - Username: username, - Password: password, - CertFile: certFile, - KeyFile: keyFile, - CAFile: caFile, - Name: name, + URL: repoURL, + Username: username, + Password: password, + CertFile: certFile, + KeyFile: keyFile, + CAFile: caFile, + Name: name, + InsecureSkipTLSverify: insecureSkipTLSverify, } r, err := NewChartRepository(&c, getters) if err != nil { From ffc3d42f87f8334684f12372bbdda8147702a12d Mon Sep 17 00:00:00 2001 From: Holder Date: Wed, 22 Jul 2020 23:03:25 +0800 Subject: [PATCH 187/281] fix(sdk): Polish the downloader/manager package error return (#8491) * fix(sdk): Polish the downloader/manager package error return Close #8471 Signed-off-by: Dong Gang * Modify the repositories validation function `resloveRepoNames` and add a unit test. Signed-off-by: Dong Gang * Remove wrong commit Signed-off-by: Dong Gang --- cmd/helm/dependency_build.go | 7 ++++++- pkg/downloader/manager.go | 13 ++++++++++++- pkg/downloader/manager_test.go | 29 +++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/cmd/helm/dependency_build.go b/cmd/helm/dependency_build.go index 478b49479..4e87684ce 100644 --- a/cmd/helm/dependency_build.go +++ b/cmd/helm/dependency_build.go @@ -16,6 +16,7 @@ limitations under the License. package main import ( + "fmt" "io" "os" "path/filepath" @@ -65,7 +66,11 @@ func newDependencyBuildCmd(out io.Writer) *cobra.Command { if client.Verify { man.Verify = downloader.VerifyIfPossible } - return man.Build() + err := man.Build() + if e, ok := err.(downloader.ErrRepoNotFound); ok { + return fmt.Errorf("%s. Please add the missing repos via 'helm repo add'", e.Error()) + } + return err }, } diff --git a/pkg/downloader/manager.go b/pkg/downloader/manager.go index 00198de0c..2d6918739 100644 --- a/pkg/downloader/manager.go +++ b/pkg/downloader/manager.go @@ -42,6 +42,17 @@ import ( "helm.sh/helm/v3/pkg/repo" ) +// ErrRepoNotFound indicates that chart repositories can't be found in local repo cache. +// The value of Repos is missing repos. +type ErrRepoNotFound struct { + Repos []string +} + +// Error implements the error interface. +func (e ErrRepoNotFound) Error() string { + return fmt.Sprintf("no repository definition for %s", strings.Join(e.Repos, ", ")) +} + // Manager handles the lifecycle of fetching, resolving, and storing dependencies. type Manager struct { // Out is used to print warnings and notifications. @@ -411,7 +422,7 @@ Loop: missing = append(missing, dd.Repository) } if len(missing) > 0 { - return errors.Errorf("no repository definition for %s. Please add the missing repos via 'helm repo add'", strings.Join(missing, ", ")) + return ErrRepoNotFound{missing} } return nil } diff --git a/pkg/downloader/manager_test.go b/pkg/downloader/manager_test.go index ea235c13f..dd3cbbd7b 100644 --- a/pkg/downloader/manager_test.go +++ b/pkg/downloader/manager_test.go @@ -360,3 +360,32 @@ func TestBuild_WithRepositoryAlias(t *testing.T) { Repository: "@test", }) } + +func TestErrRepoNotFound_Error(t *testing.T) { + type fields struct { + Repos []string + } + tests := []struct { + name string + fields fields + want string + }{ + { + name: "OK", + fields: fields{ + Repos: []string{"https://charts1.example.com", "https://charts2.example.com"}, + }, + want: "no repository definition for https://charts1.example.com, https://charts2.example.com", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + e := ErrRepoNotFound{ + Repos: tt.fields.Repos, + } + if got := e.Error(); got != tt.want { + t.Errorf("Error() = %v, want %v", got, tt.want) + } + }) + } +} From f9d35242d0cc3774e4c4d937e6c00c49aa8c6c7f Mon Sep 17 00:00:00 2001 From: Andrew Melis Date: Wed, 22 Jul 2020 12:21:30 -0400 Subject: [PATCH 188/281] Enhance readability by extracting bitwise operation Fixes https://github.com/helm/helm/pull/8282#discussion_r458368780 Signed-off-by: Andrew Melis --- pkg/action/list.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/action/list.go b/pkg/action/list.go index 3d5e6d7a6..0f85de519 100644 --- a/pkg/action/list.go +++ b/pkg/action/list.go @@ -250,7 +250,8 @@ func (l *List) filterStateMask(releases []*release.Release) []*release.Release { for _, rls := range releases { currentStatus := l.StateMask.FromName(rls.Info.Status.String()) - if l.StateMask¤tStatus == 0 { + mask := l.StateMask & currentStatus + if mask == 0 { continue } desiredStateReleases = append(desiredStateReleases, rls) From ff147e9ed7463f6349ee416da723949ae2d33330 Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Thu, 16 Jul 2020 11:03:08 -0400 Subject: [PATCH 189/281] Locking file URIs to a version in lockfile Previously, if a range was specified for a file:// url as a dependency the range would be put in the lockfile. Lockfiles are designed to pin to a specific version and not support ranges. This is for reproducibility. The change here pins to a the specific version of the chart specified using the file:// when update is run. Signed-off-by: Matt Farina --- internal/resolver/resolver.go | 13 +++++++++++-- internal/resolver/resolver_test.go | 11 +++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/internal/resolver/resolver.go b/internal/resolver/resolver.go index cec29c947..c72a39e82 100644 --- a/internal/resolver/resolver.go +++ b/internal/resolver/resolver.go @@ -27,6 +27,7 @@ import ( "github.com/pkg/errors" "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/chart/loader" "helm.sh/helm/v3/pkg/helmpath" "helm.sh/helm/v3/pkg/provenance" "helm.sh/helm/v3/pkg/repo" @@ -68,14 +69,22 @@ func (r *Resolver) Resolve(reqs []*chart.Dependency, repoNames map[string]string } if strings.HasPrefix(d.Repository, "file://") { - if _, err := GetLocalPath(d.Repository, r.chartpath); err != nil { + chartpath, err := GetLocalPath(d.Repository, r.chartpath) + if err != nil { + return nil, err + } + + // The version of the chart locked will be the version of the chart + // currently listed in the file system within the chart. + ch, err := loader.LoadDir(chartpath) + if err != nil { return nil, err } locked[i] = &chart.Dependency{ Name: d.Name, Repository: d.Repository, - Version: d.Version, + Version: ch.Metadata.Version, } continue } diff --git a/internal/resolver/resolver_test.go b/internal/resolver/resolver_test.go index 0b0c3a6f8..f59188508 100644 --- a/internal/resolver/resolver_test.go +++ b/internal/resolver/resolver_test.go @@ -82,6 +82,17 @@ func TestResolve(t *testing.T) { }, }, }, + { + name: "repo from valid local path with range resolution", + req: []*chart.Dependency{ + {Name: "base", Repository: "file://base", Version: "^0.1.0"}, + }, + expect: &chart.Lock{ + Dependencies: []*chart.Dependency{ + {Name: "base", Repository: "file://base", Version: "0.1.0"}, + }, + }, + }, { name: "repo from invalid local path", req: []*chart.Dependency{ From 9cb90a8b234f98c16aef0918cba92d83061d7c97 Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Fri, 17 Jul 2020 11:54:44 -0400 Subject: [PATCH 190/281] Make unmanaged repositories version resolvable If a repository was not know to helm (e.g. added using helm repo add) then Helm would use the range set in the depenencies as the version in the lock file. Lock files should not have ranges since they are locked to versions. Helm did this because the version information for repositories was not know to Helm. This change fixes that by making the repository and chart information known to Helm so it can resolve the versions. Closes #8449 Signed-off-by: Matt Farina --- pkg/downloader/manager.go | 132 ++++++++++++++++++++++++--------- pkg/downloader/manager_test.go | 30 ++++++++ 2 files changed, 126 insertions(+), 36 deletions(-) diff --git a/pkg/downloader/manager.go b/pkg/downloader/manager.go index 2d6918739..9db5ed928 100644 --- a/pkg/downloader/manager.go +++ b/pkg/downloader/manager.go @@ -16,6 +16,8 @@ limitations under the License. package downloader import ( + "crypto" + "encoding/hex" "fmt" "io" "io/ioutil" @@ -106,7 +108,13 @@ func (m *Manager) Build() error { } } - if _, err := m.resolveRepoNames(req); err != nil { + repoNames, err := m.resolveRepoNames(req) + if err != nil { + return err + } + + _, err = m.ensureMissingRepos(repoNames, req) + if err != nil { return err } @@ -124,11 +132,6 @@ func (m *Manager) Build() error { } } - // Check that all of the repos we're dependent on actually exist. - if err := m.hasAllRepos(lock.Dependencies); err != nil { - return err - } - if !m.SkipUpdate { // For each repo in the file, update the cached copy of that repo if err := m.UpdateRepositories(); err != nil { @@ -158,14 +161,23 @@ func (m *Manager) Update() error { return nil } - // Check that all of the repos we're dependent on actually exist and - // the repo index names. + // Get the names of the repositories the dependencies need that Helm is + // configured to know about. repoNames, err := m.resolveRepoNames(req) if err != nil { return err } - // For each repo in the file, update the cached copy of that repo + // For the repositories Helm is not configured to know about, ensure Helm + // has some information about them and, when possible, the index files + // locally. + repoNames, err = m.ensureMissingRepos(repoNames, req) + if err != nil { + return err + } + + // For each of the repositories Helm is configured to know about, update + // the index information locally. if !m.SkipUpdate { if err := m.UpdateRepositories(); err != nil { return err @@ -393,38 +405,60 @@ func (m *Manager) safeDeleteDep(name, dir string) error { return nil } -// hasAllRepos ensures that all of the referenced deps are in the local repo cache. -func (m *Manager) hasAllRepos(deps []*chart.Dependency) error { - rf, err := loadRepoConfig(m.RepositoryConfig) - if err != nil { - return err - } - repos := rf.Repositories +// ensureMissingRepos attempts to ensure the repository information for repos +// not managed by Helm is present. This takes in the repoNames Helm is configured +// to work with along with the chart dependencies. It will find the deps not +// in a known repo and attempt to ensure the data is present for steps like +// version resolution. +func (m *Manager) ensureMissingRepos(repoNames map[string]string, deps []*chart.Dependency) (map[string]string, error) { + + var ru []*repo.Entry - // Verify that all repositories referenced in the deps are actually known - // by Helm. - missing := []string{} -Loop: for _, dd := range deps { - // If repo is from local path, continue - if strings.HasPrefix(dd.Repository, "file://") { + + // When the repoName for a dependency is known we can skip ensuring + if _, ok := repoNames[dd.Name]; ok { continue } - if dd.Repository == "" { - continue + // The generated repository name, which will result in an index being + // locally cached, has a name pattern of "helm-manager-" followed by a + // sha256 of the repo name. This assumes end users will never create + // repositories with these names pointing to other repositories. Using + // this method of naming allows the existing repository pulling and + // resolution code to do most of the work. + rn, err := key(dd.Repository) + if err != nil { + return repoNames, err } - for _, repo := range repos { - if urlutil.Equal(repo.URL, strings.TrimSuffix(dd.Repository, "/")) { - continue Loop - } + rn = "helm-manager-" + rn + + repoNames[dd.Name] = rn + + // Assuming the repository is generally available. For Helm managed + // access controls the repository needs to be added through the user + // managed system. This path will work for public charts, like those + // supplied by Bitnami, but not for protected charts, like corp ones + // behind a username and pass. + ri := &repo.Entry{ + Name: rn, + URL: dd.Repository, } - missing = append(missing, dd.Repository) + ru = append(ru, ri) } - if len(missing) > 0 { - return ErrRepoNotFound{missing} + + // Calls to UpdateRepositories (a public function) will only update + // repositories configured by the user. Here we update repos found in + // the dependencies that are not known to the user if update skipping + // is not configured. + if !m.SkipUpdate && len(ru) > 0 { + fmt.Fprintln(m.Out, "Getting updates for unmanaged Helm repositories...") + if err := m.parallelRepoUpdate(ru); err != nil { + return repoNames, err + } } - return nil + + return repoNames, nil } // resolveRepoNames returns the repo names of the referenced deps which can be used to fetch the cached index file @@ -517,16 +551,18 @@ func (m *Manager) UpdateRepositories() error { } repos := rf.Repositories if len(repos) > 0 { + fmt.Fprintln(m.Out, "Hang tight while we grab the latest from your chart repositories...") // This prints warnings straight to out. if err := m.parallelRepoUpdate(repos); err != nil { return err } + fmt.Fprintln(m.Out, "Update Complete. ⎈Happy Helming!⎈") } return nil } func (m *Manager) parallelRepoUpdate(repos []*repo.Entry) error { - fmt.Fprintln(m.Out, "Hang tight while we grab the latest from your chart repositories...") + var wg sync.WaitGroup for _, c := range repos { r, err := repo.NewChartRepository(c, m.Getters) @@ -536,15 +572,27 @@ func (m *Manager) parallelRepoUpdate(repos []*repo.Entry) error { wg.Add(1) go func(r *repo.ChartRepository) { if _, err := r.DownloadIndexFile(); err != nil { - fmt.Fprintf(m.Out, "...Unable to get an update from the %q chart repository (%s):\n\t%s\n", r.Config.Name, r.Config.URL, err) + // For those dependencies that are not known to helm and using a + // generated key name we display the repo url. + if strings.HasPrefix(r.Config.Name, "helm-manager-") { + fmt.Fprintf(m.Out, "...Unable to get an update from the %q chart repository:\n\t%s\n", r.Config.URL, err) + } else { + fmt.Fprintf(m.Out, "...Unable to get an update from the %q chart repository (%s):\n\t%s\n", r.Config.Name, r.Config.URL, err) + } } else { - fmt.Fprintf(m.Out, "...Successfully got an update from the %q chart repository\n", r.Config.Name) + // For those dependencies that are not known to helm and using a + // generated key name we display the repo url. + if strings.HasPrefix(r.Config.Name, "helm-manager-") { + fmt.Fprintf(m.Out, "...Successfully got an update from the %q chart repository\n", r.Config.URL) + } else { + fmt.Fprintf(m.Out, "...Successfully got an update from the %q chart repository\n", r.Config.Name) + } } wg.Done() }(r) } wg.Wait() - fmt.Fprintln(m.Out, "Update Complete. ⎈Happy Helming!⎈") + return nil } @@ -739,3 +787,15 @@ func move(tmpPath, destPath string) error { } return nil } + +// key is used to turn a name, such as a repository url, into a filesystem +// safe name that is unique for querying. To accomplish this a unique hash of +// the string is used. +func key(name string) (string, error) { + in := strings.NewReader(name) + hash := crypto.SHA256.New() + if _, err := io.Copy(hash, in); err != nil { + return "", nil + } + return hex.EncodeToString(hash.Sum(nil)), nil +} diff --git a/pkg/downloader/manager_test.go b/pkg/downloader/manager_test.go index dd3cbbd7b..e60cf7624 100644 --- a/pkg/downloader/manager_test.go +++ b/pkg/downloader/manager_test.go @@ -389,3 +389,33 @@ func TestErrRepoNotFound_Error(t *testing.T) { }) } } + +func TestKey(t *testing.T) { + tests := []struct { + name string + expect string + }{ + { + name: "file:////tmp", + expect: "afeed3459e92a874f6373aca264ce1459bfa91f9c1d6612f10ae3dc2ee955df3", + }, + { + name: "https://example.com/charts", + expect: "7065c57c94b2411ad774638d76823c7ccb56415441f5ab2f5ece2f3845728e5d", + }, + { + name: "foo/bar/baz", + expect: "15c46a4f8a189ae22f36f201048881d6c090c93583bedcf71f5443fdef224c82", + }, + } + + for _, tt := range tests { + o, err := key(tt.name) + if err != nil { + t.Fatalf("unable to generate key for %q with error: %s", tt.name, err) + } + if o != tt.expect { + t.Errorf("wrong key name generated for %q, expected %q but got %q", tt.name, tt.expect, o) + } + } +} From 1458113696297a7d0426f275c9d0689d4d0ff47e Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Fri, 17 Jul 2020 15:44:50 -0400 Subject: [PATCH 191/281] Adding helm v4 todo Signed-off-by: Matt Farina --- pkg/downloader/manager.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/downloader/manager.go b/pkg/downloader/manager.go index 9db5ed928..6a4058c2c 100644 --- a/pkg/downloader/manager.go +++ b/pkg/downloader/manager.go @@ -171,6 +171,10 @@ func (m *Manager) Update() error { // For the repositories Helm is not configured to know about, ensure Helm // has some information about them and, when possible, the index files // locally. + // TODO(mattfarina): Repositories should be explicitly added by end users + // rather than automattic. In Helm v4 require users to add repositories. They + // should have to add them in order to make sure they are aware of the + // respoitories and opt-in to any locations. For security. repoNames, err = m.ensureMissingRepos(repoNames, req) if err != nil { return err From 25f67f74eeee861e132d1a0220144c87631dbe38 Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Wed, 22 Jul 2020 15:51:27 -0400 Subject: [PATCH 192/281] Restoring Build behavior Two things changed in this commit... 1. The Build behavior was restored and the change only impacts Update. This is a more minimal functionality change thats a more secure behavior 2. Cleanup from Josh's feedback on the PR to create a const and comment changes Signed-off-by: Matt Farina --- pkg/downloader/manager.go | 58 +++++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/pkg/downloader/manager.go b/pkg/downloader/manager.go index 6a4058c2c..bcd5dcec4 100644 --- a/pkg/downloader/manager.go +++ b/pkg/downloader/manager.go @@ -108,13 +108,7 @@ func (m *Manager) Build() error { } } - repoNames, err := m.resolveRepoNames(req) - if err != nil { - return err - } - - _, err = m.ensureMissingRepos(repoNames, req) - if err != nil { + if _, err := m.resolveRepoNames(req); err != nil { return err } @@ -132,6 +126,11 @@ func (m *Manager) Build() error { } } + // Check that all of the repos we're dependent on actually exist. + if err := m.hasAllRepos(lock.Dependencies); err != nil { + return err + } + if !m.SkipUpdate { // For each repo in the file, update the cached copy of that repo if err := m.UpdateRepositories(); err != nil { @@ -174,7 +173,7 @@ func (m *Manager) Update() error { // TODO(mattfarina): Repositories should be explicitly added by end users // rather than automattic. In Helm v4 require users to add repositories. They // should have to add them in order to make sure they are aware of the - // respoitories and opt-in to any locations. For security. + // respoitories and opt-in to any locations, for security. repoNames, err = m.ensureMissingRepos(repoNames, req) if err != nil { return err @@ -409,6 +408,40 @@ func (m *Manager) safeDeleteDep(name, dir string) error { return nil } +// hasAllRepos ensures that all of the referenced deps are in the local repo cache. +func (m *Manager) hasAllRepos(deps []*chart.Dependency) error { + rf, err := loadRepoConfig(m.RepositoryConfig) + if err != nil { + return err + } + repos := rf.Repositories + + // Verify that all repositories referenced in the deps are actually known + // by Helm. + missing := []string{} +Loop: + for _, dd := range deps { + // If repo is from local path, continue + if strings.HasPrefix(dd.Repository, "file://") { + continue + } + + if dd.Repository == "" { + continue + } + for _, repo := range repos { + if urlutil.Equal(repo.URL, strings.TrimSuffix(dd.Repository, "/")) { + continue Loop + } + } + missing = append(missing, dd.Repository) + } + if len(missing) > 0 { + return ErrRepoNotFound{missing} + } + return nil +} + // ensureMissingRepos attempts to ensure the repository information for repos // not managed by Helm is present. This takes in the repoNames Helm is configured // to work with along with the chart dependencies. It will find the deps not @@ -435,7 +468,7 @@ func (m *Manager) ensureMissingRepos(repoNames map[string]string, deps []*chart. if err != nil { return repoNames, err } - rn = "helm-manager-" + rn + rn = managerKeyPrefix + rn repoNames[dd.Name] = rn @@ -578,7 +611,7 @@ func (m *Manager) parallelRepoUpdate(repos []*repo.Entry) error { if _, err := r.DownloadIndexFile(); err != nil { // For those dependencies that are not known to helm and using a // generated key name we display the repo url. - if strings.HasPrefix(r.Config.Name, "helm-manager-") { + if strings.HasPrefix(r.Config.Name, managerKeyPrefix) { fmt.Fprintf(m.Out, "...Unable to get an update from the %q chart repository:\n\t%s\n", r.Config.URL, err) } else { fmt.Fprintf(m.Out, "...Unable to get an update from the %q chart repository (%s):\n\t%s\n", r.Config.Name, r.Config.URL, err) @@ -586,7 +619,7 @@ func (m *Manager) parallelRepoUpdate(repos []*repo.Entry) error { } else { // For those dependencies that are not known to helm and using a // generated key name we display the repo url. - if strings.HasPrefix(r.Config.Name, "helm-manager-") { + if strings.HasPrefix(r.Config.Name, managerKeyPrefix) { fmt.Fprintf(m.Out, "...Successfully got an update from the %q chart repository\n", r.Config.URL) } else { fmt.Fprintf(m.Out, "...Successfully got an update from the %q chart repository\n", r.Config.Name) @@ -792,6 +825,9 @@ func move(tmpPath, destPath string) error { return nil } +// The prefix to use for cache keys created by the manager for repo names +const managerKeyPrefix = "helm-manager-" + // key is used to turn a name, such as a repository url, into a filesystem // safe name that is unique for querying. To accomplish this a unique hash of // the string is used. From 0ecc500c451f423bbcfb393a61953170c479fc51 Mon Sep 17 00:00:00 2001 From: yxxhero Date: Thu, 23 Jul 2020 08:06:39 +0800 Subject: [PATCH 193/281] add unit tests for FindChartInAuthAndTLSRepoURL. Signed-off-by: yxxhero --- pkg/repo/chartrepo_test.go | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/pkg/repo/chartrepo_test.go b/pkg/repo/chartrepo_test.go index f50d6a2b6..5317bcbc0 100644 --- a/pkg/repo/chartrepo_test.go +++ b/pkg/repo/chartrepo_test.go @@ -276,6 +276,44 @@ func startLocalServerForTests(handler http.Handler) (*httptest.Server, error) { return httptest.NewServer(handler), nil } +// startLocalTLSServerForTests Start the local helm server with TLS +func startLocalTLSServerForTests(handler http.Handler) (*httptest.Server, error) { + if handler == nil { + fileBytes, err := ioutil.ReadFile("testdata/local-index.yaml") + if err != nil { + return nil, err + } + handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write(fileBytes) + }) + } + + return httptest.NewTLSServer(handler), nil +} + +func TestFindChartInAuthAndTLSRepoURL(t *testing.T) { + srv, err := startLocalTLSServerForTests(nil) + if err != nil { + t.Fatal(err) + } + defer srv.Close() + + chartURL, err := FindChartInAuthAndTLSRepoURL(srv.URL, "", "", "nginx", "", "", "", "", true, getter.All(&cli.EnvSettings{})) + if err != nil { + t.Fatalf("%v", err) + } + if chartURL != "https://kubernetes-charts.storage.googleapis.com/nginx-0.2.0.tgz" { + t.Errorf("%s is not the valid URL", chartURL) + } + + // If the insecureSkipTLsverify is false, it will return an error that contains "x509: certificate signed by unknown authority". + _, err = FindChartInAuthAndTLSRepoURL(srv.URL, "", "", "nginx", "0.1.0", "", "", "", false, getter.All(&cli.EnvSettings{})) + + if !strings.Contains(err.Error(), "x509: certificate signed by unknown authority") { + t.Errorf("Expected TLS error for function FindChartInAuthAndTLSRepoURL not found, but got a different error (%v)", err) + } +} + func TestFindChartInRepoURL(t *testing.T) { srv, err := startLocalServerForTests(nil) if err != nil { From d141593d834f99822c08b9f3ca768258483afbf1 Mon Sep 17 00:00:00 2001 From: She Jiayu Date: Thu, 23 Jul 2020 12:28:43 +0800 Subject: [PATCH 194/281] Avoid hardcoded container port in default notes Signed-off-by: She Jiayu --- pkg/chartutil/create.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/chartutil/create.go b/pkg/chartutil/create.go index c036ce37d..6e382b961 100644 --- a/pkg/chartutil/create.go +++ b/pkg/chartutil/create.go @@ -381,8 +381,9 @@ const defaultNotes = `1. Get the application URL by running these commands: echo http://$SERVICE_IP:{{ .Values.service.port }} {{- else if contains "ClusterIP" .Values.service.type }} export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include ".name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") echo "Visit http://127.0.0.1:8080 to use your application" - kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:80 + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT {{- end }} ` From 4faeedd98b03e5af7733317a84e77ebff28c55f7 Mon Sep 17 00:00:00 2001 From: Rajat Jindal Date: Thu, 23 Jul 2020 11:51:17 +0530 Subject: [PATCH 195/281] fix watch error due to elb/proxy timeout Signed-off-by: Rajat Jindal --- pkg/kube/client.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/kube/client.go b/pkg/kube/client.go index 62e1b104b..decfc2e6e 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -35,6 +35,7 @@ import ( "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" @@ -478,7 +479,7 @@ func (c *Client) watchUntilReady(timeout time.Duration, info *resource.Info) err ctx, cancel := watchtools.ContextWithOptionalTimeout(context.Background(), timeout) defer cancel() - _, err = watchtools.ListWatchUntil(ctx, lw, func(e watch.Event) (bool, error) { + _, err = watchtools.UntilWithSync(ctx, lw, &unstructured.Unstructured{}, nil, func(e watch.Event) (bool, error) { // Make sure the incoming object is versioned as we use unstructured // objects when we build manifests obj := convertWithMapper(e.Object, info.Mapping) From b6bbf34097312fefec8120fe066780340f2d983f Mon Sep 17 00:00:00 2001 From: Haoming Zhang Date: Thu, 23 Jul 2020 21:04:18 -0700 Subject: [PATCH 196/281] Close gzip reader when done. Signed-off-by: Haoming Zhang --- pkg/storage/driver/util.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/storage/driver/util.go b/pkg/storage/driver/util.go index a87002ab4..e5b846163 100644 --- a/pkg/storage/driver/util.go +++ b/pkg/storage/driver/util.go @@ -68,6 +68,7 @@ func decodeRelease(data string) (*rspb.Release, error) { if err != nil { return nil, err } + defer r.Close() b2, err := ioutil.ReadAll(r) if err != nil { return nil, err From b13247c333339bcd8bf963e62df2e03f37383984 Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Mon, 27 Jul 2020 08:57:57 -0700 Subject: [PATCH 197/281] introduce stale issue bot Signed-off-by: Matthew Fisher --- .github/workflows/stale-issue-bot.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .github/workflows/stale-issue-bot.yaml diff --git a/.github/workflows/stale-issue-bot.yaml b/.github/workflows/stale-issue-bot.yaml new file mode 100644 index 000000000..e21e6d5ca --- /dev/null +++ b/.github/workflows/stale-issue-bot.yaml @@ -0,0 +1,15 @@ +name: "Close stale issues" +on: + schedule: + - cron: "0 0 * * *" +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v3 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + stale-issue-message: 'This issue has been marked as stale because it has been open for 90 days with no activity. This thread will be automatically closed in 30 days if no further activity occurs.' + exempt-issue-labels: 'keep open' + days-before-stale: 90 + days-before-close: 30 From 44212f83dc9c893d962fe48648320e7b7b66950c Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Tue, 28 Jul 2020 09:52:39 -0400 Subject: [PATCH 198/281] Fix issue with install and upgrade running all hooks When #8156 was merged it had the side effect that all hooks were run all the time. All the hooks were put in the flow of the content rendered and sent to Kubernetes on every command. For example, if you ran the following 2 commands the test hooks would run: helm create foo helm install foo ./foo This should not run any hooks. But, the generated test hook is run. The change in this commit moves the writing of the hooks to output or disk back into the template command rather than in a private function within the actions. This is where it was for v3.2. One side effect is that post renderers will not work on hooks. This was the case in v3.2. Since this bug is blocking the release of v3.3.0 it is being rolled back. A refactor effort is underway for this section of code. post renderer for hooks should be added back as part of that work. Since post renderer hooks did not make it into a release it is ok to roll it back for now. There is code in the cmd/helm package that has been duplicated from pkg/action. This is a temporary measure to fix the immediate bug with plans to correct the situation as part of a refactor of renderResources. Signed-off-by: Matt Farina --- cmd/helm/template.go | 68 ++++++++++++++++++++++++++++++++++++++ pkg/action/action.go | 22 ++---------- pkg/action/install.go | 2 +- pkg/action/install_test.go | 6 ---- pkg/action/upgrade.go | 2 +- 5 files changed, 73 insertions(+), 27 deletions(-) diff --git a/cmd/helm/template.go b/cmd/helm/template.go index 5a8a25102..6123d29d4 100644 --- a/cmd/helm/template.go +++ b/cmd/helm/template.go @@ -20,6 +20,8 @@ import ( "bytes" "fmt" "io" + "os" + "path" "path/filepath" "regexp" "sort" @@ -79,6 +81,25 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { if rel != nil { var manifests bytes.Buffer fmt.Fprintln(&manifests, strings.TrimSpace(rel.Manifest)) + if !client.DisableHooks { + fileWritten := make(map[string]bool) + for _, m := range rel.Hooks { + if client.OutputDir == "" { + fmt.Fprintf(&manifests, "---\n# Source: %s\n%s\n", m.Path, m.Manifest) + } else { + newDir := client.OutputDir + if client.UseReleaseName { + newDir = filepath.Join(client.OutputDir, client.ReleaseName) + } + err = writeToFile(newDir, m.Path, m.Manifest, fileWritten[m.Path]) + if err != nil { + return err + } + fileWritten[m.Path] = true + } + + } + } // if we have a list of files to render, then check that each of the // provided files exists in the chart. @@ -149,3 +170,50 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { return cmd } + +// The following functions (writeToFile, createOrOpenFile, and ensureDirectoryForFile) +// are coppied from the actions package. This is part of a change to correct a +// bug introduced by #8156. As part of the todo to refactor renderResources +// this duplicate code should be removed. It is added here so that the API +// surface area is as minimally impacted as possible in fixing the issue. +func writeToFile(outputDir string, name string, data string, append bool) error { + outfileName := strings.Join([]string{outputDir, name}, string(filepath.Separator)) + + err := ensureDirectoryForFile(outfileName) + if err != nil { + return err + } + + f, err := createOrOpenFile(outfileName, append) + if err != nil { + return err + } + + defer f.Close() + + _, err = f.WriteString(fmt.Sprintf("---\n# Source: %s\n%s\n", name, data)) + + if err != nil { + return err + } + + fmt.Printf("wrote %s\n", outfileName) + return nil +} + +func createOrOpenFile(filename string, append bool) (*os.File, error) { + if append { + return os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0600) + } + return os.Create(filename) +} + +func ensureDirectoryForFile(file string) error { + baseDir := path.Dir(file) + _, err := os.Stat(baseDir) + if err != nil && !os.IsNotExist(err) { + return err + } + + return os.MkdirAll(baseDir, 0755) +} diff --git a/pkg/action/action.go b/pkg/action/action.go index fec86cd42..3c47cc6df 100644 --- a/pkg/action/action.go +++ b/pkg/action/action.go @@ -99,7 +99,9 @@ type Configuration struct { // renderResources renders the templates in a chart // // TODO: This function is badly in need of a refactor. -func (c *Configuration) renderResources(ch *chart.Chart, values chartutil.Values, releaseName, outputDir string, subNotes, useReleaseName, includeCrds bool, disableHooks bool, pr postrender.PostRenderer, dryRun bool) ([]*release.Hook, *bytes.Buffer, string, error) { +// TODO: As part of the refactor the duplicate code in cmd/helm/template.go should be removed +// This code has to do with writing files to dick. +func (c *Configuration) renderResources(ch *chart.Chart, values chartutil.Values, releaseName, outputDir string, subNotes, useReleaseName, includeCrds bool, pr postrender.PostRenderer, dryRun bool) ([]*release.Hook, *bytes.Buffer, string, error) { hs := []*release.Hook{} b := bytes.NewBuffer(nil) @@ -212,24 +214,6 @@ func (c *Configuration) renderResources(ch *chart.Chart, values chartutil.Values } } - if !disableHooks && len(hs) > 0 { - for _, h := range hs { - if outputDir == "" { - fmt.Fprintf(b, "---\n# Source: %s\n%s\n", h.Path, h.Manifest) - } else { - newDir := outputDir - if useReleaseName { - newDir = filepath.Join(outputDir, releaseName) - } - err = writeToFile(newDir, h.Path, h.Manifest, fileWritten[h.Path]) - if err != nil { - return hs, b, "", err - } - fileWritten[h.Path] = true - } - } - } - if pr != nil { b, err = pr.Run(b) if err != nil { diff --git a/pkg/action/install.go b/pkg/action/install.go index 48a3aeeca..00fb208b0 100644 --- a/pkg/action/install.go +++ b/pkg/action/install.go @@ -236,7 +236,7 @@ func (i *Install) Run(chrt *chart.Chart, vals map[string]interface{}) (*release. rel := i.createRelease(chrt, vals) var manifestDoc *bytes.Buffer - rel.Hooks, manifestDoc, rel.Info.Notes, err = i.cfg.renderResources(chrt, valuesToRender, i.ReleaseName, i.OutputDir, i.SubNotes, i.UseReleaseName, i.IncludeCRDs, i.DisableHooks, i.PostRenderer, i.DryRun) + rel.Hooks, manifestDoc, rel.Info.Notes, err = i.cfg.renderResources(chrt, valuesToRender, i.ReleaseName, i.OutputDir, i.SubNotes, i.UseReleaseName, i.IncludeCRDs, i.PostRenderer, i.DryRun) // Even for errors, attach this if available if manifestDoc != nil { rel.Manifest = manifestDoc.String() diff --git a/pkg/action/install_test.go b/pkg/action/install_test.go index 4366889ce..6c4012cfd 100644 --- a/pkg/action/install_test.go +++ b/pkg/action/install_test.go @@ -499,9 +499,6 @@ func TestInstallReleaseOutputDir(t *testing.T) { _, err = os.Stat(filepath.Join(dir, "hello/templates/with-partials")) is.NoError(err) - _, err = os.Stat(filepath.Join(dir, "hello/templates/hooks")) - is.NoError(err) - _, err = os.Stat(filepath.Join(dir, "hello/templates/rbac")) is.NoError(err) @@ -542,9 +539,6 @@ func TestInstallOutputDirWithReleaseName(t *testing.T) { _, err = os.Stat(filepath.Join(newDir, "hello/templates/with-partials")) is.NoError(err) - _, err = os.Stat(filepath.Join(newDir, "hello/templates/hooks")) - is.NoError(err) - _, err = os.Stat(filepath.Join(newDir, "hello/templates/rbac")) is.NoError(err) diff --git a/pkg/action/upgrade.go b/pkg/action/upgrade.go index 949aebcae..e7c2aec25 100644 --- a/pkg/action/upgrade.go +++ b/pkg/action/upgrade.go @@ -222,7 +222,7 @@ func (u *Upgrade) prepareUpgrade(name string, chart *chart.Chart, vals map[strin return nil, nil, err } - hooks, manifestDoc, notesTxt, err := u.cfg.renderResources(chart, valuesToRender, "", "", u.SubNotes, false, false, false, u.PostRenderer, u.DryRun) + hooks, manifestDoc, notesTxt, err := u.cfg.renderResources(chart, valuesToRender, "", "", u.SubNotes, false, false, u.PostRenderer, u.DryRun) if err != nil { return nil, nil, err } From 8bb0413552ca5b2de39d9cb2ec06089c6569908c Mon Sep 17 00:00:00 2001 From: Ma Xinjian Date: Fri, 31 Jul 2020 14:15:21 +0800 Subject: [PATCH 199/281] darwin-386 and windows-386 are not supported now $ curl https://get.helm.sh/helm-v3.2.4-windows-386.tar.gz BlobNotFoundThe specified does not exist. RequestId:6d53f961-d01e-0032-025f-5e4d79000000 $ curl https://get.helm.sh/helm-v2.16.9-darwin-386.tar.gz BlobNotFoundThe specified does not exist. RequestId:81020fad-c01e-0001-0e60-5e12d2000000 Signed-off-by: Ma Xinjian --- scripts/get | 2 +- scripts/get-helm-3 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/get b/scripts/get index 094892346..777a53bbc 100755 --- a/scripts/get +++ b/scripts/get @@ -62,7 +62,7 @@ runAsRoot() { # 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" + local supported="darwin-amd64\nlinux-386\nlinux-amd64\nlinux-arm\nlinux-arm64\nlinux-ppc64le\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" diff --git a/scripts/get-helm-3 b/scripts/get-helm-3 index ce9411edc..f2495e444 100755 --- a/scripts/get-helm-3 +++ b/scripts/get-helm-3 @@ -60,7 +60,7 @@ runAsRoot() { # 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\nlinux-s390x\nwindows-386\nwindows-amd64" + local supported="darwin-amd64\nlinux-386\nlinux-amd64\nlinux-arm\nlinux-arm64\nlinux-ppc64le\nlinux-s390x\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" From 4cc19d1d82c4f4ee4bb9af5739e1184f5800f588 Mon Sep 17 00:00:00 2001 From: Martin Hickey Date: Fri, 31 Jul 2020 07:57:17 +0000 Subject: [PATCH 200/281] Fix typo Signed-off-by: Martin Hickey --- pkg/action/action.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/action/action.go b/pkg/action/action.go index 3c47cc6df..071db709b 100644 --- a/pkg/action/action.go +++ b/pkg/action/action.go @@ -100,7 +100,7 @@ type Configuration struct { // // TODO: This function is badly in need of a refactor. // TODO: As part of the refactor the duplicate code in cmd/helm/template.go should be removed -// This code has to do with writing files to dick. +// This code has to do with writing files to disk. func (c *Configuration) renderResources(ch *chart.Chart, values chartutil.Values, releaseName, outputDir string, subNotes, useReleaseName, includeCrds bool, pr postrender.PostRenderer, dryRun bool) ([]*release.Hook, *bytes.Buffer, string, error) { hs := []*release.Hook{} b := bytes.NewBuffer(nil) From fbc32aea3d43853f94768e4bc7bc4045fe9fb749 Mon Sep 17 00:00:00 2001 From: bellkeyang Date: Fri, 31 Jul 2020 20:39:38 +0800 Subject: [PATCH 201/281] bufix: fix validateNumColons docs Signed-off-by: bellkeyang --- internal/experimental/registry/reference.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/experimental/registry/reference.go b/internal/experimental/registry/reference.go index ebab29a7e..f0e91d4ba 100644 --- a/internal/experimental/registry/reference.go +++ b/internal/experimental/registry/reference.go @@ -105,7 +105,7 @@ func (ref *Reference) validateRepo() error { return err } -// validateNumColon ensures the ref only contains a single colon character (:) +// validateNumColons ensures the ref only contains a single colon character (:) // (or potentially two, there might be a port number specified i.e. :5000) func (ref *Reference) validateNumColons() error { if strings.Contains(ref.Tag, ":") { From 131510aa94faaa66ea4b3c3b7e4156206902ffc5 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Fri, 31 Jul 2020 17:17:54 -0600 Subject: [PATCH 202/281] fix test that modifies the wrong cache data Signed-off-by: Matt Butcher --- cmd/helm/repo_add_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmd/helm/repo_add_test.go b/cmd/helm/repo_add_test.go index d1b9bc0fc..7cb669a1a 100644 --- a/cmd/helm/repo_add_test.go +++ b/cmd/helm/repo_add_test.go @@ -40,11 +40,12 @@ func TestRepoAddCmd(t *testing.T) { } defer srv.Stop() - repoFile := filepath.Join(ensure.TempDir(t), "repositories.yaml") + tmpdir := ensure.TempDir(t) + repoFile := filepath.Join(tmpdir, "repositories.yaml") tests := []cmdTestCase{{ name: "add a repository", - cmd: fmt.Sprintf("repo add test-name %s --repository-config %s", srv.URL(), repoFile), + cmd: fmt.Sprintf("repo add test-name %s --repository-config %s --repository-cache %s", srv.URL(), repoFile, tmpdir), golden: "output/repo-add.txt", }} From edc7d8ea320c4f32fa86a4278c175b96a8da9f2a Mon Sep 17 00:00:00 2001 From: Dmitry Chepurovskiy Date: Wed, 29 Jul 2020 20:30:11 +0300 Subject: [PATCH 203/281] Added selector option to list command Signed-off-by: Dmitry Chepurovskiy --- cmd/helm/list.go | 1 + pkg/action/list.go | 1 + 2 files changed, 2 insertions(+) diff --git a/cmd/helm/list.go b/cmd/helm/list.go index 18e5ffe45..dfa17d9bc 100644 --- a/cmd/helm/list.go +++ b/cmd/helm/list.go @@ -126,6 +126,7 @@ func newListCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { f.IntVarP(&client.Limit, "max", "m", 256, "maximum number of releases to fetch") f.IntVar(&client.Offset, "offset", 0, "next release name in the list, used to offset from start value") f.StringVarP(&client.Filter, "filter", "f", "", "a regular expression (Perl compatible). Any releases that match the expression will be included in the results") + f.StringVarP(&client.Selector, "selector", "l", "", "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)") bindOutputFlag(cmd, &outfmt) return cmd diff --git a/pkg/action/list.go b/pkg/action/list.go index 0f85de519..854a8942b 100644 --- a/pkg/action/list.go +++ b/pkg/action/list.go @@ -126,6 +126,7 @@ type List struct { Deployed bool Failed bool Pending bool + Selector string } // NewList constructs a new *List From 99bd709530bbcbee4fc21822164ad44aa1770b24 Mon Sep 17 00:00:00 2001 From: Dmitry Chepurovskiy Date: Wed, 29 Jul 2020 22:24:07 +0300 Subject: [PATCH 204/281] Pass labels from secret/configmap to release object Signed-off-by: Dmitry Chepurovskiy --- pkg/release/release.go | 2 ++ pkg/storage/driver/cfgmaps.go | 1 + pkg/storage/driver/secrets.go | 1 + 3 files changed, 4 insertions(+) diff --git a/pkg/release/release.go b/pkg/release/release.go index 8582a86f3..1245d8129 100644 --- a/pkg/release/release.go +++ b/pkg/release/release.go @@ -37,6 +37,8 @@ type Release struct { Version int `json:"version,omitempty"` // Namespace is the kubernetes namespace of the release. Namespace string `json:"namespace,omitempty"` + // Labels of the release + Labels map[string]string `json:"labels,omitempty"` } // SetStatus is a helper for setting the status on a release. diff --git a/pkg/storage/driver/cfgmaps.go b/pkg/storage/driver/cfgmaps.go index 71e635975..05213bbdd 100644 --- a/pkg/storage/driver/cfgmaps.go +++ b/pkg/storage/driver/cfgmaps.go @@ -106,6 +106,7 @@ func (cfgmaps *ConfigMaps) List(filter func(*rspb.Release) bool) ([]*rspb.Releas continue } if filter(rls) { + rls.Labels = item.ObjectMeta.Labels results = append(results, rls) } } diff --git a/pkg/storage/driver/secrets.go b/pkg/storage/driver/secrets.go index 44280f70f..1503bc054 100644 --- a/pkg/storage/driver/secrets.go +++ b/pkg/storage/driver/secrets.go @@ -98,6 +98,7 @@ func (secrets *Secrets) List(filter func(*rspb.Release) bool) ([]*rspb.Release, continue } if filter(rls) { + rls.Labels = item.ObjectMeta.Labels results = append(results, rls) } } From 357a0785bcd09e068c240595f12f14ab29fae066 Mon Sep 17 00:00:00 2001 From: Dmitry Chepurovskiy Date: Wed, 29 Jul 2020 23:30:16 +0300 Subject: [PATCH 205/281] Added selector filtering Signed-off-by: Dmitry Chepurovskiy --- pkg/action/list.go | 13 +++++++++++++ pkg/storage/driver/cfgmaps.go | 4 +++- pkg/storage/driver/secrets.go | 4 +++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/pkg/action/list.go b/pkg/action/list.go index 854a8942b..9cf7ec138 100644 --- a/pkg/action/list.go +++ b/pkg/action/list.go @@ -20,6 +20,8 @@ import ( "path" "regexp" + "k8s.io/apimachinery/pkg/labels" + "helm.sh/helm/v3/pkg/release" "helm.sh/helm/v3/pkg/releaseutil" ) @@ -152,11 +154,22 @@ func (l *List) Run() ([]*release.Release, error) { } } + selectorObj, err := labels.Parse(l.Selector) + if err != nil { + return nil, err + } + results, err := l.cfg.Releases.List(func(rel *release.Release) bool { // Skip anything that doesn't match the filter. if filter != nil && !filter.MatchString(rel.Name) { return false } + + // Skip anything that doesn't match the selector + if ! selectorObj.Matches(labels.Set(rel.Labels)) { + return false + } + return true }) diff --git a/pkg/storage/driver/cfgmaps.go b/pkg/storage/driver/cfgmaps.go index 05213bbdd..94c278875 100644 --- a/pkg/storage/driver/cfgmaps.go +++ b/pkg/storage/driver/cfgmaps.go @@ -105,8 +105,10 @@ func (cfgmaps *ConfigMaps) List(filter func(*rspb.Release) bool) ([]*rspb.Releas cfgmaps.Log("list: failed to decode release: %v: %s", item, err) continue } + + rls.Labels = item.ObjectMeta.Labels + if filter(rls) { - rls.Labels = item.ObjectMeta.Labels results = append(results, rls) } } diff --git a/pkg/storage/driver/secrets.go b/pkg/storage/driver/secrets.go index 1503bc054..64dcf8216 100644 --- a/pkg/storage/driver/secrets.go +++ b/pkg/storage/driver/secrets.go @@ -97,8 +97,10 @@ func (secrets *Secrets) List(filter func(*rspb.Release) bool) ([]*rspb.Release, secrets.Log("list: failed to decode release: %v: %s", item, err) continue } + + rls.Labels = item.ObjectMeta.Labels + if filter(rls) { - rls.Labels = item.ObjectMeta.Labels results = append(results, rls) } } From 09172b468a7c8278c566880c26efd1078e43e5c8 Mon Sep 17 00:00:00 2001 From: Dmitry Chepurovskiy Date: Wed, 29 Jul 2020 23:42:43 +0300 Subject: [PATCH 206/281] Fix linting issues Signed-off-by: Dmitry Chepurovskiy --- pkg/action/list.go | 4 ++-- pkg/storage/driver/secrets.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/action/list.go b/pkg/action/list.go index 9cf7ec138..56008c6fa 100644 --- a/pkg/action/list.go +++ b/pkg/action/list.go @@ -166,10 +166,10 @@ func (l *List) Run() ([]*release.Release, error) { } // Skip anything that doesn't match the selector - if ! selectorObj.Matches(labels.Set(rel.Labels)) { + if !selectorObj.Matches(labels.Set(rel.Labels)) { return false } - + return true }) diff --git a/pkg/storage/driver/secrets.go b/pkg/storage/driver/secrets.go index 64dcf8216..2e8530d0c 100644 --- a/pkg/storage/driver/secrets.go +++ b/pkg/storage/driver/secrets.go @@ -97,7 +97,7 @@ func (secrets *Secrets) List(filter func(*rspb.Release) bool) ([]*rspb.Release, secrets.Log("list: failed to decode release: %v: %s", item, err) continue } - + rls.Labels = item.ObjectMeta.Labels if filter(rls) { From 6d60d26913ef81180eb6cfc13cdc2106e3ae24ef Mon Sep 17 00:00:00 2001 From: Dmitry Chepurovskiy Date: Thu, 30 Jul 2020 20:52:57 +0300 Subject: [PATCH 207/281] Added notice about supported backends Signed-off-by: Dmitry Chepurovskiy --- 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 dfa17d9bc..2f40707b3 100644 --- a/cmd/helm/list.go +++ b/cmd/helm/list.go @@ -126,7 +126,7 @@ func newListCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { f.IntVarP(&client.Limit, "max", "m", 256, "maximum number of releases to fetch") f.IntVar(&client.Offset, "offset", 0, "next release name in the list, used to offset from start value") f.StringVarP(&client.Filter, "filter", "f", "", "a regular expression (Perl compatible). Any releases that match the expression will be included in the results") - f.StringVarP(&client.Selector, "selector", "l", "", "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)") + f.StringVarP(&client.Selector, "selector", "l", "", "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2). Works only for secret(default) and configmap storage backends.") bindOutputFlag(cmd, &outfmt) return cmd From 2ea8f805b9348201f95d6eacd893cccfdaeb8624 Mon Sep 17 00:00:00 2001 From: Dmitry Chepurovskiy Date: Thu, 30 Jul 2020 22:40:46 +0300 Subject: [PATCH 208/281] Added testing for list action with selector Signed-off-by: Dmitry Chepurovskiy --- pkg/action/list_test.go | 46 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/pkg/action/list_test.go b/pkg/action/list_test.go index b8e2ece68..73009d523 100644 --- a/pkg/action/list_test.go +++ b/pkg/action/list_test.go @@ -320,3 +320,49 @@ func TestFilterLatestReleases(t *testing.T) { assert.ElementsMatch(t, expectedFilteredList, filteredList) }) } + +func TestSelectorList(t *testing.T) { + r1 := releaseStub() + r1.Name = "r1" + r1.Version = 1 + r1.Labels = map[string]string{"key": "value1"} + r2 := releaseStub() + r2.Name = "r2" + r2.Version = 1 + r2.Labels = map[string]string{"key": "value2"} + r3 := releaseStub() + r3.Name = "r3" + r3.Version = 1 + r3.Labels = map[string]string{} + + lister := newListFixture(t) + for _, rel := range []*release.Release{r1, r2, r3} { + if err := lister.cfg.Releases.Create(rel); err != nil { + t.Fatal(err) + } + } + + t.Run("should fail selector parsing", func(t *testing.T) { + is := assert.New(t) + lister.Selector = "a?=b" + + _, err := lister.Run() + is.Error(err) + }) + + t.Run("should select one release with matching label", func(t *testing.T) { + lister.Selector = "key==value1" + res, _ := lister.Run() + + expectedFilteredList := []*release.Release{r1} + assert.ElementsMatch(t, expectedFilteredList, res) + }) + + t.Run("should select two releases with non matching label", func(t *testing.T) { + lister.Selector = "key!=value1" + res, _ := lister.Run() + + expectedFilteredList := []*release.Release{r2, r3} + assert.ElementsMatch(t, expectedFilteredList, res) + }) +} From 266c74f390c6dcd08ffbbe942df99d5557632806 Mon Sep 17 00:00:00 2001 From: Dmitry Chepurovskiy Date: Tue, 4 Aug 2020 18:17:56 +0300 Subject: [PATCH 209/281] Move selector filtering after latest release version filtering Signed-off-by: Dmitry Chepurovskiy --- pkg/action/list.go | 29 +++++++++++++++++++---------- pkg/release/release.go | 5 +++-- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/pkg/action/list.go b/pkg/action/list.go index 56008c6fa..5ba0c4770 100644 --- a/pkg/action/list.go +++ b/pkg/action/list.go @@ -154,22 +154,12 @@ func (l *List) Run() ([]*release.Release, error) { } } - selectorObj, err := labels.Parse(l.Selector) - if err != nil { - return nil, err - } - results, err := l.cfg.Releases.List(func(rel *release.Release) bool { // Skip anything that doesn't match the filter. if filter != nil && !filter.MatchString(rel.Name) { return false } - // Skip anything that doesn't match the selector - if !selectorObj.Matches(labels.Set(rel.Labels)) { - return false - } - return true }) @@ -192,6 +182,13 @@ func (l *List) Run() ([]*release.Release, error) { // latest releases, otherwise outdated entries can be returned results = l.filterStateMask(results) + // Skip anything that doesn't match the selector + selectorObj, err := labels.Parse(l.Selector) + if err != nil { + return nil, err + } + results = l.filterSelector(results, selectorObj) + // Unfortunately, we have to sort before truncating, which can incur substantial overhead l.sort(results) @@ -274,6 +271,18 @@ func (l *List) filterStateMask(releases []*release.Release) []*release.Release { return desiredStateReleases } +func (l *List) filterSelector(releases []*release.Release, selector labels.Selector) []*release.Release { + desiredStateReleases := make([]*release.Release, 0) + + for _, rls := range releases { + if selector.Matches(labels.Set(rls.Labels)) { + desiredStateReleases = append(desiredStateReleases, rls) + } + } + + return desiredStateReleases +} + // SetStateMask calculates the state mask based on parameters. func (l *List) SetStateMask() { if l.All { diff --git a/pkg/release/release.go b/pkg/release/release.go index 1245d8129..b90612873 100644 --- a/pkg/release/release.go +++ b/pkg/release/release.go @@ -37,8 +37,9 @@ type Release struct { Version int `json:"version,omitempty"` // Namespace is the kubernetes namespace of the release. Namespace string `json:"namespace,omitempty"` - // Labels of the release - Labels map[string]string `json:"labels,omitempty"` + // Labels of the release. + // Disabled encoding into Json cause labels are stored in storage driver metadata field. + Labels map[string]string `json:"-"` } // SetStatus is a helper for setting the status on a release. From df4708a9de8a6f4c89dca7555a5b338c8298128d Mon Sep 17 00:00:00 2001 From: Dong Gang Date: Wed, 5 Aug 2020 10:37:00 +0800 Subject: [PATCH 210/281] polish the error handler Signed-off-by: Dong Gang --- pkg/action/show.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/action/show.go b/pkg/action/show.go index 73d989884..4eab2b4fd 100644 --- a/pkg/action/show.go +++ b/pkg/action/show.go @@ -20,6 +20,7 @@ import ( "fmt" "strings" + "github.com/pkg/errors" "k8s.io/cli-runtime/pkg/printers" "sigs.k8s.io/yaml" @@ -92,7 +93,7 @@ func (s *Show) Run(chartpath string) (string, error) { if s.JSONPathTemplate != "" { printer, err := printers.NewJSONPathPrinter(s.JSONPathTemplate) if err != nil { - return "", fmt.Errorf("error parsing jsonpath %s, %v", s.JSONPathTemplate, err) + return "", errors.Wrapf(err, "error parsing jsonpath %s", s.JSONPathTemplate) } printer.Execute(&out, s.chart.Values) } else { From 0674d93609bfb82fe49eaf72b0ae84f348f4ee57 Mon Sep 17 00:00:00 2001 From: yxxhero Date: Wed, 5 Aug 2020 11:47:42 +0800 Subject: [PATCH 211/281] add helm v4 todo comments for FindChartInAuthAndTLSRepoURL. Signed-off-by: yxxhero --- pkg/repo/chartrepo.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/repo/chartrepo.go b/pkg/repo/chartrepo.go index 266986a95..edb86eaeb 100644 --- a/pkg/repo/chartrepo.go +++ b/pkg/repo/chartrepo.go @@ -211,6 +211,7 @@ func FindChartInAuthRepoURL(repoURL, username, password, chartName, chartVersion // FindChartInAuthRepoURL finds chart in chart repository pointed by repoURL // without adding repo to repositories, like FindChartInRepoURL, // but it also receives credentials and TLS verify flag for the chart repository. +// TODO Helm 4, FindChartInAuthAndTLSRepoURL should be integrated into FindChartInAuthRepoURL. func FindChartInAuthAndTLSRepoURL(repoURL, username, password, chartName, chartVersion, certFile, keyFile, caFile string, insecureSkipTLSverify bool, getters getter.Providers) (string, error) { // Download and write the index file to a temporary location From 5421c7e99526af9683491fe5e069ed001fb5fd58 Mon Sep 17 00:00:00 2001 From: Tero <31454692+tero-dev@users.noreply.github.com> Date: Thu, 6 Aug 2020 17:00:14 +0300 Subject: [PATCH 212/281] Fix Quick Start Guide Link in README.md The previous link https://docs.helm.sh/using_helm/#quickstart-guide was redirecting into https://helm.sh/ja/docs/intro/ which is a Japanese version of the docs. I've fixed the link so it goes into the English version instead. Signed-off-by: Tero <31454692+tero-dev@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bb6908fdb..9c39175bc 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ If you want to use a package manager: - [GoFish](https://gofi.sh/) users can use `gofish install helm`. - [Snapcraft](https://snapcraft.io/) users can use `snap install helm --classic` -To rapidly get Helm up and running, start with the [Quick Start Guide](https://docs.helm.sh/using_helm/#quickstart-guide). +To rapidly get Helm up and running, start with the [Quick Start Guide](https://helm.sh/docs/intro/quickstart/). See the [installation guide](https://helm.sh/docs/intro/install/) for more options, including installing pre-releases. From 4bd68b75cf0fdf2355285ad0e386fbce806fce5f Mon Sep 17 00:00:00 2001 From: Jinesi Yelizati Date: Sat, 8 Aug 2020 11:17:10 +0800 Subject: [PATCH 213/281] chore(deps): add dependabot.yml Signed-off-by: Jinesi Yelizati --- .github/dependabot.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..68334cf33 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 + +updates: + - package-ecosystem: "gomod" + directory: "/" + schedule: + interval: "daily" \ No newline at end of file From 4b1fa60d5883d53b4e4f09d3420b108e66bbd737 Mon Sep 17 00:00:00 2001 From: Thomas O'Donnell Date: Wed, 5 Aug 2020 19:02:42 +0200 Subject: [PATCH 214/281] Update Common Lables template in starter chart Have update the Common Labels template in the starter chart so that the value for the `app.kubernetes.io/version` is set to the same value as the image tag used in the deployment. Signed-off-by: Thomas O'Donnell --- pkg/chartutil/create.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pkg/chartutil/create.go b/pkg/chartutil/create.go index c036ce37d..cf10f0875 100644 --- a/pkg/chartutil/create.go +++ b/pkg/chartutil/create.go @@ -424,9 +424,7 @@ Common labels {{- define ".labels" -}} helm.sh/chart: {{ include ".chart" . }} {{ include ".selectorLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} +app.kubernetes.io/version: {{ .Values.image.tag | default .Chart.AppVersion | quote }} app.kubernetes.io/managed-by: {{ .Release.Service }} {{- end }} From 83a5e620d0acde77502b1f814f749268e8d8ef6e Mon Sep 17 00:00:00 2001 From: Morten Linderud Date: Wed, 12 Aug 2020 23:00:16 +0200 Subject: [PATCH 215/281] release/mock: Ensure conversion from int to string yields a string With the release of go 1.15, the test-suite doesn't pass as `go test` got a new warning for improper `string(x)` usage. https://golang.org/doc/go1.15#vet $ make test-unit # helm.sh/helm/v3/pkg/release pkg/release/mock.go:56:27: conversion from int to string yields a string of one rune, not a string of digits (did you mean fmt.Sprint(x)?) [snip] make: *** [Makefile:82: test-unit] Error 2 This patch changes ensures we are utilizing `fmt.Sprint` instead as recommended. Signed-off-by: Morten Linderud --- pkg/release/mock.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/release/mock.go b/pkg/release/mock.go index 3abbd574b..a28e1dc16 100644 --- a/pkg/release/mock.go +++ b/pkg/release/mock.go @@ -17,6 +17,7 @@ limitations under the License. package release import ( + "fmt" "math/rand" "helm.sh/helm/v3/pkg/chart" @@ -53,7 +54,7 @@ func Mock(opts *MockReleaseOptions) *Release { name := opts.Name if name == "" { - name = "testrelease-" + string(rand.Intn(100)) + name = "testrelease-" + fmt.Sprint(rand.Intn(100)) } version := 1 From 065b7f6e257f7ee47ca0194f8c3b804e14173514 Mon Sep 17 00:00:00 2001 From: Maartje Eyskens Date: Fri, 14 Aug 2020 09:43:56 +0200 Subject: [PATCH 216/281] Bump Kubernetes to v0.18.8 + Bump jsonpatch jsonpatch now is the same version as used in Kubernetes v0.18.8 Signed-off-by: Maartje Eyskens --- go.mod | 14 +++++++------- go.sum | 20 ++++++++++++++++++++ 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 08eae92fa..66447ee3d 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/docker/distribution v2.7.1+incompatible github.com/docker/docker v1.4.2-0.20200203170920-46ec8731fbce github.com/docker/go-units v0.4.0 - github.com/evanphx/json-patch v4.5.0+incompatible + github.com/evanphx/json-patch v0.0.0-20200808040245-162e5629780b github.com/gobwas/glob v0.2.3 github.com/gofrs/flock v0.7.1 github.com/gosuri/uitable v0.0.4 @@ -34,13 +34,13 @@ require ( github.com/stretchr/testify v1.6.1 github.com/xeipuuv/gojsonschema v1.2.0 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 - k8s.io/api v0.18.4 - k8s.io/apiextensions-apiserver v0.18.4 - k8s.io/apimachinery v0.18.4 - k8s.io/cli-runtime v0.18.4 - k8s.io/client-go v0.18.4 + k8s.io/api v0.18.8 + k8s.io/apiextensions-apiserver v0.18.8 + k8s.io/apimachinery v0.18.8 + k8s.io/cli-runtime v0.18.8 + k8s.io/client-go v0.18.8 k8s.io/klog v1.0.0 - k8s.io/kubectl v0.18.4 + k8s.io/kubectl v0.18.8 sigs.k8s.io/yaml v1.2.0 ) diff --git a/go.sum b/go.sum index 0d0ef8c83..94f5fee82 100644 --- a/go.sum +++ b/go.sum @@ -195,6 +195,8 @@ github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v0.0.0-20200808040245-162e5629780b h1:vCplRbYcTTeBVLjIU0KvipEeVBSxl6sakUBRmeLBTkw= +github.com/evanphx/json-patch v0.0.0-20200808040245-162e5629780b/go.mod h1:NAJj0yf/KaRKURN6nyi7A9IZydMivZEm9oQLWNjfKDc= github.com/evanphx/json-patch v4.2.0+incompatible h1:fUDGZCv/7iAN7u0puUVhvKCcsR6vRfwrJatElLBEf0I= github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M= @@ -395,6 +397,7 @@ github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= @@ -915,18 +918,32 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= k8s.io/api v0.18.4 h1:8x49nBRxuXGUlDlwlWd3RMY1SayZrzFfxea3UZSkFw4= k8s.io/api v0.18.4/go.mod h1:lOIQAKYgai1+vz9J7YcDZwC26Z0zQewYOGWdyIPUUQ4= +k8s.io/api v0.18.8 h1:aIKUzJPb96f3fKec2lxtY7acZC9gQNDLVhfSGpxBAC4= +k8s.io/api v0.18.8/go.mod h1:d/CXqwWv+Z2XEG1LgceeDmHQwpUJhROPx16SlxJgERY= k8s.io/apiextensions-apiserver v0.18.4 h1:Y3HGERmS8t9u12YNUFoOISqefaoGRuTc43AYCLzWmWE= k8s.io/apiextensions-apiserver v0.18.4/go.mod h1:NYeyeYq4SIpFlPxSAB6jHPIdvu3hL0pc36wuRChybio= +k8s.io/apiextensions-apiserver v0.18.8 h1:pkqYPKTHa0/3lYwH7201RpF9eFm0lmZDFBNzhN+k/sA= +k8s.io/apiextensions-apiserver v0.18.8/go.mod h1:7f4ySEkkvifIr4+BRrRWriKKIJjPyg9mb/p63dJKnlM= k8s.io/apimachinery v0.18.4 h1:ST2beySjhqwJoIFk6p7Hp5v5O0hYY6Gngq/gUYXTPIA= k8s.io/apimachinery v0.18.4/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko= +k8s.io/apimachinery v0.18.8 h1:jimPrycCqgx2QPearX3to1JePz7wSbVLq+7PdBTTwQ0= +k8s.io/apimachinery v0.18.8/go.mod h1:6sQd+iHEqmOtALqOFjSWp2KZ9F0wlU/nWm0ZgsYWMig= k8s.io/apiserver v0.18.4/go.mod h1:q+zoFct5ABNnYkGIaGQ3bcbUNdmPyOCoEBcg51LChY8= +k8s.io/apiserver v0.18.8/go.mod h1:12u5FuGql8Cc497ORNj79rhPdiXQC4bf53X/skR/1YM= k8s.io/cli-runtime v0.18.4 h1:IUx7quIOb4gbQ4M+B1ksF/PTBovQuL5tXWzplX3t+FM= k8s.io/cli-runtime v0.18.4/go.mod h1:9/hS/Cuf7NVzWR5F/5tyS6xsnclxoPLVtwhnkJG1Y4g= +k8s.io/cli-runtime v0.18.8 h1:ycmbN3hs7CfkJIYxJAOB10iW7BVPmXGXkfEyiV9NJ+k= +k8s.io/cli-runtime v0.18.8/go.mod h1:7EzWiDbS9PFd0hamHHVoCY4GrokSTPSL32MA4rzIu0M= k8s.io/client-go v0.18.4 h1:un55V1Q/B3JO3A76eS0kUSywgGK/WR3BQ8fHQjNa6Zc= k8s.io/client-go v0.18.4/go.mod h1:f5sXwL4yAZRkAtzOxRWUhA/N8XzGCb+nPZI8PfobZ9g= +k8s.io/client-go v0.18.8 h1:SdbLpIxk5j5YbFr1b7fq8S7mDgDjYmUxSbszyoesoDM= +k8s.io/client-go v0.18.8/go.mod h1:HqFqMllQ5NnQJNwjro9k5zMyfhZlOwpuTLVrxjkYSxU= k8s.io/code-generator v0.18.4/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c= +k8s.io/code-generator v0.18.8/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c= k8s.io/component-base v0.18.4 h1:Kr53Fp1iCGNsl9Uv4VcRvLy7YyIqi9oaJOQ7SXtKI98= k8s.io/component-base v0.18.4/go.mod h1:7jr/Ef5PGmKwQhyAz/pjByxJbC58mhKAhiaDu0vXfPk= +k8s.io/component-base v0.18.8 h1:BW5CORobxb6q5mb+YvdwQlyXXS6NVH5fDXWbU7tf2L8= +k8s.io/component-base v0.18.8/go.mod h1:00frPRDas29rx58pPCxNkhUfPbwajlyyvu8ruNgSErU= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= @@ -937,8 +954,11 @@ k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6 h1:Oh3Mzx5pJ+yIumsAD0MOEC k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= k8s.io/kubectl v0.18.4 h1:l9DUYPTEMs1+qNtoqPpTyaJOosvj7l7tQqphCO1K52s= k8s.io/kubectl v0.18.4/go.mod h1:EzB+nfeUWk6fm6giXQ8P4Fayw3dsN+M7Wjy23mTRtB0= +k8s.io/kubectl v0.18.8 h1:qTkHCz21YmK0+S0oE6TtjtxmjeDP42gJcZJyRKsIenA= +k8s.io/kubectl v0.18.8/go.mod h1:PlEgIAjOMua4hDFTEkVf+W5M0asHUKfE4y7VDZkpLHM= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/metrics v0.18.4/go.mod h1:luze4fyI9JG4eLDZy0kFdYEebqNfi0QrG4xNEbPkHOs= +k8s.io/metrics v0.18.8/go.mod h1:j7JzZdiyhLP2BsJm/Fzjs+j5Lb1Y7TySjhPWqBPwRXA= k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89 h1:d4vVOjXm687F1iLSP2q3lyPPuyvTUt3aVoBpi2DqRsU= k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0= From d6708667da5c79ab4a093ea7473e96b93be5f1f7 Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Sat, 8 Aug 2020 03:43:34 -0400 Subject: [PATCH 217/281] feat(comp): Disable file completion when not valid With Cobra 1.0, it is now possible to control when file completion should or should not be done. For example: helm list should not trigger file completion since 'helm list' does not accept any arguments. This commit disables file completion when appropriate and adds tests to verify that file completion is properly disabled. Signed-off-by: Marc Khouzam --- cmd/helm/completion.go | 16 ++++++--- cmd/helm/completion_test.go | 58 ++++++++++++++++++++++++++++++++ cmd/helm/create.go | 9 +++++ cmd/helm/create_test.go | 5 +++ cmd/helm/dependency.go | 11 +++--- cmd/helm/dependency_test.go | 4 +++ cmd/helm/docs.go | 11 +++--- cmd/helm/docs_test.go | 25 ++++++++++++++ cmd/helm/env.go | 9 ++--- cmd/helm/env_test.go | 25 ++++++++++++++ cmd/helm/get.go | 9 ++--- cmd/helm/get_all_test.go | 5 +++ cmd/helm/get_hooks_test.go | 5 +++ cmd/helm/get_manifest_test.go | 5 +++ cmd/helm/get_notes_test.go | 5 +++ cmd/helm/get_test.go | 25 ++++++++++++++ cmd/helm/get_values_test.go | 5 +++ cmd/helm/history_test.go | 5 +++ cmd/helm/install_test.go | 7 ++++ cmd/helm/lint_test.go | 5 +++ cmd/helm/list.go | 11 +++--- cmd/helm/list_test.go | 4 +++ cmd/helm/package_test.go | 5 +++ cmd/helm/plugin.go | 7 ++-- cmd/helm/plugin_install.go | 8 +++++ cmd/helm/plugin_list.go | 7 ++-- cmd/helm/plugin_test.go | 23 +++++++++++++ cmd/helm/pull_test.go | 5 +++ cmd/helm/release_testing_test.go | 26 ++++++++++++++ cmd/helm/repo.go | 9 ++--- cmd/helm/repo_add.go | 7 ++-- cmd/helm/repo_add_test.go | 6 ++++ cmd/helm/repo_index.go | 8 +++++ cmd/helm/repo_index_test.go | 5 +++ cmd/helm/repo_list.go | 9 ++--- cmd/helm/repo_list_test.go | 4 +++ cmd/helm/repo_remove_test.go | 5 +++ cmd/helm/repo_test.go | 25 ++++++++++++++ cmd/helm/repo_update.go | 11 +++--- cmd/helm/repo_update_test.go | 4 +++ cmd/helm/rollback_test.go | 6 ++++ cmd/helm/root.go | 3 ++ cmd/helm/root_test.go | 8 +++++ cmd/helm/search.go | 7 ++-- cmd/helm/search_hub_test.go | 4 +++ cmd/helm/search_repo.go | 5 +++ cmd/helm/search_repo_test.go | 4 +++ cmd/helm/search_test.go | 23 +++++++++++++ cmd/helm/show.go | 11 +++--- cmd/helm/show_test.go | 20 +++++++++++ cmd/helm/status_test.go | 5 +++ cmd/helm/template_test.go | 7 ++++ cmd/helm/uninstall_test.go | 5 +++ cmd/helm/upgrade_test.go | 6 ++++ cmd/helm/verify.go | 8 +++++ cmd/helm/verify_test.go | 5 +++ cmd/helm/version.go | 9 ++--- cmd/helm/version_test.go | 4 +++ 58 files changed, 517 insertions(+), 61 deletions(-) create mode 100644 cmd/helm/completion_test.go create mode 100644 cmd/helm/docs_test.go create mode 100644 cmd/helm/env_test.go create mode 100644 cmd/helm/get_test.go create mode 100644 cmd/helm/release_testing_test.go create mode 100644 cmd/helm/repo_test.go create mode 100644 cmd/helm/search_test.go diff --git a/cmd/helm/completion.go b/cmd/helm/completion.go index 696021363..c7e763c3d 100644 --- a/cmd/helm/completion.go +++ b/cmd/helm/completion.go @@ -54,10 +54,11 @@ $ helm completion zsh > "${fpath[1]}/_helm" func newCompletionCmd(out io.Writer) *cobra.Command { cmd := &cobra.Command{ - Use: "completion", - Short: "generate autocompletions script for the specified shell", - Long: completionDesc, - Args: require.NoArgs, + Use: "completion", + Short: "generate autocompletions script for the specified shell", + Long: completionDesc, + Args: require.NoArgs, + ValidArgsFunction: noCompletions, // Disable file completion } bash := &cobra.Command{ @@ -66,6 +67,7 @@ func newCompletionCmd(out io.Writer) *cobra.Command { Long: bashCompDesc, Args: require.NoArgs, DisableFlagsInUseLine: true, + ValidArgsFunction: noCompletions, RunE: func(cmd *cobra.Command, args []string) error { return runCompletionBash(out, cmd) }, @@ -77,6 +79,7 @@ func newCompletionCmd(out io.Writer) *cobra.Command { Long: zshCompDesc, Args: require.NoArgs, DisableFlagsInUseLine: true, + ValidArgsFunction: noCompletions, RunE: func(cmd *cobra.Command, args []string) error { return runCompletionZsh(out, cmd) }, @@ -253,3 +256,8 @@ __helm_bash_source <(__helm_convert_bash_to_zsh) out.Write([]byte(zshTail)) return nil } + +// Function to disable file completion +func noCompletions(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return nil, cobra.ShellCompDirectiveNoFileComp +} diff --git a/cmd/helm/completion_test.go b/cmd/helm/completion_test.go new file mode 100644 index 000000000..44c74f85a --- /dev/null +++ b/cmd/helm/completion_test.go @@ -0,0 +1,58 @@ +/* +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 main + +import ( + "fmt" + "strings" + "testing" + + "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/release" +) + +// Check if file completion should be performed according to parameter 'shouldBePerformed' +func checkFileCompletion(t *testing.T, cmdName string, shouldBePerformed bool) { + storage := storageFixture() + storage.Create(&release.Release{ + Name: "myrelease", + Info: &release.Info{Status: release.StatusDeployed}, + Chart: &chart.Chart{}, + Version: 1, + }) + + testcmd := fmt.Sprintf("__complete %s ''", cmdName) + _, out, err := executeActionCommandC(storage, testcmd) + if err != nil { + t.Errorf("unexpected error, %s", err) + } + if !strings.Contains(out, "ShellCompDirectiveNoFileComp") != shouldBePerformed { + if shouldBePerformed { + t.Error(fmt.Sprintf("Unexpected directive ShellCompDirectiveNoFileComp when completing '%s'", cmdName)) + } else { + + t.Error(fmt.Sprintf("Did not receive directive ShellCompDirectiveNoFileComp when completing '%s'", cmdName)) + } + t.Log(out) + } +} + +func TestCompletionFileCompletion(t *testing.T) { + checkFileCompletion(t, "completion", false) + checkFileCompletion(t, "completion bash", false) + checkFileCompletion(t, "completion zsh", false) +} diff --git a/cmd/helm/create.go b/cmd/helm/create.go index 5bdda05cb..21a7e026c 100644 --- a/cmd/helm/create.go +++ b/cmd/helm/create.go @@ -64,6 +64,15 @@ func newCreateCmd(out io.Writer) *cobra.Command { Short: "create a new chart with the given name", Long: createDesc, Args: require.ExactArgs(1), + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) == 0 { + // Allow file completion when completing the argument for the name + // which could be a path + return nil, cobra.ShellCompDirectiveDefault + } + // No more completions, so disable file completion + return nil, cobra.ShellCompDirectiveNoFileComp + }, RunE: func(cmd *cobra.Command, args []string) error { o.name = args[0] o.starterDir = helmpath.DataPath("starters") diff --git a/cmd/helm/create_test.go b/cmd/helm/create_test.go index bbb8d394a..1db6bed52 100644 --- a/cmd/helm/create_test.go +++ b/cmd/helm/create_test.go @@ -192,3 +192,8 @@ func TestCreateStarterAbsoluteCmd(t *testing.T) { t.Error("Did not find foo.tpl") } } + +func TestCreateFileCompletion(t *testing.T) { + checkFileCompletion(t, "create", true) + checkFileCompletion(t, "create myname", false) +} diff --git a/cmd/helm/dependency.go b/cmd/helm/dependency.go index 2cc4c5045..39dfd98ce 100644 --- a/cmd/helm/dependency.go +++ b/cmd/helm/dependency.go @@ -84,11 +84,12 @@ This will produce an error if the chart cannot be loaded. func newDependencyCmd(out io.Writer) *cobra.Command { cmd := &cobra.Command{ - Use: "dependency update|build|list", - Aliases: []string{"dep", "dependencies"}, - Short: "manage a chart's dependencies", - Long: dependencyDesc, - Args: require.NoArgs, + Use: "dependency update|build|list", + Aliases: []string{"dep", "dependencies"}, + Short: "manage a chart's dependencies", + Long: dependencyDesc, + Args: require.NoArgs, + ValidArgsFunction: noCompletions, // Disable file completion } cmd.AddCommand(newDependencyListCmd(out)) diff --git a/cmd/helm/dependency_test.go b/cmd/helm/dependency_test.go index 80b357a77..34c6a25e1 100644 --- a/cmd/helm/dependency_test.go +++ b/cmd/helm/dependency_test.go @@ -51,3 +51,7 @@ func TestDependencyListCmd(t *testing.T) { }} runTestCmd(t, tests) } + +func TestDependencyFileCompletion(t *testing.T) { + checkFileCompletion(t, "dependency", false) +} diff --git a/cmd/helm/docs.go b/cmd/helm/docs.go index c974d4014..621b17ca1 100644 --- a/cmd/helm/docs.go +++ b/cmd/helm/docs.go @@ -47,11 +47,12 @@ func newDocsCmd(out io.Writer) *cobra.Command { o := &docsOptions{} cmd := &cobra.Command{ - Use: "docs", - Short: "generate documentation as markdown or man pages", - Long: docsDesc, - Hidden: true, - Args: require.NoArgs, + Use: "docs", + Short: "generate documentation as markdown or man pages", + Long: docsDesc, + Hidden: true, + Args: require.NoArgs, + ValidArgsFunction: noCompletions, RunE: func(cmd *cobra.Command, args []string) error { o.topCmd = cmd.Root() return o.run(out) diff --git a/cmd/helm/docs_test.go b/cmd/helm/docs_test.go new file mode 100644 index 000000000..cda299b0a --- /dev/null +++ b/cmd/helm/docs_test.go @@ -0,0 +1,25 @@ +/* +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 main + +import ( + "testing" +) + +func TestDocsFileCompletion(t *testing.T) { + checkFileCompletion(t, "docs", false) +} diff --git a/cmd/helm/env.go b/cmd/helm/env.go index 0fbfb9da4..4db3d80de 100644 --- a/cmd/helm/env.go +++ b/cmd/helm/env.go @@ -32,10 +32,11 @@ Env prints out all the environment information in use by Helm. func newEnvCmd(out io.Writer) *cobra.Command { cmd := &cobra.Command{ - Use: "env", - Short: "helm client environment information", - Long: envHelp, - Args: require.NoArgs, + Use: "env", + Short: "helm client environment information", + Long: envHelp, + Args: require.NoArgs, + ValidArgsFunction: noCompletions, Run: func(cmd *cobra.Command, args []string) { envVars := settings.EnvVars() diff --git a/cmd/helm/env_test.go b/cmd/helm/env_test.go new file mode 100644 index 000000000..a7170a8cc --- /dev/null +++ b/cmd/helm/env_test.go @@ -0,0 +1,25 @@ +/* +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 main + +import ( + "testing" +) + +func TestEnvFileCompletion(t *testing.T) { + checkFileCompletion(t, "env", false) +} diff --git a/cmd/helm/get.go b/cmd/helm/get.go index 7c4854b59..e94871c7b 100644 --- a/cmd/helm/get.go +++ b/cmd/helm/get.go @@ -37,10 +37,11 @@ get extended information about the release, including: func newGetCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { cmd := &cobra.Command{ - Use: "get", - Short: "download extended information of a named release", - Long: getHelp, - Args: require.NoArgs, + Use: "get", + Short: "download extended information of a named release", + Long: getHelp, + Args: require.NoArgs, + ValidArgsFunction: noCompletions, // Disable file completion } cmd.AddCommand(newGetAllCmd(cfg, out)) diff --git a/cmd/helm/get_all_test.go b/cmd/helm/get_all_test.go index a213ac583..0c140faf8 100644 --- a/cmd/helm/get_all_test.go +++ b/cmd/helm/get_all_test.go @@ -45,3 +45,8 @@ func TestGetCmd(t *testing.T) { func TestGetAllRevisionCompletion(t *testing.T) { revisionFlagCompletionTest(t, "get all") } + +func TestGetAllFileCompletion(t *testing.T) { + checkFileCompletion(t, "get all", false) + checkFileCompletion(t, "get all myrelease", false) +} diff --git a/cmd/helm/get_hooks_test.go b/cmd/helm/get_hooks_test.go index 7b9142d12..6c010d1bd 100644 --- a/cmd/helm/get_hooks_test.go +++ b/cmd/helm/get_hooks_test.go @@ -40,3 +40,8 @@ func TestGetHooks(t *testing.T) { func TestGetHooksRevisionCompletion(t *testing.T) { revisionFlagCompletionTest(t, "get hooks") } + +func TestGetHooksFileCompletion(t *testing.T) { + checkFileCompletion(t, "get hooks", false) + checkFileCompletion(t, "get hooks myrelease", false) +} diff --git a/cmd/helm/get_manifest_test.go b/cmd/helm/get_manifest_test.go index bd0ffc5d6..f3f572e80 100644 --- a/cmd/helm/get_manifest_test.go +++ b/cmd/helm/get_manifest_test.go @@ -40,3 +40,8 @@ func TestGetManifest(t *testing.T) { func TestGetManifestRevisionCompletion(t *testing.T) { revisionFlagCompletionTest(t, "get manifest") } + +func TestGetManifestFileCompletion(t *testing.T) { + checkFileCompletion(t, "get manifest", false) + checkFileCompletion(t, "get manifest myrelease", false) +} diff --git a/cmd/helm/get_notes_test.go b/cmd/helm/get_notes_test.go index a59120b77..7d43c87e7 100644 --- a/cmd/helm/get_notes_test.go +++ b/cmd/helm/get_notes_test.go @@ -40,3 +40,8 @@ func TestGetNotesCmd(t *testing.T) { func TestGetNotesRevisionCompletion(t *testing.T) { revisionFlagCompletionTest(t, "get notes") } + +func TestGetNotesFileCompletion(t *testing.T) { + checkFileCompletion(t, "get notes", false) + checkFileCompletion(t, "get notes myrelease", false) +} diff --git a/cmd/helm/get_test.go b/cmd/helm/get_test.go new file mode 100644 index 000000000..79f914bea --- /dev/null +++ b/cmd/helm/get_test.go @@ -0,0 +1,25 @@ +/* +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 main + +import ( + "testing" +) + +func TestGetFileCompletion(t *testing.T) { + checkFileCompletion(t, "get", false) +} diff --git a/cmd/helm/get_values_test.go b/cmd/helm/get_values_test.go index ecd92d354..2a71b1e4d 100644 --- a/cmd/helm/get_values_test.go +++ b/cmd/helm/get_values_test.go @@ -60,3 +60,8 @@ func TestGetValuesRevisionCompletion(t *testing.T) { func TestGetValuesOutputCompletion(t *testing.T) { outputFlagCompletionTest(t, "get values") } + +func TestGetValuesFileCompletion(t *testing.T) { + checkFileCompletion(t, "get values", false) + checkFileCompletion(t, "get values myrelease", false) +} diff --git a/cmd/helm/history_test.go b/cmd/helm/history_test.go index c9bfb648e..fffd983da 100644 --- a/cmd/helm/history_test.go +++ b/cmd/helm/history_test.go @@ -108,3 +108,8 @@ func revisionFlagCompletionTest(t *testing.T, cmdName string) { }} runTestCmd(t, tests) } + +func TestHistoryFileCompletion(t *testing.T) { + checkFileCompletion(t, "history", false) + checkFileCompletion(t, "history myrelease", false) +} diff --git a/cmd/helm/install_test.go b/cmd/helm/install_test.go index 36de648f9..6892fcd86 100644 --- a/cmd/helm/install_test.go +++ b/cmd/helm/install_test.go @@ -239,3 +239,10 @@ func TestInstallVersionCompletion(t *testing.T) { }} runTestCmd(t, tests) } + +func TestInstallFileCompletion(t *testing.T) { + checkFileCompletion(t, "install", false) + checkFileCompletion(t, "install --generate-name", true) + checkFileCompletion(t, "install myname", true) + checkFileCompletion(t, "install myname mychart", false) +} diff --git a/cmd/helm/lint_test.go b/cmd/helm/lint_test.go index 9079935d4..3501ccf87 100644 --- a/cmd/helm/lint_test.go +++ b/cmd/helm/lint_test.go @@ -36,3 +36,8 @@ func TestLintCmdWithSubchartsFlag(t *testing.T) { }} runTestCmd(t, tests) } + +func TestLintFileCompletion(t *testing.T) { + checkFileCompletion(t, "lint", true) + checkFileCompletion(t, "lint mypath", true) // Multiple paths can be given +} diff --git a/cmd/helm/list.go b/cmd/helm/list.go index 2f40707b3..08a26be04 100644 --- a/cmd/helm/list.go +++ b/cmd/helm/list.go @@ -63,11 +63,12 @@ func newListCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { var outfmt output.Format cmd := &cobra.Command{ - Use: "list", - Short: "list releases", - Long: listHelp, - Aliases: []string{"ls"}, - Args: require.NoArgs, + Use: "list", + Short: "list releases", + Long: listHelp, + Aliases: []string{"ls"}, + Args: require.NoArgs, + ValidArgsFunction: noCompletions, RunE: func(cmd *cobra.Command, args []string) error { if client.AllNamespaces { if err := cfg.Init(settings.RESTClientGetter(), "", os.Getenv("HELM_DRIVER"), debug); err != nil { diff --git a/cmd/helm/list_test.go b/cmd/helm/list_test.go index dadb57b94..b3b29356e 100644 --- a/cmd/helm/list_test.go +++ b/cmd/helm/list_test.go @@ -235,3 +235,7 @@ func TestListCmd(t *testing.T) { func TestListOutputCompletion(t *testing.T) { outputFlagCompletionTest(t, "list") } + +func TestListFileCompletion(t *testing.T) { + checkFileCompletion(t, "list", false) +} diff --git a/cmd/helm/package_test.go b/cmd/helm/package_test.go index e0a5fabd6..ecb3ee36c 100644 --- a/cmd/helm/package_test.go +++ b/cmd/helm/package_test.go @@ -202,3 +202,8 @@ func setFlags(cmd *cobra.Command, flags map[string]string) { dest.Set(f, v) } } + +func TestPackageFileCompletion(t *testing.T) { + checkFileCompletion(t, "package", true) + checkFileCompletion(t, "package mypath", true) // Multiple paths can be given +} diff --git a/cmd/helm/plugin.go b/cmd/helm/plugin.go index 8e1044f54..a118a03eb 100644 --- a/cmd/helm/plugin.go +++ b/cmd/helm/plugin.go @@ -32,9 +32,10 @@ Manage client-side Helm plugins. func newPluginCmd(out io.Writer) *cobra.Command { cmd := &cobra.Command{ - Use: "plugin", - Short: "install, list, or uninstall Helm plugins", - Long: pluginHelp, + Use: "plugin", + Short: "install, list, or uninstall Helm plugins", + Long: pluginHelp, + ValidArgsFunction: noCompletions, // Disable file completion } cmd.AddCommand( newPluginInstallCmd(out), diff --git a/cmd/helm/plugin_install.go b/cmd/helm/plugin_install.go index 8deb1f4f9..183d3dc57 100644 --- a/cmd/helm/plugin_install.go +++ b/cmd/helm/plugin_install.go @@ -43,6 +43,14 @@ func newPluginInstallCmd(out io.Writer) *cobra.Command { Long: pluginInstallDesc, Aliases: []string{"add"}, Args: require.ExactArgs(1), + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) == 0 { + // We do file completion, in case the plugin is local + return nil, cobra.ShellCompDirectiveDefault + } + // No more completion once the plugin path has been specified + return nil, cobra.ShellCompDirectiveNoFileComp + }, PreRunE: func(cmd *cobra.Command, args []string) error { return o.complete(args) }, diff --git a/cmd/helm/plugin_list.go b/cmd/helm/plugin_list.go index 49a454963..6503161e8 100644 --- a/cmd/helm/plugin_list.go +++ b/cmd/helm/plugin_list.go @@ -28,9 +28,10 @@ import ( func newPluginListCmd(out io.Writer) *cobra.Command { cmd := &cobra.Command{ - Use: "list", - Aliases: []string{"ls"}, - Short: "list installed Helm plugins", + Use: "list", + Aliases: []string{"ls"}, + Short: "list installed Helm plugins", + ValidArgsFunction: noCompletions, RunE: func(cmd *cobra.Command, args []string) error { debug("pluginDirs: %s", settings.PluginsDirectory) plugins, err := plugin.FindPlugins(settings.PluginsDirectory) diff --git a/cmd/helm/plugin_test.go b/cmd/helm/plugin_test.go index e43f277a5..cf21d8460 100644 --- a/cmd/helm/plugin_test.go +++ b/cmd/helm/plugin_test.go @@ -298,3 +298,26 @@ func TestLoadPlugins_HelmNoPlugins(t *testing.T) { t.Fatalf("Expected 0 plugins, got %d", len(plugins)) } } + +func TestPluginFileCompletion(t *testing.T) { + checkFileCompletion(t, "plugin", false) +} + +func TestPluginInstallFileCompletion(t *testing.T) { + checkFileCompletion(t, "plugin install", true) + checkFileCompletion(t, "plugin install mypath", false) +} + +func TestPluginListFileCompletion(t *testing.T) { + checkFileCompletion(t, "plugin list", false) +} + +func TestPluginUninstallFileCompletion(t *testing.T) { + checkFileCompletion(t, "plugin uninstall", false) + checkFileCompletion(t, "plugin uninstall myplugin", false) +} + +func TestPluginUpdateFileCompletion(t *testing.T) { + checkFileCompletion(t, "plugin update", false) + checkFileCompletion(t, "plugin update myplugin", false) +} diff --git a/cmd/helm/pull_test.go b/cmd/helm/pull_test.go index 435df51f1..3f769a1bc 100644 --- a/cmd/helm/pull_test.go +++ b/cmd/helm/pull_test.go @@ -221,3 +221,8 @@ func TestPullVersionCompletion(t *testing.T) { }} runTestCmd(t, tests) } + +func TestPullFileCompletion(t *testing.T) { + checkFileCompletion(t, "pull", false) + checkFileCompletion(t, "pull repo/chart", false) +} diff --git a/cmd/helm/release_testing_test.go b/cmd/helm/release_testing_test.go new file mode 100644 index 000000000..257e95721 --- /dev/null +++ b/cmd/helm/release_testing_test.go @@ -0,0 +1,26 @@ +/* +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 main + +import ( + "testing" +) + +func TestReleaseTestingFileCompletion(t *testing.T) { + checkFileCompletion(t, "test", false) + checkFileCompletion(t, "test myrelease", false) +} diff --git a/cmd/helm/repo.go b/cmd/helm/repo.go index ad6ceaa8f..5aac38819 100644 --- a/cmd/helm/repo.go +++ b/cmd/helm/repo.go @@ -34,10 +34,11 @@ It can be used to add, remove, list, and index chart repositories. func newRepoCmd(out io.Writer) *cobra.Command { cmd := &cobra.Command{ - Use: "repo add|remove|list|index|update [ARGS]", - Short: "add, list, remove, update, and index chart repositories", - Long: repoHelm, - Args: require.NoArgs, + Use: "repo add|remove|list|index|update [ARGS]", + Short: "add, list, remove, update, and index chart repositories", + Long: repoHelm, + Args: require.NoArgs, + ValidArgsFunction: noCompletions, // Disable file completion } cmd.AddCommand(newRepoAddCmd(out)) diff --git a/cmd/helm/repo_add.go b/cmd/helm/repo_add.go index 418a549c6..3eeb342f5 100644 --- a/cmd/helm/repo_add.go +++ b/cmd/helm/repo_add.go @@ -57,9 +57,10 @@ func newRepoAddCmd(out io.Writer) *cobra.Command { o := &repoAddOptions{} cmd := &cobra.Command{ - Use: "add [NAME] [URL]", - Short: "add a chart repository", - Args: require.ExactArgs(2), + Use: "add [NAME] [URL]", + Short: "add a chart repository", + Args: require.ExactArgs(2), + ValidArgsFunction: noCompletions, RunE: func(cmd *cobra.Command, args []string) error { o.name = args[0] o.url = args[1] diff --git a/cmd/helm/repo_add_test.go b/cmd/helm/repo_add_test.go index 7cb669a1a..9ef64390b 100644 --- a/cmd/helm/repo_add_test.go +++ b/cmd/helm/repo_add_test.go @@ -160,3 +160,9 @@ func repoAddConcurrent(t *testing.T, testName, repoFile string) { } } } + +func TestRepoAddFileCompletion(t *testing.T) { + checkFileCompletion(t, "repo add", false) + checkFileCompletion(t, "repo add reponame", false) + checkFileCompletion(t, "repo add reponame https://example.com", false) +} diff --git a/cmd/helm/repo_index.go b/cmd/helm/repo_index.go index 63afaf37b..917acd442 100644 --- a/cmd/helm/repo_index.go +++ b/cmd/helm/repo_index.go @@ -53,6 +53,14 @@ func newRepoIndexCmd(out io.Writer) *cobra.Command { Short: "generate an index file given a directory containing packaged charts", Long: repoIndexDesc, Args: require.ExactArgs(1), + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) == 0 { + // Allow file completion when completing the argument for the directory + return nil, cobra.ShellCompDirectiveDefault + } + // No more completions, so disable file completion + return nil, cobra.ShellCompDirectiveNoFileComp + }, RunE: func(cmd *cobra.Command, args []string) error { o.dir = args[0] return o.run(out) diff --git a/cmd/helm/repo_index_test.go b/cmd/helm/repo_index_test.go index e04ae1b59..ae3390154 100644 --- a/cmd/helm/repo_index_test.go +++ b/cmd/helm/repo_index_test.go @@ -165,3 +165,8 @@ func copyFile(dst, src string) error { return err } + +func TestRepoIndexFileCompletion(t *testing.T) { + checkFileCompletion(t, "repo index", true) + checkFileCompletion(t, "repo index mydir", false) +} diff --git a/cmd/helm/repo_list.go b/cmd/helm/repo_list.go index ed1c9573c..fc53ba75a 100644 --- a/cmd/helm/repo_list.go +++ b/cmd/helm/repo_list.go @@ -32,10 +32,11 @@ import ( func newRepoListCmd(out io.Writer) *cobra.Command { var outfmt output.Format cmd := &cobra.Command{ - Use: "list", - Aliases: []string{"ls"}, - Short: "list chart repositories", - Args: require.NoArgs, + Use: "list", + Aliases: []string{"ls"}, + Short: "list chart repositories", + Args: require.NoArgs, + ValidArgsFunction: noCompletions, RunE: func(cmd *cobra.Command, args []string) error { f, err := repo.LoadFile(settings.RepositoryConfig) if isNotExist(err) || (len(f.Repositories) == 0 && !(outfmt == output.JSON || outfmt == output.YAML)) { diff --git a/cmd/helm/repo_list_test.go b/cmd/helm/repo_list_test.go index f371452f2..90149ebda 100644 --- a/cmd/helm/repo_list_test.go +++ b/cmd/helm/repo_list_test.go @@ -23,3 +23,7 @@ import ( func TestRepoListOutputCompletion(t *testing.T) { outputFlagCompletionTest(t, "repo list") } + +func TestRepoListFileCompletion(t *testing.T) { + checkFileCompletion(t, "repo list", false) +} diff --git a/cmd/helm/repo_remove_test.go b/cmd/helm/repo_remove_test.go index f7d50140e..0ea1d63d2 100644 --- a/cmd/helm/repo_remove_test.go +++ b/cmd/helm/repo_remove_test.go @@ -160,3 +160,8 @@ func testCacheFiles(t *testing.T, cacheIndexFile string, cacheChartsFile string, t.Errorf("Error cache chart file was not removed for repository %s", repoName) } } + +func TestRepoRemoveFileCompletion(t *testing.T) { + checkFileCompletion(t, "repo remove", false) + checkFileCompletion(t, "repo remove repo1", false) +} diff --git a/cmd/helm/repo_test.go b/cmd/helm/repo_test.go new file mode 100644 index 000000000..2b0df7c4c --- /dev/null +++ b/cmd/helm/repo_test.go @@ -0,0 +1,25 @@ +/* +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 main + +import ( + "testing" +) + +func TestRepoFileCompletion(t *testing.T) { + checkFileCompletion(t, "repo", false) +} diff --git a/cmd/helm/repo_update.go b/cmd/helm/repo_update.go index cbfc05b00..e845751c1 100644 --- a/cmd/helm/repo_update.go +++ b/cmd/helm/repo_update.go @@ -46,11 +46,12 @@ func newRepoUpdateCmd(out io.Writer) *cobra.Command { o := &repoUpdateOptions{update: updateCharts} cmd := &cobra.Command{ - Use: "update", - Aliases: []string{"up"}, - Short: "update information of available charts locally from chart repositories", - Long: updateDesc, - Args: require.NoArgs, + Use: "update", + Aliases: []string{"up"}, + Short: "update information of available charts locally from chart repositories", + Long: updateDesc, + Args: require.NoArgs, + ValidArgsFunction: noCompletions, RunE: func(cmd *cobra.Command, args []string) error { o.repoFile = settings.RepositoryConfig o.repoCache = settings.RepositoryCache diff --git a/cmd/helm/repo_update_test.go b/cmd/helm/repo_update_test.go index 981f0bba3..e5e4eb337 100644 --- a/cmd/helm/repo_update_test.go +++ b/cmd/helm/repo_update_test.go @@ -100,3 +100,7 @@ func TestUpdateCharts(t *testing.T) { t.Error("Update was not successful") } } + +func TestRepoUpdateFileCompletion(t *testing.T) { + checkFileCompletion(t, "repo update", false) +} diff --git a/cmd/helm/rollback_test.go b/cmd/helm/rollback_test.go index c11a7fca6..b39378f92 100644 --- a/cmd/helm/rollback_test.go +++ b/cmd/helm/rollback_test.go @@ -104,3 +104,9 @@ func TestRollbackRevisionCompletion(t *testing.T) { }} runTestCmd(t, tests) } + +func TestRollbackFileCompletion(t *testing.T) { + checkFileCompletion(t, "rollback", false) + checkFileCompletion(t, "rollback myrelease", false) + checkFileCompletion(t, "rollback myrelease 1", false) +} diff --git a/cmd/helm/root.go b/cmd/helm/root.go index 449009cb5..904f11a21 100644 --- a/cmd/helm/root.go +++ b/cmd/helm/root.go @@ -75,6 +75,9 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string Short: "The Helm package manager for Kubernetes.", Long: globalUsage, SilenceUsage: true, + // This breaks completion for 'helm help ' + // The Cobra release following 1.0 will fix this + //ValidArgsFunction: noCompletions, // Disable file completion } flags := cmd.PersistentFlags() diff --git a/cmd/helm/root_test.go b/cmd/helm/root_test.go index 891bb698e..075544971 100644 --- a/cmd/helm/root_test.go +++ b/cmd/helm/root_test.go @@ -121,3 +121,11 @@ func TestUnknownSubCmd(t *testing.T) { t.Errorf("Expect unknown command error, got %q", err) } } + +// Need the release of Cobra following 1.0 to be able to disable +// file completion on the root command. Until then, we cannot +// because it would break 'helm help ' +// +// func TestRootFileCompletion(t *testing.T) { +// checkFileCompletion(t, "", false) +// } diff --git a/cmd/helm/search.go b/cmd/helm/search.go index 240d5e7c7..44c8d64e3 100644 --- a/cmd/helm/search.go +++ b/cmd/helm/search.go @@ -31,9 +31,10 @@ search subcommands to search different locations for charts. func newSearchCmd(out io.Writer) *cobra.Command { cmd := &cobra.Command{ - Use: "search [keyword]", - Short: "search for a keyword in charts", - Long: searchDesc, + Use: "search [keyword]", + Short: "search for a keyword in charts", + Long: searchDesc, + ValidArgsFunction: noCompletions, // Disable file completion } cmd.AddCommand(newSearchHubCmd(out)) diff --git a/cmd/helm/search_hub_test.go b/cmd/helm/search_hub_test.go index 7b0f3a389..4f62eed74 100644 --- a/cmd/helm/search_hub_test.go +++ b/cmd/helm/search_hub_test.go @@ -54,3 +54,7 @@ func TestSearchHubCmd(t *testing.T) { func TestSearchHubOutputCompletion(t *testing.T) { outputFlagCompletionTest(t, "search hub") } + +func TestSearchHubFileCompletion(t *testing.T) { + checkFileCompletion(t, "search hub", true) // File completion may be useful when inputing a keyword +} diff --git a/cmd/helm/search_repo.go b/cmd/helm/search_repo.go index dd530379b..a7f27f179 100644 --- a/cmd/helm/search_repo.go +++ b/cmd/helm/search_repo.go @@ -365,6 +365,11 @@ func compListCharts(toComplete string, includeFiles bool) ([]string, cobra.Shell // We handle it ourselves instead. completions = compEnforceNoSpace(completions) } + if !includeFiles { + // If we should not include files in the completions, + // we should disable file completion + directive = directive | cobra.ShellCompDirectiveNoFileComp + } return completions, directive } diff --git a/cmd/helm/search_repo_test.go b/cmd/helm/search_repo_test.go index 402ef2970..39c9c53f5 100644 --- a/cmd/helm/search_repo_test.go +++ b/cmd/helm/search_repo_test.go @@ -87,3 +87,7 @@ func TestSearchRepositoriesCmd(t *testing.T) { func TestSearchRepoOutputCompletion(t *testing.T) { outputFlagCompletionTest(t, "search repo") } + +func TestSearchRepoFileCompletion(t *testing.T) { + checkFileCompletion(t, "search repo", true) // File completion may be useful when inputing a keyword +} diff --git a/cmd/helm/search_test.go b/cmd/helm/search_test.go new file mode 100644 index 000000000..6cf845b06 --- /dev/null +++ b/cmd/helm/search_test.go @@ -0,0 +1,23 @@ +/* +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 main + +import "testing" + +func TestSearchFileCompletion(t *testing.T) { + checkFileCompletion(t, "search", false) +} diff --git a/cmd/helm/show.go b/cmd/helm/show.go index fe5eaee26..888d2d3f3 100644 --- a/cmd/helm/show.go +++ b/cmd/helm/show.go @@ -55,11 +55,12 @@ func newShowCmd(out io.Writer) *cobra.Command { client := action.NewShow(action.ShowAll) showCommand := &cobra.Command{ - Use: "show", - Short: "show information of a chart", - Aliases: []string{"inspect"}, - Long: showDesc, - Args: require.NoArgs, + Use: "show", + Short: "show information of a chart", + Aliases: []string{"inspect"}, + Long: showDesc, + Args: require.NoArgs, + ValidArgsFunction: noCompletions, // Disable file completion } // Function providing dynamic auto-completion diff --git a/cmd/helm/show_test.go b/cmd/helm/show_test.go index 6c550282f..2734faf5e 100644 --- a/cmd/helm/show_test.go +++ b/cmd/helm/show_test.go @@ -118,3 +118,23 @@ func TestShowVersionCompletion(t *testing.T) { }} runTestCmd(t, tests) } + +func TestShowFileCompletion(t *testing.T) { + checkFileCompletion(t, "show", false) +} + +func TestShowAllFileCompletion(t *testing.T) { + checkFileCompletion(t, "show all", true) +} + +func TestShowChartFileCompletion(t *testing.T) { + checkFileCompletion(t, "show chart", true) +} + +func TestShowReadmeFileCompletion(t *testing.T) { + checkFileCompletion(t, "show readme", true) +} + +func TestShowValuesFileCompletion(t *testing.T) { + checkFileCompletion(t, "show values", true) +} diff --git a/cmd/helm/status_test.go b/cmd/helm/status_test.go index 0d2500e65..18731e465 100644 --- a/cmd/helm/status_test.go +++ b/cmd/helm/status_test.go @@ -171,3 +171,8 @@ func TestStatusRevisionCompletion(t *testing.T) { func TestStatusOutputCompletion(t *testing.T) { outputFlagCompletionTest(t, "status") } + +func TestStatusFileCompletion(t *testing.T) { + checkFileCompletion(t, "status", false) + checkFileCompletion(t, "status myrelease", false) +} diff --git a/cmd/helm/template_test.go b/cmd/helm/template_test.go index f8cd31347..6f7ca939d 100644 --- a/cmd/helm/template_test.go +++ b/cmd/helm/template_test.go @@ -154,3 +154,10 @@ func TestTemplateVersionCompletion(t *testing.T) { }} runTestCmd(t, tests) } + +func TestTemplateFileCompletion(t *testing.T) { + checkFileCompletion(t, "template", false) + checkFileCompletion(t, "template --generate-name", true) + checkFileCompletion(t, "template myname", true) + checkFileCompletion(t, "template myname mychart", false) +} diff --git a/cmd/helm/uninstall_test.go b/cmd/helm/uninstall_test.go index a34934077..ad78361c1 100644 --- a/cmd/helm/uninstall_test.go +++ b/cmd/helm/uninstall_test.go @@ -66,3 +66,8 @@ func TestUninstall(t *testing.T) { } runTestCmd(t, tests) } + +func TestUninstallFileCompletion(t *testing.T) { + checkFileCompletion(t, "uninstall", false) + checkFileCompletion(t, "uninstall myrelease", false) +} diff --git a/cmd/helm/upgrade_test.go b/cmd/helm/upgrade_test.go index 64daf2934..6fe79ebce 100644 --- a/cmd/helm/upgrade_test.go +++ b/cmd/helm/upgrade_test.go @@ -407,3 +407,9 @@ func TestUpgradeVersionCompletion(t *testing.T) { }} runTestCmd(t, tests) } + +func TestUpgradeFileCompletion(t *testing.T) { + checkFileCompletion(t, "upgrade", false) + checkFileCompletion(t, "upgrade myrelease", true) + checkFileCompletion(t, "upgrade myrelease repo/chart", false) +} diff --git a/cmd/helm/verify.go b/cmd/helm/verify.go index f26fb377f..d126c9ef3 100644 --- a/cmd/helm/verify.go +++ b/cmd/helm/verify.go @@ -44,6 +44,14 @@ func newVerifyCmd(out io.Writer) *cobra.Command { Short: "verify that a chart at the given path has been signed and is valid", Long: verifyDesc, Args: require.ExactArgs(1), + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) == 0 { + // Allow file completion when completing the argument for the path + return nil, cobra.ShellCompDirectiveDefault + } + // No more completions, so disable file completion + return nil, cobra.ShellCompDirectiveNoFileComp + }, RunE: func(cmd *cobra.Command, args []string) error { err := client.Run(args[0]) if err != nil { diff --git a/cmd/helm/verify_test.go b/cmd/helm/verify_test.go index ccbcb3cf2..23b793557 100644 --- a/cmd/helm/verify_test.go +++ b/cmd/helm/verify_test.go @@ -90,3 +90,8 @@ func TestVerifyCmd(t *testing.T) { }) } } + +func TestVerifyFileCompletion(t *testing.T) { + checkFileCompletion(t, "verify", true) + checkFileCompletion(t, "verify mypath", false) +} diff --git a/cmd/helm/version.go b/cmd/helm/version.go index 2f50c876c..72f93e545 100644 --- a/cmd/helm/version.go +++ b/cmd/helm/version.go @@ -59,10 +59,11 @@ func newVersionCmd(out io.Writer) *cobra.Command { o := &versionOptions{} cmd := &cobra.Command{ - Use: "version", - Short: "print the client version information", - Long: versionDesc, - Args: require.NoArgs, + Use: "version", + Short: "print the client version information", + Long: versionDesc, + Args: require.NoArgs, + ValidArgsFunction: noCompletions, RunE: func(cmd *cobra.Command, args []string) error { return o.run(out) }, diff --git a/cmd/helm/version_test.go b/cmd/helm/version_test.go index 134401948..aa3cbfb7d 100644 --- a/cmd/helm/version_test.go +++ b/cmd/helm/version_test.go @@ -43,3 +43,7 @@ func TestVersion(t *testing.T) { }} runTestCmd(t, tests) } + +func TestVersionFileCompletion(t *testing.T) { + checkFileCompletion(t, "version", false) +} From b07b2589fb157f017079c0f7285f98bc56a33ae2 Mon Sep 17 00:00:00 2001 From: Tariq Ibrahim Date: Tue, 18 Aug 2020 21:58:21 -0700 Subject: [PATCH 218/281] optimise if condition in service ready logic Signed-off-by: Tariq Ibrahim --- pkg/kube/wait.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/kube/wait.go b/pkg/kube/wait.go index 90a9f8b38..c3beb232d 100644 --- a/pkg/kube/wait.go +++ b/pkg/kube/wait.go @@ -188,8 +188,8 @@ func (w *waiter) serviceReady(s *corev1.Service) bool { return true } - // Make sure the service is not explicitly set to "None" before checking the IP - if s.Spec.ClusterIP != corev1.ClusterIPNone && s.Spec.ClusterIP == "" { + // Ensure that the service cluster IP is not empty + if s.Spec.ClusterIP == "" { w.log("Service does not have cluster IP address: %s/%s", s.GetNamespace(), s.GetName()) return false } From 9664bb2a0a528e1b9262501dfaee587e0e459c39 Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Wed, 19 Aug 2020 13:20:54 -0700 Subject: [PATCH 219/281] add v4 to list of exempt labels Signed-off-by: Matthew Fisher --- .github/workflows/stale-issue-bot.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stale-issue-bot.yaml b/.github/workflows/stale-issue-bot.yaml index e21e6d5ca..946e7f09b 100644 --- a/.github/workflows/stale-issue-bot.yaml +++ b/.github/workflows/stale-issue-bot.yaml @@ -10,6 +10,6 @@ jobs: with: repo-token: ${{ secrets.GITHUB_TOKEN }} stale-issue-message: 'This issue has been marked as stale because it has been open for 90 days with no activity. This thread will be automatically closed in 30 days if no further activity occurs.' - exempt-issue-labels: 'keep open' + exempt-issue-labels: 'keep open,v4.x' days-before-stale: 90 days-before-close: 30 From d13c43d3e8d54c09d82d2aff68b06255538649c8 Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Wed, 19 Aug 2020 13:33:12 -0700 Subject: [PATCH 220/281] use URL escape codes Signed-off-by: Matthew Fisher --- .github/workflows/stale-issue-bot.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stale-issue-bot.yaml b/.github/workflows/stale-issue-bot.yaml index 946e7f09b..32ea22418 100644 --- a/.github/workflows/stale-issue-bot.yaml +++ b/.github/workflows/stale-issue-bot.yaml @@ -10,6 +10,6 @@ jobs: with: repo-token: ${{ secrets.GITHUB_TOKEN }} stale-issue-message: 'This issue has been marked as stale because it has been open for 90 days with no activity. This thread will be automatically closed in 30 days if no further activity occurs.' - exempt-issue-labels: 'keep open,v4.x' + exempt-issue-labels: 'keep+open,v4.x' days-before-stale: 90 days-before-close: 30 From 11f658e2234f5d3c02212e30ba38cd579d134aaa Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Thu, 20 Aug 2020 13:08:48 -0400 Subject: [PATCH 221/281] Fixing linting of templates on Windows When the engine stored templates in the map the keys were generated based on path and not filepath. filepath was being used in the linter when retrieving content from the keys. On Windows the keys ended up being different. This change is to use path joins to create the lookup key. Since the name path was used in the code it needed to be changed in order to import the package. Tests already exist and were failing on windows. This got in because CI is not run on Windows. Closes #6418 Signed-off-by: Matt Farina --- pkg/lint/rules/template.go | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/pkg/lint/rules/template.go b/pkg/lint/rules/template.go index 3da5b63fa..9a3a2a1ba 100644 --- a/pkg/lint/rules/template.go +++ b/pkg/lint/rules/template.go @@ -19,6 +19,7 @@ package rules import ( "fmt" "os" + "path" "path/filepath" "regexp" "strings" @@ -47,10 +48,10 @@ var validName = regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([- // Templates lints the templates in the Linter. func Templates(linter *support.Linter, values map[string]interface{}, namespace string, strict bool) { - path := "templates/" - templatesPath := filepath.Join(linter.ChartDir, path) + fpath := "templates/" + templatesPath := filepath.Join(linter.ChartDir, fpath) - templatesDirExist := linter.RunLinterRule(support.WarningSev, path, validateTemplatesDir(templatesPath)) + templatesDirExist := linter.RunLinterRule(support.WarningSev, fpath, validateTemplatesDir(templatesPath)) // Templates directory is optional for now if !templatesDirExist { @@ -60,7 +61,7 @@ func Templates(linter *support.Linter, values map[string]interface{}, namespace // Load chart and parse templates chart, err := loader.Load(linter.ChartDir) - chartLoaded := linter.RunLinterRule(support.ErrorSev, path, err) + chartLoaded := linter.RunLinterRule(support.ErrorSev, fpath, err) if !chartLoaded { return @@ -77,14 +78,14 @@ func Templates(linter *support.Linter, values map[string]interface{}, namespace } valuesToRender, err := chartutil.ToRenderValues(chart, cvals, options, nil) if err != nil { - linter.RunLinterRule(support.ErrorSev, path, err) + linter.RunLinterRule(support.ErrorSev, fpath, err) return } var e engine.Engine e.LintMode = true renderedContentMap, err := e.Render(chart, valuesToRender) - renderOk := linter.RunLinterRule(support.ErrorSev, path, err) + renderOk := linter.RunLinterRule(support.ErrorSev, fpath, err) if !renderOk { return @@ -99,13 +100,13 @@ func Templates(linter *support.Linter, values map[string]interface{}, namespace */ for _, template := range chart.Templates { fileName, data := template.Name, template.Data - path = fileName + fpath = fileName - linter.RunLinterRule(support.ErrorSev, path, validateAllowedExtension(fileName)) + linter.RunLinterRule(support.ErrorSev, fpath, validateAllowedExtension(fileName)) // These are v3 specific checks to make sure and warn people if their // chart is not compatible with v3 - linter.RunLinterRule(support.WarningSev, path, validateNoCRDHooks(data)) - linter.RunLinterRule(support.ErrorSev, path, validateNoReleaseTime(data)) + linter.RunLinterRule(support.WarningSev, fpath, validateNoCRDHooks(data)) + linter.RunLinterRule(support.ErrorSev, fpath, validateNoReleaseTime(data)) // We only apply the following lint rules to yaml files if filepath.Ext(fileName) != ".yaml" || filepath.Ext(fileName) == ".yml" { @@ -114,12 +115,12 @@ func Templates(linter *support.Linter, values map[string]interface{}, namespace // NOTE: disabled for now, Refs https://github.com/helm/helm/issues/1463 // Check that all the templates have a matching value - //linter.RunLinterRule(support.WarningSev, path, validateNoMissingValues(templatesPath, valuesToRender, preExecutedTemplate)) + //linter.RunLinterRule(support.WarningSev, fpath, validateNoMissingValues(templatesPath, valuesToRender, preExecutedTemplate)) // NOTE: disabled for now, Refs https://github.com/helm/helm/issues/1037 - // linter.RunLinterRule(support.WarningSev, path, validateQuotes(string(preExecutedTemplate))) + // linter.RunLinterRule(support.WarningSev, fpath, validateQuotes(string(preExecutedTemplate))) - renderedContent := renderedContentMap[filepath.Join(chart.Name(), fileName)] + renderedContent := renderedContentMap[path.Join(chart.Name(), fileName)] if strings.TrimSpace(renderedContent) != "" { var yamlStruct K8sYamlStruct // Even though K8sYamlStruct only defines a few fields, an error in any other @@ -128,10 +129,10 @@ func Templates(linter *support.Linter, values map[string]interface{}, namespace // If YAML linting fails, we sill progress. So we don't capture the returned state // on this linter run. - linter.RunLinterRule(support.ErrorSev, path, validateYamlContent(err)) - linter.RunLinterRule(support.ErrorSev, path, validateMetadataName(&yamlStruct)) - linter.RunLinterRule(support.ErrorSev, path, validateNoDeprecations(&yamlStruct)) - linter.RunLinterRule(support.ErrorSev, path, validateMatchSelector(&yamlStruct, renderedContent)) + linter.RunLinterRule(support.ErrorSev, fpath, validateYamlContent(err)) + linter.RunLinterRule(support.ErrorSev, fpath, validateMetadataName(&yamlStruct)) + linter.RunLinterRule(support.ErrorSev, fpath, validateNoDeprecations(&yamlStruct)) + linter.RunLinterRule(support.ErrorSev, fpath, validateMatchSelector(&yamlStruct, renderedContent)) } } } From 8a545d6ca7e40ae412c53f5dc683ecc8a8ecdb96 Mon Sep 17 00:00:00 2001 From: Ma Xinjian Date: Fri, 21 Aug 2020 14:47:29 +0800 Subject: [PATCH 222/281] Correct checksum file links Signed-off-by: Ma Xinjian --- scripts/release-notes.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/release-notes.sh b/scripts/release-notes.sh index dd48d4a17..3625aaa9a 100755 --- a/scripts/release-notes.sh +++ b/scripts/release-notes.sh @@ -39,8 +39,8 @@ done ## Check for hints that checksum files were downloaded ## from `make fetch-dist` -if [[ ! -e "./_dist/helm-${RELEASE}-darwin-amd64.tar.gz.sha256" ]]; then - echo "checksum file ./_dist/helm-${RELEASE}-darwin-amd64.tar.gz.sha256 not found in ./_dist/" +if [[ ! -e "./_dist/helm-${RELEASE}-darwin-amd64.tar.gz.sha256sum" ]]; then + echo "checksum file ./_dist/helm-${RELEASE}-darwin-amd64.tar.gz.sha256sum not found in ./_dist/" echo "Did you forget to run \`make fetch-dist\` first ?" exit 1 fi From 4abcdc40efb9ad455ed99bc73c8ee716fe89123d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20R=C3=BCger?= Date: Fri, 21 Aug 2020 11:21:43 +0200 Subject: [PATCH 223/281] pkg/*: Small linting fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Manuel Rüger --- pkg/action/package.go | 2 +- pkg/repo/chartrepo_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/action/package.go b/pkg/action/package.go index 19d845cf3..5059eee07 100644 --- a/pkg/action/package.go +++ b/pkg/action/package.go @@ -135,7 +135,7 @@ func (p *Package) Clearsign(filename string) error { // promptUser implements provenance.PassphraseFetcher func promptUser(name string) ([]byte, error) { fmt.Printf("Password for key %q > ", name) - pw, err := terminal.ReadPassword(int(syscall.Stdin)) + pw, err := terminal.ReadPassword(syscall.Stdin) fmt.Println() return pw, err } diff --git a/pkg/repo/chartrepo_test.go b/pkg/repo/chartrepo_test.go index f50d6a2b6..eceb3009e 100644 --- a/pkg/repo/chartrepo_test.go +++ b/pkg/repo/chartrepo_test.go @@ -172,7 +172,7 @@ func TestIndexCustomSchemeDownload(t *testing.T) { func verifyIndex(t *testing.T, actual *IndexFile) { var empty time.Time - if actual.Generated == empty { + if actual.Generated.Equal(empty) { t.Errorf("Generated should be greater than 0: %s", actual.Generated) } @@ -242,7 +242,7 @@ func verifyIndex(t *testing.T, actual *IndexFile) { if len(g.Maintainers) != 2 { t.Error("Expected 2 maintainers.") } - if g.Created == empty { + if g.Created.Equal(empty) { t.Error("Expected created to be non-empty") } if g.Description == "" { From 3fc88f24929b3cac6e5284bbb5701ff94b3f11e0 Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Fri, 21 Aug 2020 11:51:58 -0400 Subject: [PATCH 224/281] Fixing failing CI for windows A fix introduced in #8631 caused a bug in Windows builds due to a type difference between POSIX and Windows environments. This change corrects that problem and provides a code comment to warn others. Signed-off-by: Matt Farina --- pkg/action/package.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/action/package.go b/pkg/action/package.go index 5059eee07..0a927cd41 100644 --- a/pkg/action/package.go +++ b/pkg/action/package.go @@ -135,7 +135,9 @@ func (p *Package) Clearsign(filename string) error { // promptUser implements provenance.PassphraseFetcher func promptUser(name string) ([]byte, error) { fmt.Printf("Password for key %q > ", name) - pw, err := terminal.ReadPassword(syscall.Stdin) + // syscall.Stdin is not an int in all environments and needs to be coerced + // into one there (e.g., Windows) + pw, err := terminal.ReadPassword(int(syscall.Stdin)) fmt.Println() return pw, err } From 10d4d2ed999a403e2464f673e0e19b95ee4e1ac6 Mon Sep 17 00:00:00 2001 From: rudeigerc Date: Sat, 22 Aug 2020 02:30:22 +0800 Subject: [PATCH 225/281] feat(env): add support of accepting a specific variable for helm env Signed-off-by: Yuchen Cheng --- cmd/helm/env.go | 48 +++++++++++++++++++-------- cmd/helm/env_test.go | 9 +++++ cmd/helm/testdata/output/env-comp.txt | 16 +++++++++ 3 files changed, 59 insertions(+), 14 deletions(-) create mode 100644 cmd/helm/testdata/output/env-comp.txt diff --git a/cmd/helm/env.go b/cmd/helm/env.go index 4db3d80de..3754b748d 100644 --- a/cmd/helm/env.go +++ b/cmd/helm/env.go @@ -32,25 +32,45 @@ Env prints out all the environment information in use by Helm. func newEnvCmd(out io.Writer) *cobra.Command { cmd := &cobra.Command{ - Use: "env", - Short: "helm client environment information", - Long: envHelp, - Args: require.NoArgs, - ValidArgsFunction: noCompletions, + Use: "env", + Short: "helm client environment information", + Long: envHelp, + Args: require.MaximumNArgs(1), + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) == 0 { + keys := getSortedEnvVarKeys() + return keys, cobra.ShellCompDirectiveNoFileComp + } + + return nil, cobra.ShellCompDirectiveNoFileComp + }, Run: func(cmd *cobra.Command, args []string) { envVars := settings.EnvVars() - // Sort the variables by alphabetical order. - // This allows for a constant output across calls to 'helm env'. - var keys []string - for k := range envVars { - keys = append(keys, k) - } - sort.Strings(keys) - for _, k := range keys { - fmt.Fprintf(out, "%s=\"%s\"\n", k, envVars[k]) + if len(args) == 0 { + // Sort the variables by alphabetical order. + // This allows for a constant output across calls to 'helm env'. + keys := getSortedEnvVarKeys() + + for _, k := range keys { + fmt.Fprintf(out, "%s=\"%s\"\n", k, envVars[k]) + } + } else { + fmt.Fprintf(out, "%s\n", envVars[args[0]]) } }, } return cmd } + +func getSortedEnvVarKeys() []string { + envVars := settings.EnvVars() + + var keys []string + for k := range envVars { + keys = append(keys, k) + } + sort.Strings(keys) + + return keys +} diff --git a/cmd/helm/env_test.go b/cmd/helm/env_test.go index a7170a8cc..abdc9c94e 100644 --- a/cmd/helm/env_test.go +++ b/cmd/helm/env_test.go @@ -20,6 +20,15 @@ import ( "testing" ) +func TestEnv(t *testing.T) { + tests := []cmdTestCase{{ + name: "completion for env", + cmd: "__complete env ''", + golden: "output/env-comp.txt", + }} + runTestCmd(t, tests) +} + func TestEnvFileCompletion(t *testing.T) { checkFileCompletion(t, "env", false) } diff --git a/cmd/helm/testdata/output/env-comp.txt b/cmd/helm/testdata/output/env-comp.txt new file mode 100644 index 000000000..c4b46ae6b --- /dev/null +++ b/cmd/helm/testdata/output/env-comp.txt @@ -0,0 +1,16 @@ +HELM_BIN +HELM_CACHE_HOME +HELM_CONFIG_HOME +HELM_DATA_HOME +HELM_DEBUG +HELM_KUBEAPISERVER +HELM_KUBECONTEXT +HELM_KUBETOKEN +HELM_MAX_HISTORY +HELM_NAMESPACE +HELM_PLUGINS +HELM_REGISTRY_CONFIG +HELM_REPOSITORY_CACHE +HELM_REPOSITORY_CONFIG +:4 +Completion ended with directive: ShellCompDirectiveNoFileComp From 195d7a650712d8eee2db887bf8e1a4e2c363328b Mon Sep 17 00:00:00 2001 From: Yuchen Cheng Date: Sat, 22 Aug 2020 22:04:09 +0800 Subject: [PATCH 226/281] add checkFileCompletion for env HELM_BIN Signed-off-by: Yuchen Cheng --- cmd/helm/env_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/helm/env_test.go b/cmd/helm/env_test.go index abdc9c94e..01ef25933 100644 --- a/cmd/helm/env_test.go +++ b/cmd/helm/env_test.go @@ -31,4 +31,5 @@ func TestEnv(t *testing.T) { func TestEnvFileCompletion(t *testing.T) { checkFileCompletion(t, "env", false) + checkFileCompletion(t, "env HELM_BIN", false) } From 0669f40e814707ce43c560f33f363d19f40e6800 Mon Sep 17 00:00:00 2001 From: Zhou Hao Date: Fri, 28 Aug 2020 13:38:29 +0800 Subject: [PATCH 227/281] cleanup tempfiles for load_test Signed-off-by: Zhou Hao --- pkg/chart/loader/load_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/chart/loader/load_test.go b/pkg/chart/loader/load_test.go index 40b86dec2..16a94d4eb 100644 --- a/pkg/chart/loader/load_test.go +++ b/pkg/chart/loader/load_test.go @@ -310,7 +310,7 @@ func TestLoadInvalidArchive(t *testing.T) { if err != nil { t.Fatal(err) } - defer os.Remove(tmpdir) + defer os.RemoveAll(tmpdir) writeTar := func(filename, internalPath string, body []byte) { dest, err := os.Create(filename) From 45b084b25504a1e19592684f964f43b57792875a Mon Sep 17 00:00:00 2001 From: knrt10 Date: Fri, 28 Aug 2020 18:23:33 +0530 Subject: [PATCH 228/281] Fix spelling in completion.go Signed-off-by: knrt10 --- cmd/helm/completion.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/helm/completion.go b/cmd/helm/completion.go index c7e763c3d..db50b375a 100644 --- a/cmd/helm/completion.go +++ b/cmd/helm/completion.go @@ -151,7 +151,7 @@ __helm_compgen() { fi for w in "${completions[@]}"; do if [[ "${w}" = "$1"* ]]; then - # Use printf instead of echo beause it is possible that + # Use printf instead of echo because it is possible that # the value to print is -n, which would be interpreted # as a flag to echo printf "%s\n" "${w}" From 96d9ab9663b69cbd85444ca5232d8283017eeeea Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Tue, 1 Sep 2020 10:44:52 -0600 Subject: [PATCH 229/281] fix name length check on lint (#8543) Signed-off-by: Matt Butcher --- go.mod | 1 + pkg/action/upgrade.go | 3 ++- pkg/lint/rules/template.go | 3 +++ pkg/lint/rules/template_test.go | 10 ++++++++++ 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 66447ee3d..4b7c90f9d 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.13 require ( github.com/BurntSushi/toml v0.3.1 github.com/DATA-DOG/go-sqlmock v1.4.1 + github.com/Masterminds/goutils v1.1.0 github.com/Masterminds/semver/v3 v3.1.0 github.com/Masterminds/sprig/v3 v3.1.0 github.com/Masterminds/squirrel v1.4.0 diff --git a/pkg/action/upgrade.go b/pkg/action/upgrade.go index e7c2aec25..b707e7e69 100644 --- a/pkg/action/upgrade.go +++ b/pkg/action/upgrade.go @@ -147,7 +147,8 @@ func validateReleaseName(releaseName string) error { return errMissingRelease } - if !ValidName.MatchString(releaseName) || (len(releaseName) > releaseNameMaxLen) { + // Check length first, since that is a less expensive operation. + if len(releaseName) > releaseNameMaxLen || !ValidName.MatchString(releaseName) { return errInvalidName } diff --git a/pkg/lint/rules/template.go b/pkg/lint/rules/template.go index 9a3a2a1ba..cd54c8915 100644 --- a/pkg/lint/rules/template.go +++ b/pkg/lint/rules/template.go @@ -165,6 +165,9 @@ func validateYamlContent(err error) error { } func validateMetadataName(obj *K8sYamlStruct) error { + if len(obj.Metadata.Name) == 0 || len(obj.Metadata.Name) > 253 { + return fmt.Errorf("object name must be between 0 and 253 characters: %q", obj.Metadata.Name) + } // This will return an error if the characters do not abide by the standard OR if the // name is left empty. if validName.MatchString(obj.Metadata.Name) { diff --git a/pkg/lint/rules/template_test.go b/pkg/lint/rules/template_test.go index ae82c8922..3b0307c1d 100644 --- a/pkg/lint/rules/template_test.go +++ b/pkg/lint/rules/template_test.go @@ -22,6 +22,8 @@ import ( "strings" "testing" + "github.com/Masterminds/goutils" + "helm.sh/helm/v3/internal/test/ensure" "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/chartutil" @@ -119,6 +121,14 @@ func TestValidateMetadataName(t *testing.T) { "a..b": false, "%^&#$%*@^*@&#^": false, } + + // The length checker should catch this first. So this is not true fuzzing. + tooLong, err := goutils.RandomAlphaNumeric(300) + if err != nil { + t.Fatalf("Randomizer failed to initialize: %s", err) + } + names[tooLong] = false + for input, expectPass := range names { obj := K8sYamlStruct{Metadata: k8sYamlMetadata{Name: input}} if err := validateMetadataName(&obj); (err == nil) != expectPass { From 70d03e5cefa8f42727e29db310f78aeae4d65bb0 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Tue, 1 Sep 2020 10:45:59 -0600 Subject: [PATCH 230/281] Fix/8467 linter failing (#8496) * add output to get debug info on linter failing Signed-off-by: Matt Butcher * trap cases where the YAML indent is incorrect. Signed-off-by: Matt Butcher --- pkg/lint/rules/template.go | 29 +++++++++++++++++++++++++++++ pkg/lint/rules/template_test.go | 17 +++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/pkg/lint/rules/template.go b/pkg/lint/rules/template.go index cd54c8915..ac65b3932 100644 --- a/pkg/lint/rules/template.go +++ b/pkg/lint/rules/template.go @@ -17,6 +17,8 @@ limitations under the License. package rules import ( + "bufio" + "bytes" "fmt" "os" "path" @@ -122,6 +124,7 @@ func Templates(linter *support.Linter, values map[string]interface{}, namespace renderedContent := renderedContentMap[path.Join(chart.Name(), fileName)] if strings.TrimSpace(renderedContent) != "" { + linter.RunLinterRule(support.WarningSev, path, validateTopIndentLevel(renderedContent)) var yamlStruct K8sYamlStruct // Even though K8sYamlStruct only defines a few fields, an error in any other // key will be raised as well @@ -137,6 +140,32 @@ func Templates(linter *support.Linter, values map[string]interface{}, namespace } } +// validateTopIndentLevel checks that the content does not start with an indent level > 0. +// +// This error can occur when a template accidentally inserts space. It can cause +// unpredictable errors dependening on whether the text is normalized before being passed +// into the YAML parser. So we trap it here. +// +// See https://github.com/helm/helm/issues/8467 +func validateTopIndentLevel(content string) error { + // Read lines until we get to a non-empty one + scanner := bufio.NewScanner(bytes.NewBufferString(content)) + for scanner.Scan() { + line := scanner.Text() + // If line is empty, skip + if strings.TrimSpace(line) == "" { + continue + } + // If it starts with one or more spaces, this is an error + if strings.HasPrefix(line, " ") || strings.HasPrefix(line, "\t") { + return fmt.Errorf("document starts with an illegal indent: %q, which may cause parsing problems", line) + } + // Any other condition passes. + return nil + } + return scanner.Err() +} + // Validation functions func validateTemplatesDir(templatesPath string) error { if fi, err := os.Stat(templatesPath); err != nil { diff --git a/pkg/lint/rules/template_test.go b/pkg/lint/rules/template_test.go index 3b0307c1d..b4397851b 100644 --- a/pkg/lint/rules/template_test.go +++ b/pkg/lint/rules/template_test.go @@ -315,3 +315,20 @@ spec: t.Error("expected Deployment with no selector to fail") } } + +func TestValidateTopIndentLevel(t *testing.T) { + for doc, shouldFail := range map[string]bool{ + // Should not fail + "\n\n\n\t\n \t\n": false, + "apiVersion:foo\n bar:baz": false, + "\n\n\napiVersion:foo\n\n\n": false, + // Should fail + " apiVersion:foo": true, + "\n\n apiVersion:foo\n\n": true, + } { + if err := validateTopIndentLevel(doc); (err == nil) == shouldFail { + t.Errorf("Expected %t for %q", shouldFail, doc) + } + } + +} From 04fb35814f64122c0aa08165f6fdb7b67c216558 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Tue, 1 Sep 2020 11:51:40 -0600 Subject: [PATCH 231/281] Fixed a variable name collision caused by two PR merges (#8681) Signed-off-by: Matt Butcher --- pkg/lint/rules/template.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/lint/rules/template.go b/pkg/lint/rules/template.go index ac65b3932..73d645264 100644 --- a/pkg/lint/rules/template.go +++ b/pkg/lint/rules/template.go @@ -124,7 +124,7 @@ func Templates(linter *support.Linter, values map[string]interface{}, namespace renderedContent := renderedContentMap[path.Join(chart.Name(), fileName)] if strings.TrimSpace(renderedContent) != "" { - linter.RunLinterRule(support.WarningSev, path, validateTopIndentLevel(renderedContent)) + linter.RunLinterRule(support.WarningSev, fpath, validateTopIndentLevel(renderedContent)) var yamlStruct K8sYamlStruct // Even though K8sYamlStruct only defines a few fields, an error in any other // key will be raised as well From 317616482cbdca1b47605d707803f31b4ee2a26f Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Wed, 2 Sep 2020 10:30:41 +0800 Subject: [PATCH 232/281] Remove duplicate variable definition Variable values `helm.sh/resource-policy` and `keep` are duplicately defined in resource_policy.go (`resourcePolicyAnno` `keepPolicy`) and resource_policy.go (`ResourcePolicyAnno` `KeepPolicy`), remove the varibales in resource_policy.go to keep the code clean. Signed-off-by: Liu Ming --- pkg/action/resource_policy.go | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/pkg/action/resource_policy.go b/pkg/action/resource_policy.go index cfabdf7ba..63e83f3d9 100644 --- a/pkg/action/resource_policy.go +++ b/pkg/action/resource_policy.go @@ -19,18 +19,10 @@ package action import ( "strings" + "helm.sh/helm/v3/pkg/kube" "helm.sh/helm/v3/pkg/releaseutil" ) -// resourcePolicyAnno is the annotation name for a resource policy -const resourcePolicyAnno = "helm.sh/resource-policy" - -// keepPolicy is the resource policy type for keep -// -// This resource policy type allows resources to skip being deleted -// during an uninstallRelease action. -const keepPolicy = "keep" - func filterManifestsToKeep(manifests []releaseutil.Manifest) (keep, remaining []releaseutil.Manifest) { for _, m := range manifests { if m.Head.Metadata == nil || m.Head.Metadata.Annotations == nil || len(m.Head.Metadata.Annotations) == 0 { @@ -38,14 +30,14 @@ func filterManifestsToKeep(manifests []releaseutil.Manifest) (keep, remaining [] continue } - resourcePolicyType, ok := m.Head.Metadata.Annotations[resourcePolicyAnno] + resourcePolicyType, ok := m.Head.Metadata.Annotations[kube.ResourcePolicyAnno] if !ok { remaining = append(remaining, m) continue } resourcePolicyType = strings.ToLower(strings.TrimSpace(resourcePolicyType)) - if resourcePolicyType == keepPolicy { + if resourcePolicyType == kube.KeepPolicy { keep = append(keep, m) } From da6878dc0f5c99d1062c2c220b0ab5a8d548a773 Mon Sep 17 00:00:00 2001 From: Ling Samuel Date: Wed, 29 Jul 2020 09:36:21 +0800 Subject: [PATCH 233/281] feat: status command display description Signed-off-by: Ling Samuel --- cmd/helm/get_all.go | 2 +- cmd/helm/install.go | 2 +- cmd/helm/release_testing.go | 2 +- cmd/helm/status.go | 13 ++++++++++--- cmd/helm/status_test.go | 8 ++++++++ cmd/helm/testdata/output/status-with-desc.txt | 7 +++++++ cmd/helm/upgrade.go | 4 ++-- pkg/action/status.go | 5 +++++ 8 files changed, 35 insertions(+), 8 deletions(-) create mode 100644 cmd/helm/testdata/output/status-with-desc.txt diff --git a/cmd/helm/get_all.go b/cmd/helm/get_all.go index a5037e4df..53f8d5905 100644 --- a/cmd/helm/get_all.go +++ b/cmd/helm/get_all.go @@ -59,7 +59,7 @@ func newGetAllCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { return tpl(template, data, out) } - return output.Table.Write(out, &statusPrinter{res, true}) + return output.Table.Write(out, &statusPrinter{res, true, false}) }, } diff --git a/cmd/helm/install.go b/cmd/helm/install.go index f7d0239fb..7edd98091 100644 --- a/cmd/helm/install.go +++ b/cmd/helm/install.go @@ -122,7 +122,7 @@ func newInstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { return err } - return outfmt.Write(out, &statusPrinter{rel, settings.Debug}) + return outfmt.Write(out, &statusPrinter{rel, settings.Debug, false}) }, } diff --git a/cmd/helm/release_testing.go b/cmd/helm/release_testing.go index 37cac6186..e4e09ef3b 100644 --- a/cmd/helm/release_testing.go +++ b/cmd/helm/release_testing.go @@ -61,7 +61,7 @@ func newReleaseTestCmd(cfg *action.Configuration, out io.Writer) *cobra.Command return runErr } - if err := outfmt.Write(out, &statusPrinter{rel, settings.Debug}); err != nil { + if err := outfmt.Write(out, &statusPrinter{rel, settings.Debug, false}); err != nil { return err } diff --git a/cmd/helm/status.go b/cmd/helm/status.go index abd32a9c2..7a3204cb9 100644 --- a/cmd/helm/status.go +++ b/cmd/helm/status.go @@ -39,6 +39,8 @@ The status consists of: - last deployment time - k8s namespace in which the release lives - state of the release (can be: unknown, deployed, uninstalled, superseded, failed, uninstalling, pending-install, pending-upgrade or pending-rollback) +- revision of the release +- description of the release (can be completion message or error message, need to enable --show-desc) - list of resources that this release consists of, sorted by kind - details on last test suite run, if applicable - additional notes provided by the chart @@ -68,7 +70,7 @@ func newStatusCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { // strip chart metadata from the output rel.Chart = nil - return outfmt.Write(out, &statusPrinter{rel, false}) + return outfmt.Write(out, &statusPrinter{rel, false, client.ShowDescription}) }, } @@ -88,13 +90,15 @@ func newStatusCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { } bindOutputFlag(cmd, &outfmt) + f.BoolVar(&client.ShowDescription, "show-desc", false, "if set, display the description message of the named release") return cmd } type statusPrinter struct { - release *release.Release - debug bool + release *release.Release + debug bool + showDescription bool } func (s statusPrinter) WriteJSON(out io.Writer) error { @@ -116,6 +120,9 @@ func (s statusPrinter) WriteTable(out io.Writer) error { fmt.Fprintf(out, "NAMESPACE: %s\n", s.release.Namespace) fmt.Fprintf(out, "STATUS: %s\n", s.release.Info.Status.String()) fmt.Fprintf(out, "REVISION: %d\n", s.release.Version) + if s.showDescription { + fmt.Fprintf(out, "DESCRIPTION: %s\n", s.release.Info.Description) + } executions := executionsByHookEvent(s.release) if tests, ok := executions[release.HookTest]; !ok || len(tests) == 0 { diff --git a/cmd/helm/status_test.go b/cmd/helm/status_test.go index 0d2500e65..2d0ce4b03 100644 --- a/cmd/helm/status_test.go +++ b/cmd/helm/status_test.go @@ -44,6 +44,14 @@ func TestStatusCmd(t *testing.T) { rels: releasesMockWithStatus(&release.Info{ Status: release.StatusDeployed, }), + }, { + name: "get status of a deployed release, with desc", + cmd: "status --show-desc flummoxed-chickadee", + golden: "output/status-with-desc.txt", + rels: releasesMockWithStatus(&release.Info{ + Status: release.StatusDeployed, + Description: "Mock description", + }), }, { name: "get status of a deployed release with notes", cmd: "status flummoxed-chickadee", diff --git a/cmd/helm/testdata/output/status-with-desc.txt b/cmd/helm/testdata/output/status-with-desc.txt new file mode 100644 index 000000000..c681fe3ec --- /dev/null +++ b/cmd/helm/testdata/output/status-with-desc.txt @@ -0,0 +1,7 @@ +NAME: flummoxed-chickadee +LAST DEPLOYED: Sat Jan 16 00:00:00 2016 +NAMESPACE: default +STATUS: deployed +REVISION: 0 +DESCRIPTION: Mock description +TEST SUITE: None diff --git a/cmd/helm/upgrade.go b/cmd/helm/upgrade.go index dbddaa368..12d797545 100644 --- a/cmd/helm/upgrade.go +++ b/cmd/helm/upgrade.go @@ -115,7 +115,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { if err != nil { return err } - return outfmt.Write(out, &statusPrinter{rel, settings.Debug}) + return outfmt.Write(out, &statusPrinter{rel, settings.Debug, false}) } else if err != nil { return err } @@ -160,7 +160,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { fmt.Fprintf(out, "Release %q has been upgraded. Happy Helming!\n", args[0]) } - return outfmt.Write(out, &statusPrinter{rel, settings.Debug}) + return outfmt.Write(out, &statusPrinter{rel, settings.Debug, false}) }, } diff --git a/pkg/action/status.go b/pkg/action/status.go index a0c7f6e21..1c556e28d 100644 --- a/pkg/action/status.go +++ b/pkg/action/status.go @@ -27,6 +27,11 @@ type Status struct { cfg *Configuration Version int + + // If true, display description to output format, + // only affect print type table. + // TODO Helm 4: Remove this flag and output the description by default. + ShowDescription bool } // NewStatus creates a new Status object with the given configuration. From 36d931105212758fb7054068e392a75d40d3a6b9 Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Thu, 27 Aug 2020 10:38:38 -0400 Subject: [PATCH 234/281] feat(comp): Add support for fish completion Signed-off-by: Marc Khouzam --- cmd/helm/completion.go | 38 ++++++++++++++++++++++++++++++++++++- cmd/helm/completion_test.go | 1 + 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/cmd/helm/completion.go b/cmd/helm/completion.go index db50b375a..275483f45 100644 --- a/cmd/helm/completion.go +++ b/cmd/helm/completion.go @@ -52,6 +52,25 @@ To load completions for every new session, execute once: $ helm completion zsh > "${fpath[1]}/_helm" ` +const fishCompDesc = ` +Generate the autocompletion script for Helm for the fish shell. + +To load completions in your current shell session: +$ helm completion fish | source + +To load completions for every new session, execute once: +$ helm completion fish > ~/.config/fish/completions/helm.fish + +You will need to start a new shell for this setup to take effect. +` + +const ( + noDescFlagName = "no-descriptions" + noDescFlagText = "disable completion descriptions" +) + +var disableCompDescriptions bool + func newCompletionCmd(out io.Writer) *cobra.Command { cmd := &cobra.Command{ Use: "completion", @@ -85,7 +104,20 @@ func newCompletionCmd(out io.Writer) *cobra.Command { }, } - cmd.AddCommand(bash, zsh) + fish := &cobra.Command{ + Use: "fish", + Short: "generate autocompletions script for fish", + Long: fishCompDesc, + Args: require.NoArgs, + DisableFlagsInUseLine: true, + ValidArgsFunction: noCompletions, + RunE: func(cmd *cobra.Command, args []string) error { + return runCompletionFish(out, cmd) + }, + } + fish.Flags().BoolVar(&disableCompDescriptions, noDescFlagName, false, noDescFlagText) + + cmd.AddCommand(bash, zsh, fish) return cmd } @@ -257,6 +289,10 @@ __helm_bash_source <(__helm_convert_bash_to_zsh) return nil } +func runCompletionFish(out io.Writer, cmd *cobra.Command) error { + return cmd.Root().GenFishCompletion(out, !disableCompDescriptions) +} + // Function to disable file completion func noCompletions(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return nil, cobra.ShellCompDirectiveNoFileComp diff --git a/cmd/helm/completion_test.go b/cmd/helm/completion_test.go index 44c74f85a..7eee39832 100644 --- a/cmd/helm/completion_test.go +++ b/cmd/helm/completion_test.go @@ -55,4 +55,5 @@ func TestCompletionFileCompletion(t *testing.T) { checkFileCompletion(t, "completion", false) checkFileCompletion(t, "completion bash", false) checkFileCompletion(t, "completion zsh", false) + checkFileCompletion(t, "completion fish", false) } From daa104d60e258fff57da12f13c49ecdcba1263c6 Mon Sep 17 00:00:00 2001 From: Martin Hickey Date: Thu, 3 Sep 2020 17:13:31 +0000 Subject: [PATCH 235/281] Revert PR 8562 Revert of PR 8562 as the container version may not represent the application version. Signed-off-by: Martin Hickey --- pkg/chartutil/create.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/chartutil/create.go b/pkg/chartutil/create.go index 8d8f48176..6e382b961 100644 --- a/pkg/chartutil/create.go +++ b/pkg/chartutil/create.go @@ -425,7 +425,9 @@ Common labels {{- define ".labels" -}} helm.sh/chart: {{ include ".chart" . }} {{ include ".selectorLabels" . }} -app.kubernetes.io/version: {{ .Values.image.tag | default .Chart.AppVersion | quote }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} app.kubernetes.io/managed-by: {{ .Release.Service }} {{- end }} From 6898ad14576463ea1df857bb17f2f0ee47653756 Mon Sep 17 00:00:00 2001 From: Josh Dolitsky <393494+jdolitsky@users.noreply.github.com> Date: Tue, 8 Sep 2020 08:48:22 -0500 Subject: [PATCH 236/281] Add GPG signature verification to install script (#7944) * Add GPG signature verification to install script The script fetches the KEYS file from GitHub, as well as the .asc files on the release and verifies the release artifacts are signed by a valid key. Added new boolean config options in the install script which allow for fine-grained control over verification and output: - DEBUG: sets -x in the bash script (default: false) - VERIFY_CHECKSUM: verifies checksum (default: true) - VERIFY_SIGNATURE: verifies signature (default: true) Also reduced check for curl/wget to only one time. Resolves #7943. Resolves #7838. Signed-off-by: Josh Dolitsky <393494+jdolitsky@users.noreply.github.com> * disable signature verification by default Signed-off-by: Josh Dolitsky <393494+jdolitsky@users.noreply.github.com> * remove repeated line Signed-off-by: Josh Dolitsky <393494+jdolitsky@users.noreply.github.com> * fix typo Signed-off-by: Josh Dolitsky <393494+jdolitsky@users.noreply.github.com> * do not auto-import GPG keys Signed-off-by: Josh Dolitsky <393494+jdolitsky@users.noreply.github.com> * silence errors about missing commands Signed-off-by: Josh Dolitsky <393494+jdolitsky@users.noreply.github.com> * use a temporary gpg keyring Signed-off-by: Josh Dolitsky <393494+jdolitsky@users.noreply.github.com> * Fix wget commands for VERIFY_SIGNATURES=true Signed-off-by: jdolitsky <393494+jdolitsky@users.noreply.github.com> --- scripts/get-helm-3 | 128 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 110 insertions(+), 18 deletions(-) diff --git a/scripts/get-helm-3 b/scripts/get-helm-3 index f2495e444..08d0e14ca 100755 --- a/scripts/get-helm-3 +++ b/scripts/get-helm-3 @@ -19,8 +19,16 @@ : ${BINARY_NAME:="helm"} : ${USE_SUDO:="true"} +: ${DEBUG:="false"} +: ${VERIFY_CHECKSUM:="true"} +: ${VERIFY_SIGNATURES:="false"} : ${HELM_INSTALL_DIR:="/usr/local/bin"} +HAS_CURL="$(type "curl" &> /dev/null && echo true || echo false)" +HAS_WGET="$(type "wget" &> /dev/null && echo true || echo false)" +HAS_OPENSSL="$(type "openssl" &> /dev/null && echo true || echo false)" +HAS_GPG="$(type "gpg" &> /dev/null && echo true || echo false)" + # initArch discovers the architecture for this system. initArch() { ARCH=$(uname -m) @@ -58,7 +66,7 @@ runAsRoot() { } # verifySupported checks that the os/arch combination is supported for -# binary builds. +# binary builds, as well whether or not necessary tools are present. verifySupported() { local supported="darwin-amd64\nlinux-386\nlinux-amd64\nlinux-arm\nlinux-arm64\nlinux-ppc64le\nlinux-s390x\nwindows-amd64" if ! echo "${supported}" | grep -q "${OS}-${ARCH}"; then @@ -67,10 +75,29 @@ verifySupported() { exit 1 fi - if ! type "curl" > /dev/null && ! type "wget" > /dev/null; then + if [ "${HAS_CURL}" != "true" ] && [ "${HAS_WGET}" != "true" ]; then echo "Either curl or wget is required" exit 1 fi + + if [ "${VERIFY_CHECKSUM}" == "true" ] && [ "${HAS_OPENSSL}" != "true" ]; then + echo "In order to verify checksum, openssl must first be installed." + echo "Please install openssl or set VERIFY_CHECKSUM=false in your environment." + exit 1 + fi + + if [ "${VERIFY_SIGNATURES}" == "true" ]; then + if [ "${HAS_GPG}" != "true" ]; then + echo "In order to verify signatures, gpg must first be installed." + echo "Please install gpg or set VERIFY_SIGNATURES=false in your environment." + exit 1 + fi + if [ "${OS}" != "linux" ]; then + echo "Signature verification is currently only supported on Linux." + echo "Please set VERIFY_SIGNATURES=false or verify the signatures manually." + exit 1 + fi + fi } # checkDesiredVersion checks if the desired version is available. @@ -78,9 +105,9 @@ checkDesiredVersion() { if [ "x$DESIRED_VERSION" == "x" ]; then # Get tag from release URL local latest_release_url="https://github.com/helm/helm/releases" - if type "curl" > /dev/null; then + if [ "${HAS_CURL}" == "true" ]; then TAG=$(curl -Ls $latest_release_url | grep 'href="/helm/helm/releases/tag/v3.[0-9]*.[0-9]*\"' | grep -v no-underline | head -n 1 | cut -d '"' -f 2 | awk '{n=split($NF,a,"/");print a[n]}' | awk 'a !~ $0{print}; {a=$0}') - elif type "wget" > /dev/null; then + elif [ "${HAS_WGET}" == "true" ]; then TAG=$(wget $latest_release_url -O - 2>&1 | grep 'href="/helm/helm/releases/tag/v3.[0-9]*.[0-9]*\"' | grep -v no-underline | head -n 1 | cut -d '"' -f 2 | awk '{n=split($NF,a,"/");print a[n]}' | awk 'a !~ $0{print}; {a=$0}') fi else @@ -115,35 +142,94 @@ downloadFile() { 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 + if [ "${HAS_CURL}" == "true" ]; 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 + elif [ "${HAS_WGET}" == "true" ]; then + wget -q -O "$HELM_SUM_FILE" "$CHECKSUM_URL" wget -q -O "$HELM_TMP_FILE" "$DOWNLOAD_URL" fi } -# installFile verifies the SHA256 for the file, then unpacks and -# installs it. +# verifyFile verifies the SHA256 checksum of the binary package +# and the GPG signatures for both the package and checksum file +# (depending on settings in environment). +verifyFile() { + if [ "${VERIFY_CHECKSUM}" == "true" ]; then + verifyChecksum + fi + if [ "${VERIFY_SIGNATURES}" == "true" ]; then + verifySignatures + fi +} + +# installFile installs the Helm binary. installFile() { HELM_TMP="$HELM_TMP_ROOT/$BINARY_NAME" + mkdir -p "$HELM_TMP" + tar xf "$HELM_TMP_FILE" -C "$HELM_TMP" + HELM_TMP_BIN="$HELM_TMP/$OS-$ARCH/helm" + echo "Preparing to install $BINARY_NAME into ${HELM_INSTALL_DIR}" + runAsRoot cp "$HELM_TMP_BIN" "$HELM_INSTALL_DIR/$BINARY_NAME" + echo "$BINARY_NAME installed into $HELM_INSTALL_DIR/$BINARY_NAME" +} + +# verifyChecksum verifies the SHA256 checksum of the binary package. +verifyChecksum() { + printf "Verifying checksum... " 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 + echo "Done." +} - mkdir -p "$HELM_TMP" - tar xf "$HELM_TMP_FILE" -C "$HELM_TMP" - HELM_TMP_BIN="$HELM_TMP/$OS-$ARCH/helm" - echo "Preparing to install $BINARY_NAME into ${HELM_INSTALL_DIR}" - runAsRoot cp "$HELM_TMP_BIN" "$HELM_INSTALL_DIR/$BINARY_NAME" - echo "$BINARY_NAME installed into $HELM_INSTALL_DIR/$BINARY_NAME" +# verifySignatures obtains the latest KEYS file from GitHub master branch +# as well as the signature .asc files from the specific GitHub release, +# then verifies that the release artifacts were signed by a maintainer's key. +verifySignatures() { + printf "Verifying signatures... " + local keys_filename="KEYS" + local github_keys_url="https://raw.githubusercontent.com/helm/helm/master/${keys_filename}" + if [ "${HAS_CURL}" == "true" ]; then + curl -SsL "${github_keys_url}" -o "${HELM_TMP_ROOT}/${keys_filename}" + elif [ "${HAS_WGET}" == "true" ]; then + wget -q -O "${HELM_TMP_ROOT}/${keys_filename}" "${github_keys_url}" + fi + local gpg_keyring="${HELM_TMP_ROOT}/keyring.gpg" + local gpg_homedir="${HELM_TMP_ROOT}/gnupg" + mkdir -p -m 0700 "${gpg_homedir}" + local gpg_stderr_device="/dev/null" + if [ "${DEBUG}" == "true" ]; then + gpg_stderr_device="/dev/stderr" + fi + gpg --batch --quiet --homedir="${gpg_homedir}" --import "${HELM_TMP_ROOT}/${keys_filename}" 2> "${gpg_stderr_device}" + gpg --batch --no-default-keyring --keyring "${gpg_homedir}/pubring.kbx" --export > "${gpg_keyring}" + local github_release_url="https://github.com/helm/helm/releases/download/${TAG}" + if [ "${HAS_CURL}" == "true" ]; then + curl -SsL "${github_release_url}/helm-${TAG}-${OS}-${ARCH}.tar.gz.sha256.asc" -o "${HELM_TMP_ROOT}/helm-${TAG}-${OS}-${ARCH}.tar.gz.sha256.asc" + curl -SsL "${github_release_url}/helm-${TAG}-${OS}-${ARCH}.tar.gz.asc" -o "${HELM_TMP_ROOT}/helm-${TAG}-${OS}-${ARCH}.tar.gz.asc" + elif [ "${HAS_WGET}" == "true" ]; then + wget -q -O "${HELM_TMP_ROOT}/helm-${TAG}-${OS}-${ARCH}.tar.gz.sha256.asc" "${github_release_url}/helm-${TAG}-${OS}-${ARCH}.tar.gz.sha256.asc" + wget -q -O "${HELM_TMP_ROOT}/helm-${TAG}-${OS}-${ARCH}.tar.gz.asc" "${github_release_url}/helm-${TAG}-${OS}-${ARCH}.tar.gz.asc" + fi + local error_text="If you think this might be a potential security issue," + error_text="${error_text}\nplease see here: https://github.com/helm/community/blob/master/SECURITY.md" + local num_goodlines_sha=$(gpg --verify --keyring="${gpg_keyring}" --status-fd=1 "${HELM_TMP_ROOT}/helm-${TAG}-${OS}-${ARCH}.tar.gz.sha256.asc" 2> "${gpg_stderr_device}" | grep -c -E '^\[GNUPG:\] (GOODSIG|VALIDSIG)') + if [[ ${num_goodlines_sha} -lt 2 ]]; then + echo "Unable to verify the signature of helm-${TAG}-${OS}-${ARCH}.tar.gz.sha256!" + echo -e "${error_text}" + exit 1 + fi + local num_goodlines_tar=$(gpg --verify --keyring="${gpg_keyring}" --status-fd=1 "${HELM_TMP_ROOT}/helm-${TAG}-${OS}-${ARCH}.tar.gz.asc" 2> "${gpg_stderr_device}" | grep -c -E '^\[GNUPG:\] (GOODSIG|VALIDSIG)') + if [[ ${num_goodlines_tar} -lt 2 ]]; then + echo "Unable to verify the signature of helm-${TAG}-${OS}-${ARCH}.tar.gz!" + echo -e "${error_text}" + exit 1 + fi + echo "Done." } # fail_trap is executed if an error occurs. @@ -195,6 +281,11 @@ cleanup() { trap "fail_trap" EXIT set -e +# Set debug if desired +if [ "${DEBUG}" == "true" ]; then + set -x +fi + # Parsing input arguments (if any) export INPUT_ARGUMENTS="${@}" set -u @@ -229,6 +320,7 @@ verifySupported checkDesiredVersion if ! checkHelmInstalledVersion; then downloadFile + verifyFile installFile fi testVersion From ba4c8029c2faa452496dd743fa41b55e20c9614c Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Wed, 9 Sep 2020 13:22:41 +0800 Subject: [PATCH 237/281] Use T.cleanup() to cleanup helm-action-test T.Cleanup() is introduced since go-1.14 Signed-off-by: Li Zhijian --- pkg/action/action_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/action/action_test.go b/pkg/action/action_test.go index 0cbdb162b..c05b4403d 100644 --- a/pkg/action/action_test.go +++ b/pkg/action/action_test.go @@ -20,6 +20,7 @@ import ( "flag" "io/ioutil" "net/http" + "os" "path/filepath" "testing" @@ -56,6 +57,8 @@ func actionConfigFixture(t *testing.T) *Configuration { t.Fatal(err) } + t.Cleanup(func() { os.RemoveAll(tdir) }) + cache, err := registry.NewCache( registry.CacheOptDebug(true), registry.CacheOptRoot(filepath.Join(tdir, registry.CacheRootDir)), From 6f780bb7502cab2b1eddcdb3a127a15a5b2579f9 Mon Sep 17 00:00:00 2001 From: leigh capili Date: Wed, 9 Sep 2020 14:44:40 -0600 Subject: [PATCH 238/281] Document all env vars for CLI help Signed-off-by: leigh capili --- cmd/helm/root.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cmd/helm/root.go b/cmd/helm/root.go index 904f11a21..0135048a6 100644 --- a/cmd/helm/root.go +++ b/cmd/helm/root.go @@ -48,11 +48,20 @@ Environment variables: | $HELM_CACHE_HOME | set an alternative location for storing cached files. | | $HELM_CONFIG_HOME | set an alternative location for storing Helm configuration. | | $HELM_DATA_HOME | set an alternative location for storing Helm data. | +| $HELM_DEBUG | indicate whether or not Helm is running in Debug mode | | $HELM_DRIVER | set the backend storage driver. Values are: configmap, secret, memory, postgres | | $HELM_DRIVER_SQL_CONNECTION_STRING | set the connection string the SQL storage driver should use. | | $HELM_MAX_HISTORY | set the maximum number of helm release history. | +| $HELM_NAMESPACE | set the namespace used for the helm operations. | | $HELM_NO_PLUGINS | disable plugins. Set HELM_NO_PLUGINS=1 to disable plugins. | +| $HELM_PLUGINS | set the path to the plugins directory | +| $HELM_REGISTRY_CONFIG | set the path to the registry config file. | +| $HELM_REPOSITORY_CACHE | set the path to the repository cache directory | +| $HELM_REPOSITORY_CONFIG | set the path to the repositories file. | | $KUBECONFIG | set an alternative Kubernetes configuration file (default "~/.kube/config") | +| $HELM_KUBEAPISERVER | set the Kubernetes API Server Endpoint for authentication | +| $HELM_KUBECONTEXT | set the name of the kubeconfig context. | +| $HELM_KUBETOKEN | set the Bearer KubeToken used for authentication. | Helm stores cache, configuration, and data based on the following configuration order: From 9429af8b39c6888a41ffa2945d7c73676afb577e Mon Sep 17 00:00:00 2001 From: leigh capili Date: Sat, 5 Sep 2020 21:01:00 -0600 Subject: [PATCH 239/281] Support impersonation via flags similar to kubectl --as="user" Signed-off-by: leigh capili --- cmd/helm/load_plugins.go | 2 +- cmd/helm/plugin_test.go | 6 ++++++ cmd/helm/root.go | 2 ++ cmd/helm/testdata/output/env-comp.txt | 2 ++ pkg/cli/environment.go | 31 ++++++++++++++++++++++----- pkg/cli/environment_test.go | 25 ++++++++++++++++----- 6 files changed, 57 insertions(+), 11 deletions(-) diff --git a/cmd/helm/load_plugins.go b/cmd/helm/load_plugins.go index a6e0c4eae..e4aac6c0f 100644 --- a/cmd/helm/load_plugins.go +++ b/cmd/helm/load_plugins.go @@ -154,7 +154,7 @@ func callPluginExecutable(pluginName string, main string, argv []string, out io. func manuallyProcessArgs(args []string) ([]string, []string) { known := []string{} unknown := []string{} - kvargs := []string{"--kube-context", "--namespace", "-n", "--kubeconfig", "--kube-apiserver", "--kube-token", "--registry-config", "--repository-cache", "--repository-config"} + kvargs := []string{"--kube-context", "--namespace", "-n", "--kubeconfig", "--kube-apiserver", "--kube-token", "--kube-as-user", "--kube-as-group", "--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 cf21d8460..0bf867f2a 100644 --- a/cmd/helm/plugin_test.go +++ b/cmd/helm/plugin_test.go @@ -37,6 +37,9 @@ func TestManuallyProcessArgs(t *testing.T) { "--kubeconfig", "/home/foo", "--kube-context=test1", "--kube-context", "test1", + "--kube-as-user", "pikachu", + "--kube-as-group", "teatime", + "--kube-as-group", "admins", "-n=test2", "-n", "test2", "--namespace=test2", @@ -51,6 +54,9 @@ func TestManuallyProcessArgs(t *testing.T) { "--kubeconfig", "/home/foo", "--kube-context=test1", "--kube-context", "test1", + "--kube-as-user", "pikachu", + "--kube-as-group", "teatime", + "--kube-as-group", "admins", "-n=test2", "-n", "test2", "--namespace=test2", diff --git a/cmd/helm/root.go b/cmd/helm/root.go index 0135048a6..82c87bd7d 100644 --- a/cmd/helm/root.go +++ b/cmd/helm/root.go @@ -60,6 +60,8 @@ Environment variables: | $HELM_REPOSITORY_CONFIG | set the path to the repositories file. | | $KUBECONFIG | set an alternative Kubernetes configuration file (default "~/.kube/config") | | $HELM_KUBEAPISERVER | set the Kubernetes API Server Endpoint for authentication | +| $HELM_KUBEASGROUPS | set the Username to impersonate for the operation. | +| $HELM_KUBEASUSER | set the Groups to use for impoersonation using a comma-separated list. | | $HELM_KUBECONTEXT | set the name of the kubeconfig context. | | $HELM_KUBETOKEN | set the Bearer KubeToken used for authentication. | diff --git a/cmd/helm/testdata/output/env-comp.txt b/cmd/helm/testdata/output/env-comp.txt index c4b46ae6b..3739d8bc1 100644 --- a/cmd/helm/testdata/output/env-comp.txt +++ b/cmd/helm/testdata/output/env-comp.txt @@ -4,6 +4,8 @@ HELM_CONFIG_HOME HELM_DATA_HOME HELM_DEBUG HELM_KUBEAPISERVER +HELM_KUBEASGROUPS +HELM_KUBEASUSER HELM_KUBECONTEXT HELM_KUBETOKEN HELM_MAX_HISTORY diff --git a/pkg/cli/environment.go b/pkg/cli/environment.go index a9994f03d..4f3abc08b 100644 --- a/pkg/cli/environment.go +++ b/pkg/cli/environment.go @@ -26,6 +26,7 @@ import ( "fmt" "os" "strconv" + "strings" "github.com/spf13/pflag" "k8s.io/cli-runtime/pkg/genericclioptions" @@ -47,6 +48,10 @@ type EnvSettings struct { KubeContext string // Bearer KubeToken used for authentication KubeToken string + // Username to impersonate for the operation + KubeAsUser string + // Groups to impersonate for the operation, multiple groups parsed from a comma delimited list + KubeAsGroups []string // Kubernetes API Server Endpoint for authentication KubeAPIServer string // Debug indicates whether or not Helm is running in Debug mode. @@ -69,6 +74,8 @@ func New() *EnvSettings { MaxHistory: envIntOr("HELM_MAX_HISTORY", defaultMaxHistory), KubeContext: os.Getenv("HELM_KUBECONTEXT"), KubeToken: os.Getenv("HELM_KUBETOKEN"), + KubeAsUser: os.Getenv("HELM_KUBEASUSER"), + KubeAsGroups: envCSV("HELM_KUBEASGROUPS"), KubeAPIServer: os.Getenv("HELM_KUBEAPISERVER"), PluginsDirectory: envOr("HELM_PLUGINS", helmpath.DataPath("plugins")), RegistryConfig: envOr("HELM_REGISTRY_CONFIG", helmpath.ConfigPath("registry.json")), @@ -79,11 +86,13 @@ func New() *EnvSettings { // bind to kubernetes config flags env.config = &genericclioptions.ConfigFlags{ - Namespace: &env.namespace, - Context: &env.KubeContext, - BearerToken: &env.KubeToken, - APIServer: &env.KubeAPIServer, - KubeConfig: &env.KubeConfig, + Namespace: &env.namespace, + Context: &env.KubeContext, + BearerToken: &env.KubeToken, + APIServer: &env.KubeAPIServer, + KubeConfig: &env.KubeConfig, + Impersonate: &env.KubeAsUser, + ImpersonateGroup: &env.KubeAsGroups, } return env } @@ -94,6 +103,8 @@ func (s *EnvSettings) AddFlags(fs *pflag.FlagSet) { fs.StringVar(&s.KubeConfig, "kubeconfig", "", "path to the kubeconfig file") fs.StringVar(&s.KubeContext, "kube-context", s.KubeContext, "name of the kubeconfig context to use") fs.StringVar(&s.KubeToken, "kube-token", s.KubeToken, "bearer token used for authentication") + fs.StringVar(&s.KubeAsUser, "kube-as-user", s.KubeAsUser, "Username to impersonate for the operation") + fs.StringArrayVar(&s.KubeAsGroups, "kube-as-group", s.KubeAsGroups, "Group to impersonate for the operation, this flag can be repeated to specify multiple groups.") fs.StringVar(&s.KubeAPIServer, "kube-apiserver", s.KubeAPIServer, "the address and the port for the Kubernetes API server") fs.BoolVar(&s.Debug, "debug", s.Debug, "enable verbose output") fs.StringVar(&s.RegistryConfig, "registry-config", s.RegistryConfig, "path to the registry config file") @@ -120,6 +131,14 @@ func envIntOr(name string, def int) int { return ret } +func envCSV(name string) (ls []string) { + trimmed := strings.Trim(os.Getenv(name), ", ") + if trimmed != "" { + ls = strings.Split(trimmed, ",") + } + return +} + func (s *EnvSettings) EnvVars() map[string]string { envvars := map[string]string{ "HELM_BIN": os.Args[0], @@ -137,6 +156,8 @@ func (s *EnvSettings) EnvVars() map[string]string { // broken, these are populated from helm flags and not kubeconfig. "HELM_KUBECONTEXT": s.KubeContext, "HELM_KUBETOKEN": s.KubeToken, + "HELM_KUBEASUSER": s.KubeAsUser, + "HELM_KUBEASGROUPS": strings.Join(s.KubeAsGroups, ","), "HELM_KUBEAPISERVER": s.KubeAPIServer, } if s.KubeConfig != "" { diff --git a/pkg/cli/environment_test.go b/pkg/cli/environment_test.go index 3234a133b..ffdbce68b 100644 --- a/pkg/cli/environment_test.go +++ b/pkg/cli/environment_test.go @@ -18,6 +18,7 @@ package cli import ( "os" + "reflect" "strings" "testing" @@ -36,6 +37,8 @@ func TestEnvSettings(t *testing.T) { ns, kcontext string debug bool maxhistory int + kAsUser string + kAsGroups []string }{ { name: "defaults", @@ -44,25 +47,31 @@ func TestEnvSettings(t *testing.T) { }, { name: "with flags set", - args: "--debug --namespace=myns", + args: "--debug --namespace=myns --kube-as-user=poro --kube-as-group=admins --kube-as-group=teatime --kube-as-group=snackeaters", ns: "myns", debug: true, maxhistory: defaultMaxHistory, + kAsUser: "poro", + kAsGroups: []string{"admins", "teatime", "snackeaters"}, }, { name: "with envvars set", - envvars: map[string]string{"HELM_DEBUG": "1", "HELM_NAMESPACE": "yourns", "HELM_MAX_HISTORY": "5"}, + envvars: map[string]string{"HELM_DEBUG": "1", "HELM_NAMESPACE": "yourns", "HELM_KUBEASUSER": "pikachu", "HELM_KUBEASGROUPS": ",,,operators,snackeaters,partyanimals", "HELM_MAX_HISTORY": "5"}, ns: "yourns", maxhistory: 5, debug: true, + kAsUser: "pikachu", + kAsGroups: []string{"operators", "snackeaters", "partyanimals"}, }, { name: "with flags and envvars set", - args: "--debug --namespace=myns", - envvars: map[string]string{"HELM_DEBUG": "1", "HELM_NAMESPACE": "yourns"}, + args: "--debug --namespace=myns --kube-as-user=poro --kube-as-group=admins --kube-as-group=teatime --kube-as-group=snackeaters", + envvars: map[string]string{"HELM_DEBUG": "1", "HELM_NAMESPACE": "yourns", "HELM_KUBEASUSER": "pikachu", "HELM_KUBEASGROUPS": ",,,operators,snackeaters,partyanimals", "HELM_MAX_HISTORY": "5"}, ns: "myns", debug: true, - maxhistory: defaultMaxHistory, + maxhistory: 5, + kAsUser: "poro", + kAsGroups: []string{"admins", "teatime", "snackeaters"}, }, } @@ -92,6 +101,12 @@ func TestEnvSettings(t *testing.T) { if settings.MaxHistory != tt.maxhistory { t.Errorf("expected maxHistory %d, got %d", tt.maxhistory, settings.MaxHistory) } + if tt.kAsUser != settings.KubeAsUser { + t.Errorf("expected kAsUser %q, got %q", tt.kAsUser, settings.KubeAsUser) + } + if !reflect.DeepEqual(tt.kAsGroups, settings.KubeAsGroups) { + t.Errorf("expected kAsGroups %+v, got %+v", len(tt.kAsGroups), len(settings.KubeAsGroups)) + } }) } } From 35c5268d9dd98238319578c469072e80e4aeb1e7 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Wed, 9 Sep 2020 13:38:57 +0800 Subject: [PATCH 240/281] Use T.Cleanup() to cleanup temp dir helm-repotest For backward compatibility, as suggested by @bacongobbler, we introduce a new API NewTempServerWithCleanup Signed-off-by: Li Zhijian --- cmd/helm/dependency_build_test.go | 2 +- cmd/helm/dependency_update_test.go | 4 ++-- cmd/helm/pull_test.go | 2 +- cmd/helm/repo_add_test.go | 6 +++--- cmd/helm/repo_remove_test.go | 2 +- cmd/helm/repo_update_test.go | 2 +- cmd/helm/show_test.go | 2 +- pkg/downloader/chart_downloader_test.go | 6 +++--- pkg/downloader/manager_test.go | 4 ++-- pkg/repo/repotest/server.go | 14 ++++++++++++++ pkg/repo/repotest/server_test.go | 2 +- 11 files changed, 30 insertions(+), 16 deletions(-) diff --git a/cmd/helm/dependency_build_test.go b/cmd/helm/dependency_build_test.go index eeca12fa6..d6dfdabcb 100644 --- a/cmd/helm/dependency_build_test.go +++ b/cmd/helm/dependency_build_test.go @@ -28,7 +28,7 @@ import ( ) func TestDependencyBuildCmd(t *testing.T) { - srv, err := repotest.NewTempServer("testdata/testcharts/*.tgz") + srv, err := repotest.NewTempServerWithCleanup(t, "testdata/testcharts/*.tgz") defer srv.Stop() if err != nil { t.Fatal(err) diff --git a/cmd/helm/dependency_update_test.go b/cmd/helm/dependency_update_test.go index 1f9d55867..bf27c7b6c 100644 --- a/cmd/helm/dependency_update_test.go +++ b/cmd/helm/dependency_update_test.go @@ -33,7 +33,7 @@ import ( ) func TestDependencyUpdateCmd(t *testing.T) { - srv, err := repotest.NewTempServer("testdata/testcharts/*.tgz") + srv, err := repotest.NewTempServerWithCleanup(t, "testdata/testcharts/*.tgz") if err != nil { t.Fatal(err) } @@ -121,7 +121,7 @@ func TestDependencyUpdateCmd_DontDeleteOldChartsOnError(t *testing.T) { defer resetEnv()() defer ensure.HelmHome(t)() - srv, err := repotest.NewTempServer("testdata/testcharts/*.tgz") + srv, err := repotest.NewTempServerWithCleanup(t, "testdata/testcharts/*.tgz") if err != nil { t.Fatal(err) } diff --git a/cmd/helm/pull_test.go b/cmd/helm/pull_test.go index 3f769a1bc..1d439e873 100644 --- a/cmd/helm/pull_test.go +++ b/cmd/helm/pull_test.go @@ -26,7 +26,7 @@ import ( ) func TestPullCmd(t *testing.T) { - srv, err := repotest.NewTempServer("testdata/testcharts/*.tgz*") + srv, err := repotest.NewTempServerWithCleanup(t, "testdata/testcharts/*.tgz*") if err != nil { t.Fatal(err) } diff --git a/cmd/helm/repo_add_test.go b/cmd/helm/repo_add_test.go index 9ef64390b..19281f3aa 100644 --- a/cmd/helm/repo_add_test.go +++ b/cmd/helm/repo_add_test.go @@ -34,7 +34,7 @@ import ( ) func TestRepoAddCmd(t *testing.T) { - srv, err := repotest.NewTempServer("testdata/testserver/*.*") + srv, err := repotest.NewTempServerWithCleanup(t, "testdata/testserver/*.*") if err != nil { t.Fatal(err) } @@ -53,7 +53,7 @@ func TestRepoAddCmd(t *testing.T) { } func TestRepoAdd(t *testing.T) { - ts, err := repotest.NewTempServer("testdata/testserver/*.*") + ts, err := repotest.NewTempServerWithCleanup(t, "testdata/testserver/*.*") if err != nil { t.Fatal(err) } @@ -118,7 +118,7 @@ func TestRepoAddConcurrentDirNotExist(t *testing.T) { } func repoAddConcurrent(t *testing.T, testName, repoFile string) { - ts, err := repotest.NewTempServer("testdata/testserver/*.*") + ts, err := repotest.NewTempServerWithCleanup(t, "testdata/testserver/*.*") if err != nil { t.Fatal(err) } diff --git a/cmd/helm/repo_remove_test.go b/cmd/helm/repo_remove_test.go index 0ea1d63d2..cb5c6e9ab 100644 --- a/cmd/helm/repo_remove_test.go +++ b/cmd/helm/repo_remove_test.go @@ -30,7 +30,7 @@ import ( ) func TestRepoRemove(t *testing.T) { - ts, err := repotest.NewTempServer("testdata/testserver/*.*") + ts, err := repotest.NewTempServerWithCleanup(t, "testdata/testserver/*.*") if err != nil { t.Fatal(err) } diff --git a/cmd/helm/repo_update_test.go b/cmd/helm/repo_update_test.go index e5e4eb337..4b16a1ea7 100644 --- a/cmd/helm/repo_update_test.go +++ b/cmd/helm/repo_update_test.go @@ -75,7 +75,7 @@ func TestUpdateCharts(t *testing.T) { defer resetEnv()() defer ensure.HelmHome(t)() - ts, err := repotest.NewTempServer("testdata/testserver/*.*") + ts, err := repotest.NewTempServerWithCleanup(t, "testdata/testserver/*.*") if err != nil { t.Fatal(err) } diff --git a/cmd/helm/show_test.go b/cmd/helm/show_test.go index 2734faf5e..ac5294d3c 100644 --- a/cmd/helm/show_test.go +++ b/cmd/helm/show_test.go @@ -26,7 +26,7 @@ import ( ) func TestShowPreReleaseChart(t *testing.T) { - srv, err := repotest.NewTempServer("testdata/testcharts/*.tgz*") + srv, err := repotest.NewTempServerWithCleanup(t, "testdata/testcharts/*.tgz*") if err != nil { t.Fatal(err) } diff --git a/pkg/downloader/chart_downloader_test.go b/pkg/downloader/chart_downloader_test.go index abfb007ff..b456143a1 100644 --- a/pkg/downloader/chart_downloader_test.go +++ b/pkg/downloader/chart_downloader_test.go @@ -172,7 +172,7 @@ func TestIsTar(t *testing.T) { func TestDownloadTo(t *testing.T) { // Set up a fake repo with basic auth enabled - srv, err := repotest.NewTempServer("testdata/*.tgz*") + srv, err := repotest.NewTempServerWithCleanup(t, "testdata/*.tgz*") srv.Stop() if err != nil { t.Fatal(err) @@ -229,7 +229,7 @@ func TestDownloadTo(t *testing.T) { func TestDownloadTo_TLS(t *testing.T) { // Set up mock server w/ tls enabled - srv, err := repotest.NewTempServer("testdata/*.tgz*") + srv, err := repotest.NewTempServerWithCleanup(t, "testdata/*.tgz*") srv.Stop() if err != nil { t.Fatal(err) @@ -285,7 +285,7 @@ func TestDownloadTo_VerifyLater(t *testing.T) { dest := ensure.TempDir(t) // Set up a fake repo - srv, err := repotest.NewTempServer("testdata/*.tgz*") + srv, err := repotest.NewTempServerWithCleanup(t, "testdata/*.tgz*") if err != nil { t.Fatal(err) } diff --git a/pkg/downloader/manager_test.go b/pkg/downloader/manager_test.go index e60cf7624..9532cca7c 100644 --- a/pkg/downloader/manager_test.go +++ b/pkg/downloader/manager_test.go @@ -183,7 +183,7 @@ func TestGetRepoNames(t *testing.T) { func TestUpdateBeforeBuild(t *testing.T) { // Set up a fake repo - srv, err := repotest.NewTempServer("testdata/*.tgz*") + srv, err := repotest.NewTempServerWithCleanup(t, "testdata/*.tgz*") if err != nil { t.Fatal(err) } @@ -257,7 +257,7 @@ func TestUpdateBeforeBuild(t *testing.T) { // If each of these main fields (name, version, repository) is not supplied by dep param, default value will be used. func checkBuildWithOptionalFields(t *testing.T, chartName string, dep chart.Dependency) { // Set up a fake repo - srv, err := repotest.NewTempServer("testdata/*.tgz*") + srv, err := repotest.NewTempServerWithCleanup(t, "testdata/*.tgz*") if err != nil { t.Fatal(err) } diff --git a/pkg/repo/repotest/server.go b/pkg/repo/repotest/server.go index b18bce49c..9032b1e30 100644 --- a/pkg/repo/repotest/server.go +++ b/pkg/repo/repotest/server.go @@ -21,6 +21,7 @@ import ( "net/http/httptest" "os" "path/filepath" + "testing" "helm.sh/helm/v3/internal/tlsutil" @@ -29,6 +30,19 @@ import ( "helm.sh/helm/v3/pkg/repo" ) +// NewTempServerWithCleanup creates a server inside of a temp dir. +// +// If the passed in string is not "", it will be treated as a shell glob, and files +// will be copied from that path to the server's docroot. +// +// The caller is responsible for stopping the server. +// The temp dir will be removed by testing package automatically when test finished. +func NewTempServerWithCleanup(t *testing.T, glob string) (*Server, error) { + srv, err := NewTempServer(glob) + t.Cleanup(func() { os.RemoveAll(srv.docroot) }) + return srv, err +} + // NewTempServer creates a server inside of a temp dir. // // If the passed in string is not "", it will be treated as a shell glob, and files diff --git a/pkg/repo/repotest/server_test.go b/pkg/repo/repotest/server_test.go index ee62791af..6d71071da 100644 --- a/pkg/repo/repotest/server_test.go +++ b/pkg/repo/repotest/server_test.go @@ -99,7 +99,7 @@ func TestServer(t *testing.T) { func TestNewTempServer(t *testing.T) { defer ensure.HelmHome(t)() - srv, err := NewTempServer("testdata/examplechart-0.1.0.tgz") + srv, err := NewTempServerWithCleanup(t, "testdata/examplechart-0.1.0.tgz") if err != nil { t.Fatal(err) } From cccc2867ea8242de55e32910b1d6c2f252ed1af5 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Thu, 10 Sep 2020 09:31:03 +0800 Subject: [PATCH 241/281] mark NewTempServer as Deprecated Please use NewTempServerWithCleanup instead Signed-off-by: Li Zhijian --- pkg/repo/repotest/server.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/repo/repotest/server.go b/pkg/repo/repotest/server.go index 9032b1e30..270c8958a 100644 --- a/pkg/repo/repotest/server.go +++ b/pkg/repo/repotest/server.go @@ -50,6 +50,8 @@ func NewTempServerWithCleanup(t *testing.T, glob string) (*Server, error) { // // The caller is responsible for destroying the temp directory as well as stopping // the server. +// +// Deprecated: use NewTempServerWithCleanup func NewTempServer(glob string) (*Server, error) { tdir, err := ioutil.TempDir("", "helm-repotest-") if err != nil { From d9ad9153c8ddd910bdd3bb0d0cb6b0f693189c54 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Wed, 9 Sep 2020 13:45:37 +0800 Subject: [PATCH 242/281] Use RemoveAll to remove a non-empty directory Signed-off-by: Li Zhijian --- pkg/chart/loader/load_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/chart/loader/load_test.go b/pkg/chart/loader/load_test.go index 40b86dec2..16a94d4eb 100644 --- a/pkg/chart/loader/load_test.go +++ b/pkg/chart/loader/load_test.go @@ -310,7 +310,7 @@ func TestLoadInvalidArchive(t *testing.T) { if err != nil { t.Fatal(err) } - defer os.Remove(tmpdir) + defer os.RemoveAll(tmpdir) writeTar := func(filename, internalPath string, body []byte) { dest, err := os.Create(filename) From 4258e8664e6b8dfd1b9c3b8ca2115930b296c41c Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Wed, 9 Sep 2020 14:32:41 +0800 Subject: [PATCH 243/281] Use T.cleanup() to cleanup cmdtest_temp file Signed-off-by: Li Zhijian --- pkg/kube/client_test.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/pkg/kube/client_test.go b/pkg/kube/client_test.go index 568afa094..de5358aee 100644 --- a/pkg/kube/client_test.go +++ b/pkg/kube/client_test.go @@ -91,9 +91,12 @@ func newResponse(code int, obj runtime.Object) (*http.Response, error) { return &http.Response{StatusCode: code, Header: header, Body: body}, nil } -func newTestClient() *Client { +func newTestClient(t *testing.T) *Client { + testFactory := cmdtesting.NewTestFactory() + t.Cleanup(testFactory.Cleanup) + return &Client{ - Factory: cmdtesting.NewTestFactory().WithNamespace("default"), + Factory: testFactory.WithNamespace("default"), Log: nopLogger, } } @@ -107,7 +110,7 @@ func TestUpdate(t *testing.T) { var actions []string - c := newTestClient() + c := newTestClient(t) c.Factory.(*cmdtesting.TestFactory).UnstructuredClient = &fake.RESTClient{ NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { @@ -232,7 +235,7 @@ func TestBuild(t *testing.T) { }, } - c := newTestClient() + c := newTestClient(t) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Test for an invalid manifest @@ -279,7 +282,7 @@ func TestPerform(t *testing.T) { return nil } - c := newTestClient() + c := newTestClient(t) infos, err := c.Build(tt.reader, false) if err != nil && err.Error() != tt.errMessage { t.Errorf("Error while building manifests: %v", err) From 319240841575d97bbac0cc274c18fdb162b72919 Mon Sep 17 00:00:00 2001 From: Paul Brousseau Date: Sun, 13 Sep 2020 21:22:18 -0700 Subject: [PATCH 244/281] Fixing typo in engine comments Signed-off-by: Paul Brousseau --- pkg/engine/engine.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index 5aa0ed8ec..155d50a38 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -40,7 +40,7 @@ type Engine struct { Strict bool // In LintMode, some 'required' template values may be missing, so don't fail LintMode bool - // the rest config to connect to te kubernetes api + // the rest config to connect to the kubernetes api config *rest.Config } From 8b2cf17648889270ac5c5985f5bb9ef5e43d12d6 Mon Sep 17 00:00:00 2001 From: Ma Xinjian Date: Wed, 2 Sep 2020 13:43:35 +0800 Subject: [PATCH 245/281] Add support to install helm install the binary that was compiled by make build Signed-off-by: Ma Xinjian --- Makefile | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Makefile b/Makefile index 97f99fd86..85041f7f4 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,5 @@ BINDIR := $(CURDIR)/bin +INSTALL_PATH ?= /usr/local/bin DIST_DIRS := find * -type d -exec TARGETS := darwin/amd64 linux/amd64 linux/386 linux/arm linux/arm64 linux/ppc64le linux/s390x windows/amd64 TARGET_OBJS ?= darwin-amd64.tar.gz darwin-amd64.tar.gz.sha256 darwin-amd64.tar.gz.sha256sum linux-amd64.tar.gz linux-amd64.tar.gz.sha256 linux-amd64.tar.gz.sha256sum linux-386.tar.gz linux-386.tar.gz.sha256 linux-386.tar.gz.sha256sum linux-arm.tar.gz linux-arm.tar.gz.sha256 linux-arm.tar.gz.sha256sum linux-arm64.tar.gz linux-arm64.tar.gz.sha256 linux-arm64.tar.gz.sha256sum linux-ppc64le.tar.gz linux-ppc64le.tar.gz.sha256 linux-ppc64le.tar.gz.sha256sum linux-s390x.tar.gz linux-s390x.tar.gz.sha256 linux-s390x.tar.gz.sha256sum windows-amd64.zip windows-amd64.zip.sha256 windows-amd64.zip.sha256sum @@ -62,6 +63,13 @@ build: $(BINDIR)/$(BINNAME) $(BINDIR)/$(BINNAME): $(SRC) GO111MODULE=on go build $(GOFLAGS) -tags '$(TAGS)' -ldflags '$(LDFLAGS)' -o '$(BINDIR)'/$(BINNAME) ./cmd/helm +# ------------------------------------------------------------------------------ +# install + +.PHONY: install +install: build + @install "$(BINDIR)/$(BINNAME)" "$(INSTALL_PATH)/$(BINNAME)" + # ------------------------------------------------------------------------------ # test From f917c169d001a96fbdfd7943c441fb09509b9f7f Mon Sep 17 00:00:00 2001 From: Morten Linderud Date: Tue, 1 Sep 2020 12:08:41 +0200 Subject: [PATCH 246/281] Makefile: Fix LDFLAGS overriding When distributions build software it's desirable to have the ability to define own linker flags, or Go flags. As `-ldflags` defined in `go build` overrides `-ldflags` defined in the env variable `GOFLAGS`, there is a distinct need to be able to replace the default values with new ones or append to them. Fixes #8645 Signed-off-by: Morten Linderud --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 97f99fd86..0014efa5f 100644 --- a/Makefile +++ b/Makefile @@ -49,6 +49,7 @@ endif LDFLAGS += -X helm.sh/helm/v3/internal/version.metadata=${VERSION_METADATA} LDFLAGS += -X helm.sh/helm/v3/internal/version.gitCommit=${GIT_COMMIT} LDFLAGS += -X helm.sh/helm/v3/internal/version.gitTreeState=${GIT_DIRTY} +LDFLAGS += $(EXT_LDFLAGS) .PHONY: all all: build From 459dcd7f728b38ec44c72d79192ee93d6964d53d Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Mon, 14 Sep 2020 07:36:41 -0400 Subject: [PATCH 247/281] fix(comp): Disable file comp for output formats It does not make sense to suggest files to the user as output formats. Signed-off-by: Marc Khouzam --- cmd/helm/flags.go | 2 +- cmd/helm/testdata/output/output-comp.txt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/helm/flags.go b/cmd/helm/flags.go index 544fb7608..d1329c279 100644 --- a/cmd/helm/flags.go +++ b/cmd/helm/flags.go @@ -69,7 +69,7 @@ func bindOutputFlag(cmd *cobra.Command, varRef *output.Format) { formatNames = append(formatNames, format) } } - return formatNames, cobra.ShellCompDirectiveDefault + return formatNames, cobra.ShellCompDirectiveNoFileComp }) if err != nil { diff --git a/cmd/helm/testdata/output/output-comp.txt b/cmd/helm/testdata/output/output-comp.txt index be574756b..e7799a56b 100644 --- a/cmd/helm/testdata/output/output-comp.txt +++ b/cmd/helm/testdata/output/output-comp.txt @@ -1,5 +1,5 @@ table json yaml -:0 -Completion ended with directive: ShellCompDirectiveDefault +:4 +Completion ended with directive: ShellCompDirectiveNoFileComp From d1c8561be6e6b08bbf425dc79631149100a1e0db Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Wed, 16 Sep 2020 09:37:37 +0800 Subject: [PATCH 248/281] fix incorrect wildcard expand Previously, when there is no *.{gz,zip} files under _dist, the wildcard will be expanded to 2 strings '_dist/*.gz' and '_dist/*.zip'(see below). helm$ ls _dist helm$ make checksum for f in _dist/*.{gz,zip} ; do \ shasum -a 256 "${f}" | sed 's/_dist\///' > "${f}.sha256sum" ; \ shasum -a 256 "${f}" | awk '{print $1}' > "${f}.sha256" ; \ done shasum: _dist/*.gz: shasum: _dist/*.gz: shasum: _dist/*.zip: shasum: _dist/*.zip: helm$ ls _dist '*.gz.sha256' '*.gz.sha256sum' '*.zip.sha256' '*.zip.sha256sum' Signed-off-by: Li Zhijian --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index a9eac8033..79fc31976 100644 --- a/Makefile +++ b/Makefile @@ -165,7 +165,7 @@ fetch-dist: .PHONY: sign sign: - for f in _dist/*.{gz,zip,sha256,sha256sum} ; do \ + for f in $$(ls _dist/*.{gz,zip,sha256,sha256sum} 2>/dev/null) ; do \ gpg --armor --detach-sign $${f} ; \ done @@ -178,7 +178,7 @@ sign: # removed in Helm v4. .PHONY: checksum checksum: - for f in _dist/*.{gz,zip} ; do \ + for f in $$(ls _dist/*.{gz,zip} 2>/dev/null) ; do \ shasum -a 256 "$${f}" | sed 's/_dist\///' > "$${f}.sha256sum" ; \ shasum -a 256 "$${f}" | awk '{print $$1}' > "$${f}.sha256" ; \ done From 82398667dfe208407be9fe499ac96240aa8ce54b Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Tue, 8 Sep 2020 17:15:01 -0600 Subject: [PATCH 249/281] fix: check mode bits on kubeconfig file Signed-off-by: Matt Butcher --- cmd/helm/root.go | 3 ++ cmd/helm/root_unix.go | 58 ++++++++++++++++++++++++++++++++++++ cmd/helm/root_unix_test.go | 61 ++++++++++++++++++++++++++++++++++++++ cmd/helm/root_windows.go | 24 +++++++++++++++ 4 files changed, 146 insertions(+) create mode 100644 cmd/helm/root_unix.go create mode 100644 cmd/helm/root_unix_test.go create mode 100644 cmd/helm/root_windows.go diff --git a/cmd/helm/root.go b/cmd/helm/root.go index 82c87bd7d..91542bb7e 100644 --- a/cmd/helm/root.go +++ b/cmd/helm/root.go @@ -204,5 +204,8 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string // Find and add plugins loadPlugins(cmd, out) + // Check permissions on critical files + checkPerms(out) + return cmd, nil } diff --git a/cmd/helm/root_unix.go b/cmd/helm/root_unix.go new file mode 100644 index 000000000..210842b35 --- /dev/null +++ b/cmd/helm/root_unix.go @@ -0,0 +1,58 @@ +/* +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 main + +import ( + "fmt" + "io" + "os" + "os/user" + "path/filepath" +) + +func checkPerms(out io.Writer) { + // This function MUST NOT FAIL, as it is just a check for a common permissions problem. + // If for some reason the function hits a stopping condition, it may panic. But only if + // we can be sure that it is panicing because Helm cannot proceed. + + kc := settings.KubeConfig + if kc == "" { + kc = os.Getenv("KUBECONFIG") + } + if kc == "" { + u, err := user.Current() + if err != nil { + // No idea where to find KubeConfig, so return silently. Many helm commands + // can proceed happily without a KUBECONFIG, so this is not a fatal error. + return + } + kc = filepath.Join(u.HomeDir, ".kube", "config") + } + fi, err := os.Stat(kc) + if err != nil { + // DO NOT error if no KubeConfig is found. Not all commands require one. + return + } + + perm := fi.Mode().Perm() + if perm&0040 > 0 { + fmt.Fprintf(out, "WARNING: Kubernetes configuration file is group-readable. This is insecure. Location: %s\n", kc) + } + if perm&0004 > 0 { + fmt.Fprintf(out, "WARNING: Kubernetes configuration file is world-readable. This is insecure. Location: %s\n", kc) + } +} diff --git a/cmd/helm/root_unix_test.go b/cmd/helm/root_unix_test.go new file mode 100644 index 000000000..73f18ec28 --- /dev/null +++ b/cmd/helm/root_unix_test.go @@ -0,0 +1,61 @@ +/* +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 main + +import ( + "bytes" + "io/ioutil" + "os" + "path/filepath" + "strings" + "testing" +) + +func TestCheckPerms(t *testing.T) { + tdir, err := ioutil.TempDir("", "helmtest") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tdir) + tfile := filepath.Join(tdir, "testconfig") + fh, err := os.OpenFile(tfile, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0440) + if err != nil { + t.Errorf("Failed to create temp file: %s", err) + } + + tconfig := settings.KubeConfig + settings.KubeConfig = tfile + defer func() { settings.KubeConfig = tconfig }() + + var b bytes.Buffer + checkPerms(&b) + expectPrefix := "WARNING: Kubernetes configuration file is group-readable. This is insecure. Location:" + if !strings.HasPrefix(b.String(), expectPrefix) { + t.Errorf("Expected to get a warning for group perms. Got %q", b.String()) + } + + if err := fh.Chmod(0404); err != nil { + t.Errorf("Could not change mode on file: %s", err) + } + b.Reset() + checkPerms(&b) + expectPrefix = "WARNING: Kubernetes configuration file is world-readable. This is insecure. Location:" + if !strings.HasPrefix(b.String(), expectPrefix) { + t.Errorf("Expected to get a warning for world perms. Got %q", b.String()) + } + +} diff --git a/cmd/helm/root_windows.go b/cmd/helm/root_windows.go new file mode 100644 index 000000000..243780d40 --- /dev/null +++ b/cmd/helm/root_windows.go @@ -0,0 +1,24 @@ +/* +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 main + +import "io" + +func checkPerms(out io.Writer) { + // Not yet implemented on Windows. If you know how to do a comprehensive perms + // check on Windows, contributions welcomed! +} From c4ef82be13a0a3b6b42ce92bdd0357f4f6ac9e62 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Wed, 9 Sep 2020 16:33:18 -0600 Subject: [PATCH 250/281] validate the name passed in during helm create Signed-off-by: Matt Butcher --- pkg/chartutil/create.go | 27 +++++++++++++++++++++++++++ pkg/chartutil/create_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/pkg/chartutil/create.go b/pkg/chartutil/create.go index 6e382b961..d4b65e9b8 100644 --- a/pkg/chartutil/create.go +++ b/pkg/chartutil/create.go @@ -21,6 +21,7 @@ import ( "io/ioutil" "os" "path/filepath" + "regexp" "strings" "github.com/pkg/errors" @@ -30,6 +31,12 @@ import ( "helm.sh/helm/v3/pkg/chart/loader" ) +// chartName is a regular expression for testing the supplied name of a chart. +// This regular expression is probably stricter than it needs to be. We can relax it +// somewhat. Newline characters, as well as $, quotes, +, parens, and % are known to be +// problematic. +var chartName = regexp.MustCompile("^[a-zA-Z0-9._-]+$") + const ( // ChartfileName is the default Chart file name. ChartfileName = "Chart.yaml" @@ -63,6 +70,10 @@ const ( TestConnectionName = TemplatesTestsDir + sep + "test-connection.yaml" ) +// maxChartNameLength is lower than the limits we know of with certain file systems, +// and with certain Kubernetes fields. +const maxChartNameLength = 250 + const sep = string(filepath.Separator) const defaultChartfile = `apiVersion: v2 @@ -522,6 +533,12 @@ func CreateFrom(chartfile *chart.Metadata, dest, src string) error { // error. In such a case, this will attempt to clean up by removing the // new chart directory. func Create(name, dir string) (string, error) { + + // Sanity-check the name of a chart so user doesn't create one that causes problems. + if err := validateChartName(name); err != nil { + return "", err + } + path, err := filepath.Abs(dir) if err != nil { return path, err @@ -627,3 +644,13 @@ func writeFile(name string, content []byte) error { } return ioutil.WriteFile(name, content, 0644) } + +func validateChartName(name string) error { + if name == "" || len(name) > maxChartNameLength { + return fmt.Errorf("chart name must be between 1 and %d characters", maxChartNameLength) + } + if !chartName.MatchString(name) { + return fmt.Errorf("chart name must match the regular expression %q", chartName.String()) + } + return nil +} diff --git a/pkg/chartutil/create_test.go b/pkg/chartutil/create_test.go index a11c45140..f68ebbd63 100644 --- a/pkg/chartutil/create_test.go +++ b/pkg/chartutil/create_test.go @@ -117,3 +117,30 @@ func TestCreateFrom(t *testing.T) { } } } + +func TestValidateChartName(t *testing.T) { + for name, shouldPass := range map[string]bool{ + "": false, + "abcdefghijklmnopqrstuvwxyz-_.": true, + "ABCDEFGHIJKLMNOPQRSTUVWXYZ-_.": true, + "$hello": false, + "Hellô": false, + "he%%o": false, + "he\nllo": false, + + "abcdefghijklmnopqrstuvwxyz-_." + + "abcdefghijklmnopqrstuvwxyz-_." + + "abcdefghijklmnopqrstuvwxyz-_." + + "abcdefghijklmnopqrstuvwxyz-_." + + "abcdefghijklmnopqrstuvwxyz-_." + + "abcdefghijklmnopqrstuvwxyz-_." + + "abcdefghijklmnopqrstuvwxyz-_." + + "abcdefghijklmnopqrstuvwxyz-_." + + "abcdefghijklmnopqrstuvwxyz-_." + + "ABCDEFGHIJKLMNOPQRSTUVWXYZ-_.": false, + } { + if err := validateChartName(name); (err != nil) == shouldPass { + t.Errorf("test for %q failed", name) + } + } +} From ed5fba5142fa5a2366df143616e9161ff866a53d Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Wed, 9 Sep 2020 13:30:30 -0600 Subject: [PATCH 251/281] refactor the release name validation to be consistent across Helm Signed-off-by: Matt Butcher --- pkg/action/action.go | 7 +- pkg/action/action_test.go | 37 ----------- pkg/action/history.go | 3 +- pkg/action/release_testing.go | 3 +- pkg/action/rollback.go | 3 +- pkg/action/uninstall.go | 3 +- pkg/action/upgrade.go | 15 +---- pkg/chartutil/validate_name.go | 99 +++++++++++++++++++++++++++++ pkg/chartutil/validate_name_test.go | 91 ++++++++++++++++++++++++++ pkg/lint/rules/template.go | 14 +--- 10 files changed, 206 insertions(+), 69 deletions(-) create mode 100644 pkg/chartutil/validate_name.go create mode 100644 pkg/chartutil/validate_name_test.go diff --git a/pkg/action/action.go b/pkg/action/action.go index 071db709b..79bb4f638 100644 --- a/pkg/action/action.go +++ b/pkg/action/action.go @@ -58,14 +58,15 @@ var ( errMissingRelease = errors.New("no release provided") // errInvalidRevision indicates that an invalid release revision number was provided. errInvalidRevision = errors.New("invalid release revision") - // errInvalidName indicates that an invalid release name was provided - errInvalidName = errors.New("invalid release name, must match regex ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])+$ and the length must not longer than 53") // errPending indicates that another instance of Helm is already applying an operation on a release. errPending = errors.New("another operation (install/upgrade/rollback) is in progress") ) // ValidName is a regular expression for resource names. // +// DEPRECATED: This will be removed in Helm 4, and is no longer used here. See +// pkg/chartutil.ValidateName for the replacement. +// // According to the Kubernetes help text, the regular expression it uses is: // // [a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)* @@ -294,7 +295,7 @@ func (c *Configuration) Now() time.Time { } func (c *Configuration) releaseContent(name string, version int) (*release.Release, error) { - if err := validateReleaseName(name); err != nil { + if err := chartutil.ValidateReleaseName(name); err != nil { return nil, errors.Errorf("releaseContent: Release name is invalid: %s", name) } diff --git a/pkg/action/action_test.go b/pkg/action/action_test.go index c05b4403d..fedf260fb 100644 --- a/pkg/action/action_test.go +++ b/pkg/action/action_test.go @@ -319,40 +319,3 @@ func TestGetVersionSet(t *testing.T) { t.Error("Non-existent version is reported found.") } } - -// TestValidName is a regression test for ValidName -// -// Kubernetes has strict naming conventions for resource names. This test represents -// those conventions. -// -// See https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names -// -// NOTE: At the time of this writing, the docs above say that names cannot begin with -// digits. However, `kubectl`'s regular expression explicit allows this, and -// Kubernetes (at least as of 1.18) also accepts resources whose names begin with digits. -func TestValidName(t *testing.T) { - names := map[string]bool{ - "": false, - "foo": true, - "foo.bar1234baz.seventyone": true, - "FOO": false, - "123baz": true, - "foo.BAR.baz": false, - "one-two": true, - "-two": false, - "one_two": false, - "a..b": false, - "%^&#$%*@^*@&#^": false, - "example:com": false, - "example%%com": false, - } - for input, expectPass := range names { - if ValidName.MatchString(input) != expectPass { - st := "fail" - if expectPass { - st = "succeed" - } - t.Errorf("Expected %q to %s", input, st) - } - } -} diff --git a/pkg/action/history.go b/pkg/action/history.go index a592745e9..f4043609c 100644 --- a/pkg/action/history.go +++ b/pkg/action/history.go @@ -19,6 +19,7 @@ package action import ( "github.com/pkg/errors" + "helm.sh/helm/v3/pkg/chartutil" "helm.sh/helm/v3/pkg/release" ) @@ -45,7 +46,7 @@ func (h *History) Run(name string) ([]*release.Release, error) { return nil, err } - if err := validateReleaseName(name); err != nil { + if err := chartutil.ValidateReleaseName(name); err != nil { return nil, errors.Errorf("release name is invalid: %s", name) } diff --git a/pkg/action/release_testing.go b/pkg/action/release_testing.go index 795c3c747..2f6f5cfce 100644 --- a/pkg/action/release_testing.go +++ b/pkg/action/release_testing.go @@ -25,6 +25,7 @@ import ( "github.com/pkg/errors" v1 "k8s.io/api/core/v1" + "helm.sh/helm/v3/pkg/chartutil" "helm.sh/helm/v3/pkg/release" ) @@ -51,7 +52,7 @@ func (r *ReleaseTesting) Run(name string) (*release.Release, error) { return nil, err } - if err := validateReleaseName(name); err != nil { + if err := chartutil.ValidateReleaseName(name); err != nil { return nil, errors.Errorf("releaseTest: Release name is invalid: %s", name) } diff --git a/pkg/action/rollback.go b/pkg/action/rollback.go index 8773b6271..542acefae 100644 --- a/pkg/action/rollback.go +++ b/pkg/action/rollback.go @@ -24,6 +24,7 @@ import ( "github.com/pkg/errors" + "helm.sh/helm/v3/pkg/chartutil" "helm.sh/helm/v3/pkg/release" helmtime "helm.sh/helm/v3/pkg/time" ) @@ -90,7 +91,7 @@ func (r *Rollback) Run(name string) error { // prepareRollback finds the previous release and prepares a new release object with // the previous release's configuration func (r *Rollback) prepareRollback(name string) (*release.Release, *release.Release, error) { - if err := validateReleaseName(name); err != nil { + if err := chartutil.ValidateReleaseName(name); err != nil { return nil, nil, errors.Errorf("prepareRollback: Release name is invalid: %s", name) } diff --git a/pkg/action/uninstall.go b/pkg/action/uninstall.go index a51a283d6..c466c6ee2 100644 --- a/pkg/action/uninstall.go +++ b/pkg/action/uninstall.go @@ -22,6 +22,7 @@ import ( "github.com/pkg/errors" + "helm.sh/helm/v3/pkg/chartutil" "helm.sh/helm/v3/pkg/release" "helm.sh/helm/v3/pkg/releaseutil" helmtime "helm.sh/helm/v3/pkg/time" @@ -62,7 +63,7 @@ func (u *Uninstall) Run(name string) (*release.UninstallReleaseResponse, error) return &release.UninstallReleaseResponse{Release: r}, nil } - if err := validateReleaseName(name); err != nil { + if err := chartutil.ValidateReleaseName(name); err != nil { return nil, errors.Errorf("uninstall: Release name is invalid: %s", name) } diff --git a/pkg/action/upgrade.go b/pkg/action/upgrade.go index b707e7e69..c439af79d 100644 --- a/pkg/action/upgrade.go +++ b/pkg/action/upgrade.go @@ -115,7 +115,7 @@ func (u *Upgrade) Run(name string, chart *chart.Chart, vals map[string]interface // the user doesn't have to specify both u.Wait = u.Wait || u.Atomic - if err := validateReleaseName(name); err != nil { + if err := chartutil.ValidateReleaseName(name); err != nil { return nil, errors.Errorf("release name is invalid: %s", name) } u.cfg.Log("preparing upgrade for %s", name) @@ -142,19 +142,6 @@ func (u *Upgrade) Run(name string, chart *chart.Chart, vals map[string]interface return res, nil } -func validateReleaseName(releaseName string) error { - if releaseName == "" { - return errMissingRelease - } - - // Check length first, since that is a less expensive operation. - if len(releaseName) > releaseNameMaxLen || !ValidName.MatchString(releaseName) { - return errInvalidName - } - - return nil -} - // prepareUpgrade builds an upgraded release for an upgrade operation. func (u *Upgrade) prepareUpgrade(name string, chart *chart.Chart, vals map[string]interface{}) (*release.Release, *release.Release, error) { if chart == nil { diff --git a/pkg/chartutil/validate_name.go b/pkg/chartutil/validate_name.go new file mode 100644 index 000000000..22132c80e --- /dev/null +++ b/pkg/chartutil/validate_name.go @@ -0,0 +1,99 @@ +/* +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 chartutil + +import ( + "regexp" + + "github.com/pkg/errors" +) + +// validName is a regular expression for resource names. +// +// According to the Kubernetes help text, the regular expression it uses is: +// +// [a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)* +// +// This follows the above regular expression (but requires a full string match, not partial). +// +// The Kubernetes documentation is here, though it is not entirely correct: +// https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names +var validName = regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`) + +var ( + // errMissingName indicates that a release (name) was not provided. + errMissingName = errors.New("no name provided") + + // errInvalidName indicates that an invalid release name was provided + errInvalidName = errors.New("invalid release name, must match regex ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])+$ and the length must not longer than 53") + + // errInvalidKubernetesName indicates that the name does not meet the Kubernetes + // restrictions on metadata names. + errInvalidKubernetesName = errors.New("invalid metadata name, must match regex ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])+$ and the length must not longer than 253") +) + +const ( + // maxNameLen is the maximum length Helm allows for a release name + maxReleaseNameLen = 53 + // maxMetadataNameLen is the maximum length Kubernetes allows for any name. + maxMetadataNameLen = 253 +) + +// ValidateReleaseName performs checks for an entry for a Helm release name +// +// For Helm to allow a name, it must be below a certain character count (53) and also match +// a reguar expression. +// +// According to the Kubernetes help text, the regular expression it uses is: +// +// [a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)* +// +// This follows the above regular expression (but requires a full string match, not partial). +// +// The Kubernetes documentation is here, though it is not entirely correct: +// https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names +func ValidateReleaseName(name string) error { + // This case is preserved for backwards compatibility + if name == "" { + return errMissingName + + } + if len(name) > maxReleaseNameLen || !validName.MatchString(name) { + return errInvalidName + } + return nil +} + +// ValidateMetadataName validates the name field of a Kubernetes metadata object. +// +// Empty strings, strings longer than 253 chars, or strings that don't match the regexp +// will fail. +// +// According to the Kubernetes help text, the regular expression it uses is: +// +// [a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)* +// +// This follows the above regular expression (but requires a full string match, not partial). +// +// The Kubernetes documentation is here, though it is not entirely correct: +// https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names +func ValidateMetadataName(name string) error { + if name == "" || len(name) > maxMetadataNameLen || !validName.MatchString(name) { + return errInvalidKubernetesName + } + return nil +} diff --git a/pkg/chartutil/validate_name_test.go b/pkg/chartutil/validate_name_test.go new file mode 100644 index 000000000..5f0792f94 --- /dev/null +++ b/pkg/chartutil/validate_name_test.go @@ -0,0 +1,91 @@ +/* +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 chartutil + +import "testing" + +// TestValidateName is a regression test for ValidateName +// +// Kubernetes has strict naming conventions for resource names. This test represents +// those conventions. +// +// See https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names +// +// NOTE: At the time of this writing, the docs above say that names cannot begin with +// digits. However, `kubectl`'s regular expression explicit allows this, and +// Kubernetes (at least as of 1.18) also accepts resources whose names begin with digits. +func TestValidateReleaseName(t *testing.T) { + names := map[string]bool{ + "": false, + "foo": true, + "foo.bar1234baz.seventyone": true, + "FOO": false, + "123baz": true, + "foo.BAR.baz": false, + "one-two": true, + "-two": false, + "one_two": false, + "a..b": false, + "%^&#$%*@^*@&#^": false, + "example:com": false, + "example%%com": false, + "a1111111111111111111111111111111111111111111111111111111111z": false, + } + for input, expectPass := range names { + if err := ValidateReleaseName(input); (err == nil) != expectPass { + st := "fail" + if expectPass { + st = "succeed" + } + t.Errorf("Expected %q to %s", input, st) + } + } +} + +func TestValidateMetadataName(t *testing.T) { + names := map[string]bool{ + "": false, + "foo": true, + "foo.bar1234baz.seventyone": true, + "FOO": false, + "123baz": true, + "foo.BAR.baz": false, + "one-two": true, + "-two": false, + "one_two": false, + "a..b": false, + "%^&#$%*@^*@&#^": false, + "example:com": false, + "example%%com": false, + "a1111111111111111111111111111111111111111111111111111111111z": true, + "a1111111111111111111111111111111111111111111111111111111111z" + + "a1111111111111111111111111111111111111111111111111111111111z" + + "a1111111111111111111111111111111111111111111111111111111111z" + + "a1111111111111111111111111111111111111111111111111111111111z" + + "a1111111111111111111111111111111111111111111111111111111111z" + + "a1111111111111111111111111111111111111111111111111111111111z": false, + } + for input, expectPass := range names { + if err := ValidateMetadataName(input); (err == nil) != expectPass { + st := "fail" + if expectPass { + st = "succeed" + } + t.Errorf("Expected %q to %s", input, st) + } + } +} diff --git a/pkg/lint/rules/template.go b/pkg/lint/rules/template.go index 73d645264..5de0819c4 100644 --- a/pkg/lint/rules/template.go +++ b/pkg/lint/rules/template.go @@ -40,14 +40,6 @@ var ( releaseTimeSearch = regexp.MustCompile(`\.Release\.Time`) ) -// validName is a regular expression for names. -// -// This is different than action.ValidName. It conforms to the regular expression -// `kubectl` says it uses, plus it disallows empty names. -// -// For details, see https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names -var validName = regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`) - // Templates lints the templates in the Linter. func Templates(linter *support.Linter, values map[string]interface{}, namespace string, strict bool) { fpath := "templates/" @@ -199,10 +191,10 @@ func validateMetadataName(obj *K8sYamlStruct) error { } // This will return an error if the characters do not abide by the standard OR if the // name is left empty. - if validName.MatchString(obj.Metadata.Name) { - return nil + if err := chartutil.ValidateMetadataName(obj.Metadata.Name); err != nil { + return errors.Wrapf(err, "object name does not conform to Kubernetes naming requirements: %q", obj.Metadata.Name) } - return fmt.Errorf("object name does not conform to Kubernetes naming requirements: %q", obj.Metadata.Name) + return nil } func validateNoCRDHooks(manifest []byte) error { From 106f1fb45c93fe862ac86d9b774e2de8b1dd314c Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Wed, 9 Sep 2020 16:47:59 -0600 Subject: [PATCH 252/281] fixed bug that caused helm create to not overwrite modified files Signed-off-by: Matt Butcher --- cmd/helm/create.go | 1 + pkg/chartutil/create.go | 11 ++++++++-- pkg/chartutil/create_test.go | 39 ++++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/cmd/helm/create.go b/cmd/helm/create.go index 21a7e026c..fe5cc540a 100644 --- a/cmd/helm/create.go +++ b/cmd/helm/create.go @@ -107,6 +107,7 @@ func (o *createOptions) run(out io.Writer) error { return chartutil.CreateFrom(cfile, filepath.Dir(o.name), lstarter) } + chartutil.Stderr = out _, err := chartutil.Create(chartname, filepath.Dir(o.name)) return err } diff --git a/pkg/chartutil/create.go b/pkg/chartutil/create.go index 6e382b961..4ae5c7f3c 100644 --- a/pkg/chartutil/create.go +++ b/pkg/chartutil/create.go @@ -18,6 +18,7 @@ package chartutil import ( "fmt" + "io" "io/ioutil" "os" "path/filepath" @@ -468,6 +469,12 @@ spec: restartPolicy: Never ` +// Stderr is an io.Writer to which error messages can be written +// +// In Helm 4, this will be replaced. It is needed in Helm 3 to preserve API backward +// compatibility. +var Stderr io.Writer = os.Stderr + // CreateFrom creates a new chart, but scaffolds it from the src chart. func CreateFrom(chartfile *chart.Metadata, dest, src string) error { schart, err := loader.Load(src) @@ -601,8 +608,8 @@ func Create(name, dir string) (string, error) { for _, file := range files { if _, err := os.Stat(file.path); err == nil { - // File exists and is okay. Skip it. - continue + // There is no handle to a preferred output stream here. + fmt.Fprintf(Stderr, "WARNING: File %q already exists. Overwriting.\n", file.path) } if err := writeFile(file.path, file.content); err != nil { return cdir, err diff --git a/pkg/chartutil/create_test.go b/pkg/chartutil/create_test.go index a11c45140..49dcde633 100644 --- a/pkg/chartutil/create_test.go +++ b/pkg/chartutil/create_test.go @@ -117,3 +117,42 @@ func TestCreateFrom(t *testing.T) { } } } + +// TestCreate_Overwrite is a regression test for making sure that files are overwritten. +func TestCreate_Overwrite(t *testing.T) { + tdir, err := ioutil.TempDir("", "helm-") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tdir) + + var errlog bytes.Buffer + + if _, err := Create("foo", tdir); err != nil { + t.Fatal(err) + } + + dir := filepath.Join(tdir, "foo") + + tplname := filepath.Join(dir, "templates/hpa.yaml") + writeFile(tplname, []byte("FOO")) + + // Now re-run the create + Stderr = &errlog + if _, err := Create("foo", tdir); err != nil { + t.Fatal(err) + } + + data, err := ioutil.ReadFile(tplname) + if err != nil { + t.Fatal(err) + } + + if string(data) == "FOO" { + t.Fatal("File that should have been modified was not.") + } + + if errlog.Len() == 0 { + t.Errorf("Expected warnings about overwriting files.") + } +} From 40b78002873d525a31c5dec75c8607be67327360 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Fri, 11 Sep 2020 16:23:34 -0600 Subject: [PATCH 253/281] handle case where dependency name collisions break dependency resolution Signed-off-by: Matt Butcher --- pkg/action/dependency.go | 87 ++++++++++++++++++++-------- pkg/action/dependency_test.go | 103 ++++++++++++++++++++++++++++++++++ 2 files changed, 167 insertions(+), 23 deletions(-) diff --git a/pkg/action/dependency.go b/pkg/action/dependency.go index 4a4b8ebad..4c80d0159 100644 --- a/pkg/action/dependency.go +++ b/pkg/action/dependency.go @@ -21,6 +21,7 @@ import ( "io" "os" "path/filepath" + "strings" "github.com/Masterminds/semver/v3" "github.com/gosuri/uitable" @@ -61,6 +62,7 @@ func (d *Dependency) List(chartpath string, out io.Writer) error { return nil } +// dependecyStatus returns a string describing the status of a dependency viz a viz the parent chart. func (d *Dependency) dependencyStatus(chartpath string, dep *chart.Dependency, parent *chart.Chart) string { filename := fmt.Sprintf("%s-%s.tgz", dep.Name, "*") @@ -75,35 +77,40 @@ func (d *Dependency) dependencyStatus(chartpath string, dep *chart.Dependency, p case err != nil: return "bad pattern" case len(archives) > 1: - return "too many matches" - case len(archives) == 1: - archive := archives[0] - if _, err := os.Stat(archive); err == nil { - c, err := loader.Load(archive) - if err != nil { - return "corrupt" + // See if the second part is a SemVer + found := []string{} + for _, arc := range archives { + // we need to trip the prefix dirs and the extension off. + filename = strings.TrimSuffix(filepath.Base(arc), ".tgz") + maybeVersion := strings.TrimPrefix(filename, fmt.Sprintf("%s-", dep.Name)) + + if _, err := semver.StrictNewVersion(maybeVersion); err == nil { + // If the version parsed without an error, it is possibly a valid + // version. + found = append(found, arc) } - if c.Name() != dep.Name { - return "misnamed" + } + + if l := len(found); l == 1 { + // If we get here, we do the same thing as in len(archives) == 1. + if r := statArchiveForStatus(found[0], dep); r != "" { + return r } - if c.Metadata.Version != dep.Version { - constraint, err := semver.NewConstraint(dep.Version) - if err != nil { - return "invalid version" - } + // Fall through and look for directories + } else if l > 1 { + return "too many matches" + } - v, err := semver.NewVersion(c.Metadata.Version) - if err != nil { - return "invalid version" - } + // The sanest thing to do here is to fall through and see if we have any directory + // matches. - if !constraint.Check(v) { - return "wrong version" - } - } - return "ok" + case len(archives) == 1: + archive := archives[0] + if r := statArchiveForStatus(archive, dep); r != "" { + return r } + } // End unnecessary code. @@ -137,6 +144,40 @@ func (d *Dependency) dependencyStatus(chartpath string, dep *chart.Dependency, p return "unpacked" } +// stat an archive and return a message if the stat is successful +// +// This is a refactor of the code originally in dependencyStatus. It is here to +// support legacy behavior, and should be removed in Helm 4. +func statArchiveForStatus(archive string, dep *chart.Dependency) string { + if _, err := os.Stat(archive); err == nil { + c, err := loader.Load(archive) + if err != nil { + return "corrupt" + } + if c.Name() != dep.Name { + return "misnamed" + } + + if c.Metadata.Version != dep.Version { + constraint, err := semver.NewConstraint(dep.Version) + if err != nil { + return "invalid version" + } + + v, err := semver.NewVersion(c.Metadata.Version) + if err != nil { + return "invalid version" + } + + if !constraint.Check(v) { + return "wrong version" + } + } + return "ok" + } + return "" +} + // printDependencies prints all of the dependencies in the yaml file. func (d *Dependency) printDependencies(chartpath string, out io.Writer, c *chart.Chart) { table := uitable.New() diff --git a/pkg/action/dependency_test.go b/pkg/action/dependency_test.go index 4f3cb69a5..b5032a377 100644 --- a/pkg/action/dependency_test.go +++ b/pkg/action/dependency_test.go @@ -18,9 +18,16 @@ package action import ( "bytes" + "io/ioutil" + "os" + "path/filepath" "testing" + "github.com/stretchr/testify/assert" + "helm.sh/helm/v3/internal/test" + "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/chartutil" ) func TestList(t *testing.T) { @@ -56,3 +63,99 @@ func TestList(t *testing.T) { test.AssertGoldenBytes(t, buf.Bytes(), tcase.golden) } } + +// TestDependencyStatus_Dashes is a regression test to make sure that dashes in +// chart names do not cause resolution problems. +func TestDependencyStatus_Dashes(t *testing.T) { + // Make a temp dir + dir, err := ioutil.TempDir("", "helmtest-") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + chartpath := filepath.Join(dir, "charts") + if err := os.MkdirAll(chartpath, 0700); err != nil { + t.Fatal(err) + } + + // Add some fake charts + first := buildChart(withName("first-chart")) + _, err = chartutil.Save(first, chartpath) + if err != nil { + t.Fatal(err) + } + + second := buildChart(withName("first-chart-second-chart")) + _, err = chartutil.Save(second, chartpath) + if err != nil { + t.Fatal(err) + } + + dep := &chart.Dependency{ + Name: "first-chart", + Version: "0.1.0", + } + + // Now try to get the deps + stat := NewDependency().dependencyStatus(dir, dep, first) + if stat != "ok" { + t.Errorf("Unexpected status: %q", stat) + } +} + +func TestStatArchiveForStatus(t *testing.T) { + // Make a temp dir + dir, err := ioutil.TempDir("", "helmtest-") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + chartpath := filepath.Join(dir, "charts") + if err := os.MkdirAll(chartpath, 0700); err != nil { + t.Fatal(err) + } + + // unsaved chart + lilith := buildChart(withName("lilith")) + + // dep referring to chart + dep := &chart.Dependency{ + Name: "lilith", + Version: "1.2.3", + } + + is := assert.New(t) + + lilithpath := filepath.Join(chartpath, "lilith-1.2.3.tgz") + is.Empty(statArchiveForStatus(lilithpath, dep)) + + // save the chart (version 0.1.0, because that is the default) + where, err := chartutil.Save(lilith, chartpath) + is.NoError(err) + + // Should get "wrong version" because we asked for 1.2.3 and got 0.1.0 + is.Equal("wrong version", statArchiveForStatus(where, dep)) + + // Break version on dep + dep = &chart.Dependency{ + Name: "lilith", + Version: "1.2.3.4.5", + } + is.Equal("invalid version", statArchiveForStatus(where, dep)) + + // Break the name + dep = &chart.Dependency{ + Name: "lilith2", + Version: "1.2.3", + } + is.Equal("misnamed", statArchiveForStatus(where, dep)) + + // Now create the right version + dep = &chart.Dependency{ + Name: "lilith", + Version: "0.1.0", + } + is.Equal("ok", statArchiveForStatus(where, dep)) +} From 882eeac7271858124a3cecbe22c5d7d61560714f Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Fri, 11 Sep 2020 16:32:45 -0600 Subject: [PATCH 254/281] replace --no-update with --force-update and invert default. BREAKING. Signed-off-by: Matt Butcher --- cmd/helm/repo_add.go | 19 ++++++++++++------- cmd/helm/repo_add_test.go | 20 +++++++++++--------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/cmd/helm/repo_add.go b/cmd/helm/repo_add.go index 3eeb342f5..1c2162bfa 100644 --- a/cmd/helm/repo_add.go +++ b/cmd/helm/repo_add.go @@ -38,11 +38,11 @@ import ( ) type repoAddOptions struct { - name string - url string - username string - password string - noUpdate bool + name string + url string + username string + password string + forceUpdate bool certFile string keyFile string @@ -51,6 +51,9 @@ type repoAddOptions struct { repoFile string repoCache string + + // Deprecated, but cannot be removed until Helm 4 + deprecatedNoUpdate bool } func newRepoAddCmd(out io.Writer) *cobra.Command { @@ -74,7 +77,8 @@ func newRepoAddCmd(out io.Writer) *cobra.Command { f := cmd.Flags() f.StringVar(&o.username, "username", "", "chart repository username") f.StringVar(&o.password, "password", "", "chart repository password") - f.BoolVar(&o.noUpdate, "no-update", false, "raise error if repo is already registered") + f.BoolVar(&o.forceUpdate, "force-update", false, "replace (overwrite) the repo if it already exists") + f.BoolVar(&o.deprecatedNoUpdate, "no-update", false, "Ignored. Formerly, it would disabled forced updates. It is deprecated by force-update.") f.StringVar(&o.certFile, "cert-file", "", "identify HTTPS client using this SSL certificate file") f.StringVar(&o.keyFile, "key-file", "", "identify HTTPS client using this SSL key file") f.StringVar(&o.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle") @@ -112,7 +116,8 @@ func (o *repoAddOptions) run(out io.Writer) error { return err } - if o.noUpdate && f.Has(o.name) { + // If the repo exists and --force-update was not specified, error out. + if !o.forceUpdate && f.Has(o.name) { return errors.Errorf("repository name (%s) already exists, please specify a different name", o.name) } diff --git a/cmd/helm/repo_add_test.go b/cmd/helm/repo_add_test.go index 9ef64390b..05fa084df 100644 --- a/cmd/helm/repo_add_test.go +++ b/cmd/helm/repo_add_test.go @@ -65,10 +65,11 @@ func TestRepoAdd(t *testing.T) { const testRepoName = "test-name" o := &repoAddOptions{ - name: testRepoName, - url: ts.URL(), - noUpdate: true, - repoFile: repoFile, + name: testRepoName, + url: ts.URL(), + forceUpdate: false, + deprecatedNoUpdate: true, + repoFile: repoFile, } os.Setenv(xdg.CacheHomeEnvVar, rootDir) @@ -94,7 +95,7 @@ func TestRepoAdd(t *testing.T) { t.Errorf("Error cache charts file was not created for repository %s", testRepoName) } - o.noUpdate = false + o.forceUpdate = true if err := o.run(ioutil.Discard); err != nil { t.Errorf("Repository was not updated: %s", err) @@ -130,10 +131,11 @@ func repoAddConcurrent(t *testing.T, testName, repoFile string) { go func(name string) { defer wg.Done() o := &repoAddOptions{ - name: name, - url: ts.URL(), - noUpdate: true, - repoFile: repoFile, + name: name, + url: ts.URL(), + deprecatedNoUpdate: true, + forceUpdate: false, + repoFile: repoFile, } if err := o.run(ioutil.Discard); err != nil { t.Error(err) From e2da16f5146e2709211f116cb81dd8f9c9a62fd5 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Thu, 10 Sep 2020 16:45:40 -0600 Subject: [PATCH 255/281] improve the HTTP detection for tar archives Signed-off-by: Matt Butcher --- pkg/plugin/installer/http_installer.go | 12 +++++ pkg/plugin/installer/http_installer_test.go | 52 +++++++++++++++++++-- pkg/plugin/installer/installer.go | 22 ++++++++- pkg/plugin/installer/installer_test.go | 40 ++++++++++++++++ 4 files changed, 122 insertions(+), 4 deletions(-) create mode 100644 pkg/plugin/installer/installer_test.go diff --git a/pkg/plugin/installer/http_installer.go b/pkg/plugin/installer/http_installer.go index 28e50b72b..bcbcbde93 100644 --- a/pkg/plugin/installer/http_installer.go +++ b/pkg/plugin/installer/http_installer.go @@ -59,6 +59,18 @@ var Extractors = map[string]Extractor{ ".tgz": &TarGzExtractor{}, } +// Convert a media type to an extractor extension. +// +// This should be refactored in Helm 4, combined with the extension-based mechanism. +func mediaTypeToExtension(mt string) (string, bool) { + switch strings.ToLower(mt) { + case "application/gzip", "application/x-gzip", "application/x-tgz", "application/x-gtar": + return ".tgz", true + default: + return "", false + } +} + // NewExtractor creates a new extractor matching the source file name func NewExtractor(source string) (Extractor, error) { for suffix, extractor := range Extractors { diff --git a/pkg/plugin/installer/http_installer_test.go b/pkg/plugin/installer/http_installer_test.go index 3eb92ee77..e89fea29d 100644 --- a/pkg/plugin/installer/http_installer_test.go +++ b/pkg/plugin/installer/http_installer_test.go @@ -20,9 +20,13 @@ import ( "bytes" "compress/gzip" "encoding/base64" + "fmt" "io/ioutil" + "net/http" + "net/http/httptest" "os" "path/filepath" + "strings" "syscall" "testing" @@ -63,9 +67,24 @@ func TestStripName(t *testing.T) { } } +func mockArchiveServer() *httptest.Server { + return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if !strings.HasSuffix(r.URL.Path, ".tar.gz") { + w.Header().Add("Content-Type", "text/html") + fmt.Fprintln(w, "broken") + return + } + w.Header().Add("Content-Type", "application/gzip") + fmt.Fprintln(w, "test") + })) +} + func TestHTTPInstaller(t *testing.T) { defer ensure.HelmHome(t)() - source := "https://repo.localdomain/plugins/fake-plugin-0.0.1.tar.gz" + + srv := mockArchiveServer() + defer srv.Close() + source := srv.URL + "/plugins/fake-plugin-0.0.1.tar.gz" if err := os.MkdirAll(helmpath.DataPath("plugins"), 0755); err != nil { t.Fatalf("Could not create %s: %s", helmpath.DataPath("plugins"), err) @@ -111,7 +130,9 @@ func TestHTTPInstaller(t *testing.T) { func TestHTTPInstallerNonExistentVersion(t *testing.T) { defer ensure.HelmHome(t)() - source := "https://repo.localdomain/plugins/fake-plugin-0.0.2.tar.gz" + srv := mockArchiveServer() + defer srv.Close() + source := srv.URL + "/plugins/fake-plugin-0.0.1.tar.gz" if err := os.MkdirAll(helmpath.DataPath("plugins"), 0755); err != nil { t.Fatalf("Could not create %s: %s", helmpath.DataPath("plugins"), err) @@ -141,7 +162,9 @@ func TestHTTPInstallerNonExistentVersion(t *testing.T) { } func TestHTTPInstallerUpdate(t *testing.T) { - source := "https://repo.localdomain/plugins/fake-plugin-0.0.1.tar.gz" + srv := mockArchiveServer() + defer srv.Close() + source := srv.URL + "/plugins/fake-plugin-0.0.1.tar.gz" defer ensure.HelmHome(t)() if err := os.MkdirAll(helmpath.DataPath("plugins"), 0755); err != nil { @@ -307,3 +330,26 @@ func TestCleanJoin(t *testing.T) { } } + +func TestMediaTypeToExtension(t *testing.T) { + + for mt, shouldPass := range map[string]bool{ + "": false, + "application/gzip": true, + "application/x-gzip": true, + "application/x-tgz": true, + "application/x-gtar": true, + "application/json": false, + } { + ext, ok := mediaTypeToExtension(mt) + if ok != shouldPass { + t.Errorf("Media type %q failed test", mt) + } + if shouldPass && ext == "" { + t.Errorf("Expected an extension but got empty string") + } + if !shouldPass && len(ext) != 0 { + t.Error("Expected extension to be empty for unrecognized type") + } + } +} diff --git a/pkg/plugin/installer/installer.go b/pkg/plugin/installer/installer.go index 61b49ab3b..6f01494e5 100644 --- a/pkg/plugin/installer/installer.go +++ b/pkg/plugin/installer/installer.go @@ -18,6 +18,7 @@ package installer import ( "fmt" "log" + "net/http" "os" "path/filepath" "strings" @@ -89,10 +90,29 @@ func isLocalReference(source string) bool { } // isRemoteHTTPArchive checks if the source is a http/https url and is an archive +// +// It works by checking whether the source looks like a URL and, if it does, running a +// HEAD operation to see if the remote resource is a file that we understand. func isRemoteHTTPArchive(source string) bool { if strings.HasPrefix(source, "http://") || strings.HasPrefix(source, "https://") { + res, err := http.Head(source) + if err != nil { + // If we get an error at the network layer, we can't install it. So + // we return false. + return false + } + + // Next, we look for the content type or content disposition headers to see + // if they have matching extractors. + contentType := res.Header.Get("content-type") + foundSuffix, ok := mediaTypeToExtension(contentType) + if !ok { + // Media type not recognized + return false + } + for suffix := range Extractors { - if strings.HasSuffix(source, suffix) { + if strings.HasSuffix(foundSuffix, suffix) { return true } } diff --git a/pkg/plugin/installer/installer_test.go b/pkg/plugin/installer/installer_test.go new file mode 100644 index 000000000..a11464924 --- /dev/null +++ b/pkg/plugin/installer/installer_test.go @@ -0,0 +1,40 @@ +/* +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 installer + +import "testing" + +func TestIsRemoteHTTPArchive(t *testing.T) { + srv := mockArchiveServer() + defer srv.Close() + source := srv.URL + "/plugins/fake-plugin-0.0.1.tar.gz" + + if isRemoteHTTPArchive("/not/a/URL") { + t.Errorf("Expected non-URL to return false") + } + + if isRemoteHTTPArchive("https://127.0.0.1:123/fake/plugin-1.2.3.tgz") { + t.Errorf("Bad URL should not have succeeded.") + } + + if !isRemoteHTTPArchive(source) { + t.Errorf("Expected %q to be a valid archive URL", source) + } + + if isRemoteHTTPArchive(source + "-not-an-extension") { + t.Error("Expected media type match to fail") + } +} From 2a74204508f005d89fe51b0e2824dae4f30b3252 Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Thu, 17 Sep 2020 11:09:37 -0700 Subject: [PATCH 256/281] go fmt Signed-off-by: Matthew Fisher --- pkg/chartutil/create_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/chartutil/create_test.go b/pkg/chartutil/create_test.go index 6cf3154ad..9a473fc59 100644 --- a/pkg/chartutil/create_test.go +++ b/pkg/chartutil/create_test.go @@ -154,7 +154,7 @@ func TestCreate_Overwrite(t *testing.T) { if errlog.Len() == 0 { t.Errorf("Expected warnings about overwriting files.") - } + } } func TestValidateChartName(t *testing.T) { From 59d5b94d35b24a500e30839a7c69f05d9ff077e2 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Thu, 17 Sep 2020 12:31:23 -0600 Subject: [PATCH 257/281] Merge pull request from GHSA-9vp5-m38w-j776 --- pkg/chart/chart.go | 4 +++ pkg/chart/errors.go | 7 ++++++ pkg/chart/metadata.go | 19 +++++++++++++++ pkg/chart/metadata_test.go | 50 +++++++++++++++++++++++++++++++++++++- 4 files changed, 79 insertions(+), 1 deletion(-) diff --git a/pkg/chart/chart.go b/pkg/chart/chart.go index bd75375a4..a3bed63a3 100644 --- a/pkg/chart/chart.go +++ b/pkg/chart/chart.go @@ -17,6 +17,7 @@ package chart import ( "path/filepath" + "regexp" "strings" ) @@ -26,6 +27,9 @@ const APIVersionV1 = "v1" // APIVersionV2 is the API version number for version 2. const APIVersionV2 = "v2" +// aliasNameFormat defines the characters that are legal in an alias name. +var aliasNameFormat = regexp.MustCompile("^[a-zA-Z0-9_-]+$") + // Chart is a helm package that contains metadata, a default config, zero or more // optionally parameterizable templates, and zero or more charts (dependencies). type Chart struct { diff --git a/pkg/chart/errors.go b/pkg/chart/errors.go index 4cb4189e6..2fad5f370 100644 --- a/pkg/chart/errors.go +++ b/pkg/chart/errors.go @@ -15,9 +15,16 @@ limitations under the License. package chart +import "fmt" + // ValidationError represents a data validation error. type ValidationError string func (v ValidationError) Error() string { return "validation: " + string(v) } + +// ValidationErrorf takes a message and formatting options and creates a ValidationError +func ValidationErrorf(msg string, args ...interface{}) ValidationError { + return ValidationError(fmt.Sprintf(msg, args...)) +} diff --git a/pkg/chart/metadata.go b/pkg/chart/metadata.go index 96a3965b9..1848eb280 100644 --- a/pkg/chart/metadata.go +++ b/pkg/chart/metadata.go @@ -81,6 +81,15 @@ func (md *Metadata) Validate() error { if !isValidChartType(md.Type) { return ValidationError("chart.metadata.type must be application or library") } + + // Aliases need to be validated here to make sure that the alias name does + // not contain any illegal characters. + for _, dependency := range md.Dependencies { + if err := validateDependency(dependency); err != nil { + return err + } + } + // TODO validate valid semver here? return nil } @@ -92,3 +101,13 @@ func isValidChartType(in string) bool { } return false } + +// validateDependency checks for common problems with the dependency datastructure in +// the chart. This check must be done at load time before the dependency's charts are +// loaded. +func validateDependency(dep *Dependency) error { + if len(dep.Alias) > 0 && !aliasNameFormat.MatchString(dep.Alias) { + return ValidationErrorf("dependency %q has disallowed characters in the alias", dep.Name) + } + return nil +} diff --git a/pkg/chart/metadata_test.go b/pkg/chart/metadata_test.go index 8b436000b..0c7b173dd 100644 --- a/pkg/chart/metadata_test.go +++ b/pkg/chart/metadata_test.go @@ -48,12 +48,60 @@ func TestValidate(t *testing.T) { &Metadata{Name: "test", APIVersion: "v2", Version: "1.0", Type: "application"}, nil, }, + { + &Metadata{ + Name: "test", + APIVersion: "v2", + Version: "1.0", + Type: "application", + Dependencies: []*Dependency{ + {Name: "dependency", Alias: "legal-alias"}, + }, + }, + nil, + }, + { + &Metadata{ + Name: "test", + APIVersion: "v2", + Version: "1.0", + Type: "application", + Dependencies: []*Dependency{ + {Name: "bad", Alias: "illegal alias"}, + }, + }, + ValidationError("dependency \"bad\" has disallowed characters in the alias"), + }, } for _, tt := range tests { result := tt.md.Validate() if result != tt.err { - t.Errorf("expected %s, got %s", tt.err, result) + t.Errorf("expected '%s', got '%s'", tt.err, result) + } + } +} + +func TestValidateDependency(t *testing.T) { + dep := &Dependency{ + Name: "example", + } + for value, shouldFail := range map[string]bool{ + "abcdefghijklmenopQRSTUVWXYZ-0123456780_": false, + "-okay": false, + "_okay": false, + "- bad": true, + " bad": true, + "bad\nvalue": true, + "bad ": true, + "bad$": true, + } { + dep.Alias = value + res := validateDependency(dep) + if res != nil && !shouldFail { + t.Errorf("Failed on case %q", dep.Alias) + } else if res == nil && shouldFail { + t.Errorf("Expected failure for %q", dep.Alias) } } } From 055dd41cbe53ce131ab0357524a7f6729e6e40dc Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Thu, 17 Sep 2020 12:33:59 -0600 Subject: [PATCH 258/281] Merge pull request from GHSA-jm56-5h66-w453 Signed-off-by: Matt Butcher --- pkg/downloader/chart_downloader_test.go | 2 +- pkg/repo/index.go | 19 +++++++++++++++- pkg/repo/index_test.go | 29 +++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/pkg/downloader/chart_downloader_test.go b/pkg/downloader/chart_downloader_test.go index b456143a1..b9fd3bf87 100644 --- a/pkg/downloader/chart_downloader_test.go +++ b/pkg/downloader/chart_downloader_test.go @@ -71,7 +71,7 @@ func TestResolveChartRef(t *testing.T) { if tt.fail { continue } - t.Errorf("%s: failed with error %s", tt.name, err) + t.Errorf("%s: failed with error %q", tt.name, err) continue } if got := u.String(); got != tt.expect { diff --git a/pkg/repo/index.go b/pkg/repo/index.go index 6ef2cf8b5..8b831029f 100644 --- a/pkg/repo/index.go +++ b/pkg/repo/index.go @@ -228,6 +228,23 @@ type ChartVersion struct { Created time.Time `json:"created,omitempty"` Removed bool `json:"removed,omitempty"` Digest string `json:"digest,omitempty"` + + // ChecksumDeprecated is deprecated in Helm 3, and therefore ignored. Helm 3 replaced + // this with Digest. However, with a strict YAML parser enabled, a field must be + // present on the struct for backwards compatibility. + ChecksumDeprecated string `json:"checksum,omitempty"` + + // EngineDeprecated is deprecated in Helm 3, and therefore ignored. However, with a strict + // YAML parser enabled, this field must be present. + EngineDeprecated string `json:"engine,omitempty"` + + // TillerVersionDeprecated is deprecated in Helm 3, and therefore ignored. However, with a strict + // YAML parser enabled, this field must be present. + TillerVersionDeprecated string `json:"tillerVersion,omitempty"` + + // URLDeprecated is deprectaed in Helm 3, superseded by URLs. It is ignored. However, + // with a strict YAML parser enabled, this must be present on the struct. + URLDeprecated string `json:"url,omitempty"` } // IndexDirectory reads a (flat) directory and generates an index. @@ -281,7 +298,7 @@ func IndexDirectory(dir, baseURL string) (*IndexFile, error) { // This will fail if API Version is not set (ErrNoAPIVersion) or if the unmarshal fails. func loadIndex(data []byte) (*IndexFile, error) { i := &IndexFile{} - if err := yaml.Unmarshal(data, i); err != nil { + if err := yaml.UnmarshalStrict(data, i); err != nil { return i, err } i.SortEntries() diff --git a/pkg/repo/index_test.go b/pkg/repo/index_test.go index 466a2c306..77b3a90ab 100644 --- a/pkg/repo/index_test.go +++ b/pkg/repo/index_test.go @@ -95,6 +95,35 @@ func TestLoadIndex(t *testing.T) { verifyLocalIndex(t, i) } +const indexWithDuplicates = ` +apiVersion: v1 +entries: + nginx: + - urls: + - https://kubernetes-charts.storage.googleapis.com/nginx-0.2.0.tgz + name: nginx + description: string + version: 0.2.0 + home: https://github.com/something/else + digest: "sha256:1234567890abcdef" + nginx: + - urls: + - https://kubernetes-charts.storage.googleapis.com/alpine-1.0.0.tgz + - http://storage2.googleapis.com/kubernetes-charts/alpine-1.0.0.tgz + name: alpine + description: string + version: 1.0.0 + home: https://github.com/something + digest: "sha256:1234567890abcdef" +` + +// TestLoadIndex_Duplicates is a regression to make sure that we don't non-deterministically allow duplicate packages. +func TestLoadIndex_Duplicates(t *testing.T) { + if _, err := loadIndex([]byte(indexWithDuplicates)); err == nil { + t.Errorf("Expected an error when duplicate entries are present") + } +} + func TestLoadIndexFile(t *testing.T) { i, err := LoadIndexFile(testfile) if err != nil { From 809e2d999e2c33e20e77f6bff30652d79c287542 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Thu, 17 Sep 2020 12:35:10 -0600 Subject: [PATCH 259/281] Merge pull request from GHSA-m54r-vrmv-hw33 Signed-off-by: Matt Butcher --- cmd/helm/load_plugins.go | 2 +- cmd/helm/plugin_install.go | 3 ++- pkg/plugin/plugin.go | 47 +++++++++++++++++++++++++++++++----- pkg/plugin/plugin_test.go | 49 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 93 insertions(+), 8 deletions(-) diff --git a/cmd/helm/load_plugins.go b/cmd/helm/load_plugins.go index e4aac6c0f..83590210a 100644 --- a/cmd/helm/load_plugins.go +++ b/cmd/helm/load_plugins.go @@ -59,7 +59,7 @@ func loadPlugins(baseCmd *cobra.Command, out io.Writer) { found, err := plugin.FindPlugins(settings.PluginsDirectory) if err != nil { - fmt.Fprintf(os.Stderr, "failed to load plugins: %s", err) + fmt.Fprintf(os.Stderr, "failed to load plugins: %s\n", err) return } diff --git a/cmd/helm/plugin_install.go b/cmd/helm/plugin_install.go index 183d3dc57..4e8ee327b 100644 --- a/cmd/helm/plugin_install.go +++ b/cmd/helm/plugin_install.go @@ -19,6 +19,7 @@ import ( "fmt" "io" + "github.com/pkg/errors" "github.com/spf13/cobra" "helm.sh/helm/v3/cmd/helm/require" @@ -81,7 +82,7 @@ func (o *pluginInstallOptions) run(out io.Writer) error { debug("loading plugin from %s", i.Path()) p, err := plugin.LoadDir(i.Path()) if err != nil { - return err + return errors.Wrap(err, "plugin is installed but unusable") } if err := runHook(p, plugin.Install); err != nil { diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go index caa34fbd3..9bac2244c 100644 --- a/pkg/plugin/plugin.go +++ b/pkg/plugin/plugin.go @@ -20,9 +20,11 @@ import ( "io/ioutil" "os" "path/filepath" + "regexp" "runtime" "strings" + "github.com/pkg/errors" "sigs.k8s.io/yaml" "helm.sh/helm/v3/pkg/cli" @@ -157,18 +159,51 @@ func (p *Plugin) PrepareCommand(extraArgs []string) (string, []string, error) { return main, baseArgs, nil } +// validPluginName is a regular expression that validates plugin names. +// +// Plugin names can only contain the ASCII characters a-z, A-Z, 0-9, ​_​ and ​-. +var validPluginName = regexp.MustCompile("^[A-Za-z0-9_-]+$") + +// validatePluginData validates a plugin's YAML data. +func validatePluginData(plug *Plugin, filepath string) error { + if !validPluginName.MatchString(plug.Metadata.Name) { + return fmt.Errorf("invalid plugin name at %q", filepath) + } + // We could also validate SemVer, executable, and other fields should we so choose. + return nil +} + +func detectDuplicates(plugs []*Plugin) error { + names := map[string]string{} + + for _, plug := range plugs { + if oldpath, ok := names[plug.Metadata.Name]; ok { + return fmt.Errorf( + "two plugins claim the name %q at %q and %q", + plug.Metadata.Name, + oldpath, + plug.Dir, + ) + } + names[plug.Metadata.Name] = plug.Dir + } + + return nil +} + // LoadDir loads a plugin from the given directory. func LoadDir(dirname string) (*Plugin, error) { - data, err := ioutil.ReadFile(filepath.Join(dirname, PluginFileName)) + pluginfile := filepath.Join(dirname, PluginFileName) + data, err := ioutil.ReadFile(pluginfile) if err != nil { - return nil, err + return nil, errors.Wrapf(err, "failed to read plugin at %q", pluginfile) } plug := &Plugin{Dir: dirname} if err := yaml.Unmarshal(data, &plug.Metadata); err != nil { - return nil, err + return nil, errors.Wrapf(err, "failed to load plugin at %q", pluginfile) } - return plug, nil + return plug, validatePluginData(plug, pluginfile) } // LoadAll loads all plugins found beneath the base directory. @@ -180,7 +215,7 @@ func LoadAll(basedir string) ([]*Plugin, error) { scanpath := filepath.Join(basedir, "*", PluginFileName) matches, err := filepath.Glob(scanpath) if err != nil { - return plugins, err + return plugins, errors.Wrapf(err, "failed to find plugins in %q", scanpath) } if matches == nil { @@ -195,7 +230,7 @@ func LoadAll(basedir string) ([]*Plugin, error) { } plugins = append(plugins, p) } - return plugins, nil + return plugins, detectDuplicates(plugins) } // FindPlugins returns a list of YAML files that describe plugins. diff --git a/pkg/plugin/plugin_test.go b/pkg/plugin/plugin_test.go index af0b61846..88add037d 100644 --- a/pkg/plugin/plugin_test.go +++ b/pkg/plugin/plugin_test.go @@ -16,6 +16,7 @@ limitations under the License. package plugin // import "helm.sh/helm/v3/pkg/plugin" import ( + "fmt" "os" "path/filepath" "reflect" @@ -320,3 +321,51 @@ func TestSetupEnv(t *testing.T) { } } } + +func TestValidatePluginData(t *testing.T) { + for i, item := range []struct { + pass bool + plug *Plugin + }{ + {true, mockPlugin("abcdefghijklmnopqrstuvwxyz0123456789_-ABC")}, + {true, mockPlugin("foo-bar-FOO-BAR_1234")}, + {false, mockPlugin("foo -bar")}, + {false, mockPlugin("$foo -bar")}, // Test leading chars + {false, mockPlugin("foo -bar ")}, // Test trailing chars + {false, mockPlugin("foo\nbar")}, // Test newline + } { + err := validatePluginData(item.plug, fmt.Sprintf("test-%d", i)) + if item.pass && err != nil { + t.Errorf("failed to validate case %d: %s", i, err) + } else if !item.pass && err == nil { + t.Errorf("expected case %d to fail", i) + } + } +} + +func TestDetectDuplicates(t *testing.T) { + plugs := []*Plugin{ + mockPlugin("foo"), + mockPlugin("bar"), + } + if err := detectDuplicates(plugs); err != nil { + t.Error("no duplicates in the first set") + } + plugs = append(plugs, mockPlugin("foo")) + if err := detectDuplicates(plugs); err == nil { + t.Error("duplicates in the second set") + } +} + +func mockPlugin(name string) *Plugin { + return &Plugin{ + Metadata: &Metadata{ + Name: name, + Version: "v0.1.2", + Usage: "Mock plugin", + Description: "Mock plugin for testing", + Command: "echo mock plugin", + }, + Dir: "no-such-dir", + } +} From 6eeec4a00241b7da1acaddcbf3278355de1f216e Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Thu, 17 Sep 2020 08:33:11 -0700 Subject: [PATCH 260/281] switched to stricter YAML parsing on plugin metadata files Signed-off-by: Matthew Fisher --- pkg/plugin/installer/local_installer_test.go | 2 +- pkg/plugin/installer/vcs_installer_test.go | 2 +- pkg/plugin/plugin.go | 8 +++++++- pkg/plugin/plugin_test.go | 15 +++++++++++---- .../plugdir/bad/duplicate-entries/plugin.yaml | 11 +++++++++++ .../plugdir/{ => good}/downloader/plugin.yaml | 0 .../testdata/plugdir/{ => good}/echo/plugin.yaml | 0 .../testdata/plugdir/{ => good}/hello/hello.sh | 0 .../testdata/plugdir/{ => good}/hello/plugin.yaml | 1 - 9 files changed, 31 insertions(+), 8 deletions(-) create mode 100644 pkg/plugin/testdata/plugdir/bad/duplicate-entries/plugin.yaml rename pkg/plugin/testdata/plugdir/{ => good}/downloader/plugin.yaml (100%) rename pkg/plugin/testdata/plugdir/{ => good}/echo/plugin.yaml (100%) rename pkg/plugin/testdata/plugdir/{ => good}/hello/hello.sh (100%) rename pkg/plugin/testdata/plugdir/{ => good}/hello/plugin.yaml (85%) diff --git a/pkg/plugin/installer/local_installer_test.go b/pkg/plugin/installer/local_installer_test.go index 3d9607331..96958ab09 100644 --- a/pkg/plugin/installer/local_installer_test.go +++ b/pkg/plugin/installer/local_installer_test.go @@ -37,7 +37,7 @@ func TestLocalInstaller(t *testing.T) { t.Fatal(err) } - source := "../testdata/plugdir/echo" + source := "../testdata/plugdir/good/echo" i, err := NewForSource(source, "") if err != nil { t.Fatalf("unexpected error: %s", err) diff --git a/pkg/plugin/installer/vcs_installer_test.go b/pkg/plugin/installer/vcs_installer_test.go index b8dc6b1e2..6785264b3 100644 --- a/pkg/plugin/installer/vcs_installer_test.go +++ b/pkg/plugin/installer/vcs_installer_test.go @@ -56,7 +56,7 @@ func TestVCSInstaller(t *testing.T) { } source := "https://github.com/adamreese/helm-env" - testRepoPath, _ := filepath.Abs("../testdata/plugdir/echo") + testRepoPath, _ := filepath.Abs("../testdata/plugdir/good/echo") repo := &testRepo{ local: testRepoPath, tags: []string{"0.1.0", "0.1.1"}, diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go index 9bac2244c..93b5527a1 100644 --- a/pkg/plugin/plugin.go +++ b/pkg/plugin/plugin.go @@ -96,6 +96,12 @@ type Metadata struct { // Downloaders field is used if the plugin supply downloader mechanism // for special protocols. Downloaders []Downloaders `json:"downloaders"` + + // UseTunnelDeprecated indicates that this command needs a tunnel. + // Setting this will cause a number of side effects, such as the + // automatic setting of HELM_HOST. + // DEPRECATED and unused, but retained for backwards compatibility with Helm 2 plugins. Remove in Helm 4 + UseTunnelDeprecated bool `json:"useTunnel,omitempty"` } // Plugin represents a plugin. @@ -200,7 +206,7 @@ func LoadDir(dirname string) (*Plugin, error) { } plug := &Plugin{Dir: dirname} - if err := yaml.Unmarshal(data, &plug.Metadata); err != nil { + if err := yaml.UnmarshalStrict(data, &plug.Metadata); err != nil { return nil, errors.Wrapf(err, "failed to load plugin at %q", pluginfile) } return plug, validatePluginData(plug, pluginfile) diff --git a/pkg/plugin/plugin_test.go b/pkg/plugin/plugin_test.go index 88add037d..2c4478953 100644 --- a/pkg/plugin/plugin_test.go +++ b/pkg/plugin/plugin_test.go @@ -178,7 +178,7 @@ func TestNoMatchPrepareCommand(t *testing.T) { } func TestLoadDir(t *testing.T) { - dirname := "testdata/plugdir/hello" + dirname := "testdata/plugdir/good/hello" plug, err := LoadDir(dirname) if err != nil { t.Fatalf("error loading Hello plugin: %s", err) @@ -205,8 +205,15 @@ func TestLoadDir(t *testing.T) { } } +func TestLoadDirDuplicateEntries(t *testing.T) { + dirname := "testdata/plugdir/bad/duplicate-entries" + if _, err := LoadDir(dirname); err == nil { + t.Errorf("successfully loaded plugin with duplicate entries when it should've failed") + } +} + func TestDownloader(t *testing.T) { - dirname := "testdata/plugdir/downloader" + dirname := "testdata/plugdir/good/downloader" plug, err := LoadDir(dirname) if err != nil { t.Fatalf("error loading Hello plugin: %s", err) @@ -244,7 +251,7 @@ func TestLoadAll(t *testing.T) { t.Fatalf("expected empty dir to have 0 plugins") } - basedir := "testdata/plugdir" + basedir := "testdata/plugdir/good" plugs, err := LoadAll(basedir) if err != nil { t.Fatalf("Could not load %q: %s", basedir, err) @@ -288,7 +295,7 @@ func TestFindPlugins(t *testing.T) { }, { name: "normal", - plugdirs: "./testdata/plugdir", + plugdirs: "./testdata/plugdir/good", expected: 3, }, } diff --git a/pkg/plugin/testdata/plugdir/bad/duplicate-entries/plugin.yaml b/pkg/plugin/testdata/plugdir/bad/duplicate-entries/plugin.yaml new file mode 100644 index 000000000..66498be96 --- /dev/null +++ b/pkg/plugin/testdata/plugdir/bad/duplicate-entries/plugin.yaml @@ -0,0 +1,11 @@ +name: "duplicate-entries" +version: "0.1.0" +usage: "usage" +description: |- + description +command: "echo hello" +ignoreFlags: true +hooks: + install: "echo installing..." +hooks: + install: "echo installing something different" diff --git a/pkg/plugin/testdata/plugdir/downloader/plugin.yaml b/pkg/plugin/testdata/plugdir/good/downloader/plugin.yaml similarity index 100% rename from pkg/plugin/testdata/plugdir/downloader/plugin.yaml rename to pkg/plugin/testdata/plugdir/good/downloader/plugin.yaml diff --git a/pkg/plugin/testdata/plugdir/echo/plugin.yaml b/pkg/plugin/testdata/plugdir/good/echo/plugin.yaml similarity index 100% rename from pkg/plugin/testdata/plugdir/echo/plugin.yaml rename to pkg/plugin/testdata/plugdir/good/echo/plugin.yaml diff --git a/pkg/plugin/testdata/plugdir/hello/hello.sh b/pkg/plugin/testdata/plugdir/good/hello/hello.sh similarity index 100% rename from pkg/plugin/testdata/plugdir/hello/hello.sh rename to pkg/plugin/testdata/plugdir/good/hello/hello.sh diff --git a/pkg/plugin/testdata/plugdir/hello/plugin.yaml b/pkg/plugin/testdata/plugdir/good/hello/plugin.yaml similarity index 85% rename from pkg/plugin/testdata/plugdir/hello/plugin.yaml rename to pkg/plugin/testdata/plugdir/good/hello/plugin.yaml index 6a78756d3..2b972da59 100644 --- a/pkg/plugin/testdata/plugdir/hello/plugin.yaml +++ b/pkg/plugin/testdata/plugdir/good/hello/plugin.yaml @@ -5,6 +5,5 @@ description: |- description command: "$HELM_PLUGIN_SELF/hello.sh" ignoreFlags: true -install: "echo installing..." hooks: install: "echo installing..." From 45d230fcc95c1c4d2e055b7451a988441f038509 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Thu, 17 Sep 2020 11:46:22 -0700 Subject: [PATCH 261/281] fix(cmd/helm): add build tags for architecture Signed-off-by: Adam Reese --- cmd/helm/root_unix.go | 2 ++ cmd/helm/root_unix_test.go | 2 ++ 2 files changed, 4 insertions(+) diff --git a/cmd/helm/root_unix.go b/cmd/helm/root_unix.go index 210842b35..4eb0b442b 100644 --- a/cmd/helm/root_unix.go +++ b/cmd/helm/root_unix.go @@ -1,3 +1,5 @@ +// +build !windows + /* Copyright The Helm Authors. diff --git a/cmd/helm/root_unix_test.go b/cmd/helm/root_unix_test.go index 73f18ec28..b1fcfbc66 100644 --- a/cmd/helm/root_unix_test.go +++ b/cmd/helm/root_unix_test.go @@ -1,3 +1,5 @@ +// +build !windows + /* Copyright The Helm Authors. From f19acbdc94578194d19a6758f01cd8eed85b792e Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Thu, 17 Sep 2020 14:54:55 -0700 Subject: [PATCH 262/281] fix: allow serverInfo field on index files A recent change merged into Helm fixes a number of security issues related to parsing malformed index files. Unfortunately, it also broke the ability for users to load index files from chartmuseum, which adds a "server info" field to add additional metadata. This commit adds that field so that index files from chartmuseum can be validated. Since Helm does not use this field for anything, the information is discarded and unused. Signed-off-by: Matthew Fisher --- pkg/repo/index.go | 2 + pkg/repo/index_test.go | 85 +++++++++++++++--------- pkg/repo/testdata/chartmuseum-index.yaml | 50 ++++++++++++++ 3 files changed, 105 insertions(+), 32 deletions(-) create mode 100644 pkg/repo/testdata/chartmuseum-index.yaml diff --git a/pkg/repo/index.go b/pkg/repo/index.go index 8b831029f..55b984eea 100644 --- a/pkg/repo/index.go +++ b/pkg/repo/index.go @@ -77,6 +77,8 @@ func (c ChartVersions) Less(a, b int) bool { // IndexFile represents the index file in a chart repository type IndexFile struct { + // This is used ONLY for validation against chartmuseum's index files and is discarded after validation. + ServerInfo map[string]interface{} `json:"serverInfo,omitempty"` APIVersion string `json:"apiVersion"` Generated time.Time `json:"generated"` Entries map[string]ChartVersions `json:"entries"` diff --git a/pkg/repo/index_test.go b/pkg/repo/index_test.go index 77b3a90ab..c22588971 100644 --- a/pkg/repo/index_test.go +++ b/pkg/repo/index_test.go @@ -35,9 +35,31 @@ import ( ) const ( - testfile = "testdata/local-index.yaml" - unorderedTestfile = "testdata/local-index-unordered.yaml" - testRepo = "test-repo" + testfile = "testdata/local-index.yaml" + chartmuseumtestfile = "testdata/chartmuseum-index.yaml" + unorderedTestfile = "testdata/local-index-unordered.yaml" + testRepo = "test-repo" + indexWithDuplicates = ` +apiVersion: v1 +entries: + nginx: + - urls: + - https://kubernetes-charts.storage.googleapis.com/nginx-0.2.0.tgz + name: nginx + description: string + version: 0.2.0 + home: https://github.com/something/else + digest: "sha256:1234567890abcdef" + nginx: + - urls: + - https://kubernetes-charts.storage.googleapis.com/alpine-1.0.0.tgz + - http://storage2.googleapis.com/kubernetes-charts/alpine-1.0.0.tgz + name: alpine + description: string + version: 1.0.0 + home: https://github.com/something + digest: "sha256:1234567890abcdef" +` ) func TestIndexFile(t *testing.T) { @@ -84,39 +106,38 @@ func TestIndexFile(t *testing.T) { } func TestLoadIndex(t *testing.T) { - b, err := ioutil.ReadFile(testfile) - if err != nil { - t.Fatal(err) + + tests := []struct { + Name string + Filename string + }{ + { + Name: "regular index file", + Filename: testfile, + }, + { + Name: "chartmuseum index file", + Filename: chartmuseumtestfile, + }, } - i, err := loadIndex(b) - if err != nil { - t.Fatal(err) + + for _, tc := range tests { + tc := tc + t.Run(tc.Name, func(t *testing.T) { + t.Parallel() + b, err := ioutil.ReadFile(tc.Filename) + if err != nil { + t.Fatal(err) + } + i, err := loadIndex(b) + if err != nil { + t.Fatal(err) + } + verifyLocalIndex(t, i) + }) } - verifyLocalIndex(t, i) } -const indexWithDuplicates = ` -apiVersion: v1 -entries: - nginx: - - urls: - - https://kubernetes-charts.storage.googleapis.com/nginx-0.2.0.tgz - name: nginx - description: string - version: 0.2.0 - home: https://github.com/something/else - digest: "sha256:1234567890abcdef" - nginx: - - urls: - - https://kubernetes-charts.storage.googleapis.com/alpine-1.0.0.tgz - - http://storage2.googleapis.com/kubernetes-charts/alpine-1.0.0.tgz - name: alpine - description: string - version: 1.0.0 - home: https://github.com/something - digest: "sha256:1234567890abcdef" -` - // TestLoadIndex_Duplicates is a regression to make sure that we don't non-deterministically allow duplicate packages. func TestLoadIndex_Duplicates(t *testing.T) { if _, err := loadIndex([]byte(indexWithDuplicates)); err == nil { diff --git a/pkg/repo/testdata/chartmuseum-index.yaml b/pkg/repo/testdata/chartmuseum-index.yaml new file mode 100644 index 000000000..3077596f4 --- /dev/null +++ b/pkg/repo/testdata/chartmuseum-index.yaml @@ -0,0 +1,50 @@ +serverInfo: + contextPath: /v1/helm +apiVersion: v1 +entries: + nginx: + - urls: + - https://kubernetes-charts.storage.googleapis.com/nginx-0.2.0.tgz + name: nginx + description: string + version: 0.2.0 + home: https://github.com/something/else + digest: "sha256:1234567890abcdef" + keywords: + - popular + - web server + - proxy + - urls: + - https://kubernetes-charts.storage.googleapis.com/nginx-0.1.0.tgz + name: nginx + description: string + version: 0.1.0 + home: https://github.com/something + digest: "sha256:1234567890abcdef" + keywords: + - popular + - web server + - proxy + alpine: + - urls: + - https://kubernetes-charts.storage.googleapis.com/alpine-1.0.0.tgz + - http://storage2.googleapis.com/kubernetes-charts/alpine-1.0.0.tgz + name: alpine + description: string + version: 1.0.0 + home: https://github.com/something + keywords: + - linux + - alpine + - small + - sumtin + digest: "sha256:1234567890abcdef" + chartWithNoURL: + - name: chartWithNoURL + description: string + version: 1.0.0 + home: https://github.com/something + keywords: + - small + - sumtin + digest: "sha256:1234567890abcdef" From 3eeeb0345d1ef7e8990efc761a7d10ddf6e67f52 Mon Sep 17 00:00:00 2001 From: Scott Rigby Date: Thu, 17 Sep 2020 21:29:44 -0400 Subject: [PATCH 263/281] Update docs links in release notes script Signed-off-by: Scott Rigby --- scripts/release-notes.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/release-notes.sh b/scripts/release-notes.sh index 3625aaa9a..b024a958d 100755 --- a/scripts/release-notes.sh +++ b/scripts/release-notes.sh @@ -90,7 +90,7 @@ Download Helm ${RELEASE}. The common platform binaries are here: - [Linux s390x](https://get.helm.sh/helm-${RELEASE}-linux-s390x.tar.gz) ([checksum](https://get.helm.sh/helm-${RELEASE}-linux-s390x.tar.gz.sha256sum) / $(cat _dist/helm-${RELEASE}-darwin-amd64.tar.gz.sha256)) - [Windows amd64](https://get.helm.sh/helm-${RELEASE}-windows-amd64.zip) ([checksum](https://get.helm.sh/helm-${RELEASE}-windows-amd64.zip.sha256sum) / $(cat _dist/helm-${RELEASE}-windows-amd64.zip.sha256)) -The [Quickstart Guide](https://docs.helm.sh/using_helm/#quickstart-guide) will get you going from there. For **upgrade instructions** or detailed installation notes, check the [install guide](https://docs.helm.sh/using_helm/#installing-helm). You can also use a [script to install](https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3) on any system with \`bash\`. +The [Quickstart Guide](https://helm.sh/docs/intro/quickstart/) will get you going from there. For **upgrade instructions** or detailed installation notes, check the [install guide](https://helm.sh/docs/intro/install/). You can also use a [script to install](https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3) on any system with \`bash\`. ## What's Next From 1138def202c95c2e76d0bd9d27bc36aa35224326 Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Thu, 17 Sep 2020 10:49:40 -0700 Subject: [PATCH 264/281] size/S and larger requiring 2 LGTMs Signed-off-by: Matthew Fisher --- CONTRIBUTING.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a8028dd01..ac88d13f2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -232,8 +232,9 @@ Like any good open source project, we use Pull Requests (PRs) to track code chan 3. Assigning reviews - Once a review has the `awaiting review` label, maintainers will review them as schedule permits. The maintainer who takes the issue should self-request a review. - - Any PR with the `size/large` label requires 2 review approvals from maintainers before it can - be merged. Those with `size/medium` or `size/small` are per the judgement of the maintainers. + - PRs from a community member with the label `size/S` or larger requires 2 review approvals from + maintainers before it can be merged. Those with `size/XS` are per the judgement of the + maintainers. 4. Reviewing/Discussion - All reviews will be completed using Github review tool. - A "Comment" review should be used when there are questions about the code that should be @@ -313,15 +314,16 @@ makes 30 lines of changes in 1 file, but it changes key functionality, it will l feature, but requires another 150 lines of tests to cover all cases, could be labeled as `size/S` even though the number of lines is greater than defined below. -PRs submitted by a core maintainer, regardless of size, only requires approval from one additional -maintainer. This ensures there are at least two maintainers who are aware of any significant PRs -introduced to the codebase. +Any changes from the community labeled as `size/S` or larger should be thoroughly tested before +merging and always requires approval from 2 core maintainers. PRs submitted by a core maintainer, +regardless of size, only requires approval from one additional maintainer. This ensures there are at +least two maintainers who are aware of any significant PRs introduced to the codebase. | Label | Description | | ----- | ----------- | | `size/XS` | Denotes a PR that changes 0-9 lines, ignoring generated files. Very little testing may be required depending on the change. | | `size/S` | Denotes a PR that changes 10-29 lines, ignoring generated files. Only small amounts of manual testing may be required. | | `size/M` | Denotes a PR that changes 30-99 lines, ignoring generated files. Manual validation should be required. | -| `size/L` | Denotes a PR that changes 100-499 lines, ignoring generated files. This should be thoroughly tested before merging and always requires 2 approvals. | -| `size/XL` | Denotes a PR that changes 500-999 lines, ignoring generated files. This should be thoroughly tested before merging and always requires 2 approvals. | -| `size/XXL` | Denotes a PR that changes 1000+ lines, ignoring generated files. This should be thoroughly tested before merging and always requires 2 approvals. | +| `size/L` | Denotes a PR that changes 100-499 lines, ignoring generated files. | +| `size/XL` | Denotes a PR that changes 500-999 lines, ignoring generated files. | +| `size/XXL` | Denotes a PR that changes 1000+ lines, ignoring generated files. | From 467bd49bb0cb0d613e802c738a0e38225eec054a Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sat, 19 Sep 2020 00:23:40 +0200 Subject: [PATCH 265/281] support passing signing passphrase from file or stdin (#8394) Signed-off-by: Sebastian Sdorra --- cmd/helm/package.go | 1 + pkg/action/package.go | 43 +++++++++++++++++++++++++++- pkg/action/package_test.go | 58 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 1 deletion(-) diff --git a/cmd/helm/package.go b/cmd/helm/package.go index 00fe0ef11..7134a8784 100644 --- a/cmd/helm/package.go +++ b/cmd/helm/package.go @@ -114,6 +114,7 @@ func newPackageCmd(out io.Writer) *cobra.Command { f.BoolVar(&client.Sign, "sign", false, "use a PGP private key to sign this package") f.StringVar(&client.Key, "key", "", "name of the key to use when signing. Used if --sign is true") f.StringVar(&client.Keyring, "keyring", defaultKeyring(), "location of a public keyring") + f.StringVar(&client.PassphraseFile, "passphrase-file", "", `location of a file which contains the passphrase for the signing key. Use "-" in order to read from stdin.`) f.StringVar(&client.Version, "version", "", "set the version on the chart to this semver version") f.StringVar(&client.AppVersion, "app-version", "", "set the appVersion on the chart to this version") f.StringVarP(&client.Destination, "destination", "d", ".", "location to write the chart.") diff --git a/pkg/action/package.go b/pkg/action/package.go index 0a927cd41..8f53bcac4 100644 --- a/pkg/action/package.go +++ b/pkg/action/package.go @@ -17,6 +17,7 @@ limitations under the License. package action import ( + "bufio" "fmt" "io/ioutil" "os" @@ -39,6 +40,7 @@ type Package struct { Sign bool Key string Keyring string + PassphraseFile string Version string AppVersion string Destination string @@ -120,7 +122,15 @@ func (p *Package) Clearsign(filename string) error { return err } - if err := signer.DecryptKey(promptUser); err != nil { + passphraseFetcher := promptUser + if p.PassphraseFile != "" { + passphraseFetcher, err = passphraseFileFetcher(p.PassphraseFile, os.Stdin) + if err != nil { + return err + } + } + + if err := signer.DecryptKey(passphraseFetcher); err != nil { return err } @@ -141,3 +151,34 @@ func promptUser(name string) ([]byte, error) { fmt.Println() return pw, err } + +func passphraseFileFetcher(passphraseFile string, stdin *os.File) (provenance.PassphraseFetcher, error) { + file, err := openPassphraseFile(passphraseFile, stdin) + if err != nil { + return nil, err + } + defer file.Close() + + reader := bufio.NewReader(file) + passphrase, _, err := reader.ReadLine() + if err != nil { + return nil, err + } + return func(name string) ([]byte, error) { + return passphrase, nil + }, nil +} + +func openPassphraseFile(passphraseFile string, stdin *os.File) (*os.File, error) { + if passphraseFile == "-" { + stat, err := stdin.Stat() + if err != nil { + return nil, err + } + if (stat.Mode() & os.ModeNamedPipe) == 0 { + return nil, errors.New("specified reading passphrase from stdin, without input on stdin") + } + return stdin, nil + } + return os.Open(passphraseFile) +} diff --git a/pkg/action/package_test.go b/pkg/action/package_test.go index 0f716118d..9a202cde4 100644 --- a/pkg/action/package_test.go +++ b/pkg/action/package_test.go @@ -17,8 +17,12 @@ limitations under the License. package action import ( + "io/ioutil" + "os" + "path" "testing" + "helm.sh/helm/v3/internal/test/ensure" "helm.sh/helm/v3/pkg/chart" ) @@ -42,3 +46,57 @@ func TestSetVersion(t *testing.T) { t.Error("Expected bogus version to return an error.") } } + +func TestPassphraseFileFetcher(t *testing.T) { + secret := "secret" + directory := ensure.TempFile(t, "passphrase-file", []byte(secret)) + defer os.RemoveAll(directory) + + fetcher, err := passphraseFileFetcher(path.Join(directory, "passphrase-file"), nil) + if err != nil { + t.Fatal("Unable to create passphraseFileFetcher", err) + } + + passphrase, err := fetcher("key") + if err != nil { + t.Fatal("Unable to fetch passphrase") + } + + if string(passphrase) != secret { + t.Errorf("Expected %s got %s", secret, string(passphrase)) + } +} + +func TestPassphraseFileFetcher_WithLineBreak(t *testing.T) { + secret := "secret" + directory := ensure.TempFile(t, "passphrase-file", []byte(secret+"\n\n.")) + defer os.RemoveAll(directory) + + fetcher, err := passphraseFileFetcher(path.Join(directory, "passphrase-file"), nil) + if err != nil { + t.Fatal("Unable to create passphraseFileFetcher", err) + } + + passphrase, err := fetcher("key") + if err != nil { + t.Fatal("Unable to fetch passphrase") + } + + if string(passphrase) != secret { + t.Errorf("Expected %s got %s", secret, string(passphrase)) + } +} + +func TestPassphraseFileFetcher_WithInvalidStdin(t *testing.T) { + directory := ensure.TempDir(t) + defer os.RemoveAll(directory) + + stdin, err := ioutil.TempFile(directory, "non-existing") + if err != nil { + t.Fatal("Unable to create test file", err) + } + + if _, err := passphraseFileFetcher("-", stdin); err == nil { + t.Error("Expected passphraseFileFetcher returning an error") + } +} From baf5b76a957dc52a2fca84fa1328628cc78cc307 Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Mon, 21 Sep 2020 10:42:47 -0400 Subject: [PATCH 266/281] Fixing issue with idempotent repo add A security issue fixed in 3.3.2 caught repos with the same name being added a second time and produced an error. This caused an issue for tools, such as helmfile, that will add the same name with the same configuration multiple times. This fix checks that the configuration on the existing and new repo are the same. If there is no change it notes it and exists with a 0 exit code. If there is a change the existing error is returned (for reverse compat). If --force-update is given the user opts in to changing the config for the name. Closes #8771 Signed-off-by: Matt Farina --- cmd/helm/repo_add.go | 22 +++++++++++++---- cmd/helm/repo_add_test.go | 34 ++++++++++++++++++++++---- cmd/helm/testdata/output/repo-add2.txt | 1 + 3 files changed, 47 insertions(+), 10 deletions(-) create mode 100644 cmd/helm/testdata/output/repo-add2.txt diff --git a/cmd/helm/repo_add.go b/cmd/helm/repo_add.go index 1c2162bfa..f79c213c0 100644 --- a/cmd/helm/repo_add.go +++ b/cmd/helm/repo_add.go @@ -116,11 +116,6 @@ func (o *repoAddOptions) run(out io.Writer) error { return err } - // If the repo exists and --force-update was not specified, error out. - if !o.forceUpdate && f.Has(o.name) { - return errors.Errorf("repository name (%s) already exists, please specify a different name", o.name) - } - if o.username != "" && o.password == "" { fd := int(os.Stdin.Fd()) fmt.Fprint(out, "Password: ") @@ -143,6 +138,23 @@ func (o *repoAddOptions) run(out io.Writer) error { InsecureSkipTLSverify: o.insecureSkipTLSverify, } + // If the repo exists do one of two things: + // 1. If the configuration for the name is the same continue without error + // 2. When the config is different require --force-update + if !o.forceUpdate && f.Has(o.name) { + existing := f.Get(o.name) + if c != *existing { + + // The input coming in for the name is different from what is already + // configured. Return an error. + return errors.Errorf("repository name (%s) already exists, please specify a different name", o.name) + } + + // The add is idempotent so do nothing + fmt.Fprintf(out, "%q already exists with the same configuration, skipping\n", o.name) + return nil + } + r, err := repo.NewChartRepository(&c, getter.All(settings)) if err != nil { return err diff --git a/cmd/helm/repo_add_test.go b/cmd/helm/repo_add_test.go index d358ad970..f3bc54985 100644 --- a/cmd/helm/repo_add_test.go +++ b/cmd/helm/repo_add_test.go @@ -40,14 +40,38 @@ func TestRepoAddCmd(t *testing.T) { } defer srv.Stop() + // A second test server is setup to verify URL changing + srv2, err := repotest.NewTempServerWithCleanup(t, "testdata/testserver/*.*") + if err != nil { + t.Fatal(err) + } + defer srv2.Stop() + tmpdir := ensure.TempDir(t) repoFile := filepath.Join(tmpdir, "repositories.yaml") - tests := []cmdTestCase{{ - name: "add a repository", - cmd: fmt.Sprintf("repo add test-name %s --repository-config %s --repository-cache %s", srv.URL(), repoFile, tmpdir), - golden: "output/repo-add.txt", - }} + tests := []cmdTestCase{ + { + name: "add a repository", + cmd: fmt.Sprintf("repo add test-name %s --repository-config %s --repository-cache %s", srv.URL(), repoFile, tmpdir), + golden: "output/repo-add.txt", + }, + { + name: "add repository second time", + cmd: fmt.Sprintf("repo add test-name %s --repository-config %s --repository-cache %s", srv.URL(), repoFile, tmpdir), + golden: "output/repo-add2.txt", + }, + { + name: "add repository different url", + cmd: fmt.Sprintf("repo add test-name %s --repository-config %s --repository-cache %s", srv2.URL(), repoFile, tmpdir), + wantError: true, + }, + { + name: "add repository second time", + cmd: fmt.Sprintf("repo add test-name %s --repository-config %s --repository-cache %s --force-update", srv2.URL(), repoFile, tmpdir), + golden: "output/repo-add.txt", + }, + } runTestCmd(t, tests) } diff --git a/cmd/helm/testdata/output/repo-add2.txt b/cmd/helm/testdata/output/repo-add2.txt new file mode 100644 index 000000000..263ffa9e4 --- /dev/null +++ b/cmd/helm/testdata/output/repo-add2.txt @@ -0,0 +1 @@ +"test-name" already exists with the same configuration, skipping From 8b546e90a96aaa541ccf818f515e418a18d77fbc Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Mon, 21 Sep 2020 15:05:24 -0400 Subject: [PATCH 267/281] Adding size labels pointer Add size of labels and number of reviewers is listed twice, pointing the area with less detail to the one with more detail. Signed-off-by: Matt Farina --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ac88d13f2..308154af5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -234,7 +234,7 @@ Like any good open source project, we use Pull Requests (PRs) to track code chan permits. The maintainer who takes the issue should self-request a review. - PRs from a community member with the label `size/S` or larger requires 2 review approvals from maintainers before it can be merged. Those with `size/XS` are per the judgement of the - maintainers. + maintainers. For more detail see the [Size Labels](#size-labels) section. 4. Reviewing/Discussion - All reviews will be completed using Github review tool. - A "Comment" review should be used when there are questions about the code that should be From 92c4bda184cda8323ad961d889a8186b2baa98bd Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Mon, 21 Sep 2020 08:31:08 -0700 Subject: [PATCH 268/281] use warning function This ensures warning messages are displayed on stderr rather than stdout. Signed-off-by: Matthew Fisher --- cmd/helm/registry_login.go | 2 +- cmd/helm/root.go | 2 +- cmd/helm/root_unix.go | 8 +++----- cmd/helm/root_unix_test.go | 33 +++++++++++++++++++++++---------- cmd/helm/root_windows.go | 2 +- cmd/helm/search_repo.go | 3 +-- 6 files changed, 30 insertions(+), 20 deletions(-) diff --git a/cmd/helm/registry_login.go b/cmd/helm/registry_login.go index e3435bf9d..43228f90a 100644 --- a/cmd/helm/registry_login.go +++ b/cmd/helm/registry_login.go @@ -104,7 +104,7 @@ func getUsernamePassword(usernameOpt string, passwordOpt string, passwordFromStd } } } else { - fmt.Fprintln(os.Stderr, "WARNING! Using --password via the CLI is insecure. Use --password-stdin.") + warning("Using --password via the CLI is insecure. Use --password-stdin.") } return username, password, nil diff --git a/cmd/helm/root.go b/cmd/helm/root.go index 91542bb7e..0a2b1be8f 100644 --- a/cmd/helm/root.go +++ b/cmd/helm/root.go @@ -205,7 +205,7 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string loadPlugins(cmd, out) // Check permissions on critical files - checkPerms(out) + checkPerms() return cmd, nil } diff --git a/cmd/helm/root_unix.go b/cmd/helm/root_unix.go index 4eb0b442b..b1b0f3896 100644 --- a/cmd/helm/root_unix.go +++ b/cmd/helm/root_unix.go @@ -19,14 +19,12 @@ limitations under the License. package main import ( - "fmt" - "io" "os" "os/user" "path/filepath" ) -func checkPerms(out io.Writer) { +func checkPerms() { // This function MUST NOT FAIL, as it is just a check for a common permissions problem. // If for some reason the function hits a stopping condition, it may panic. But only if // we can be sure that it is panicing because Helm cannot proceed. @@ -52,9 +50,9 @@ func checkPerms(out io.Writer) { perm := fi.Mode().Perm() if perm&0040 > 0 { - fmt.Fprintf(out, "WARNING: Kubernetes configuration file is group-readable. This is insecure. Location: %s\n", kc) + warning("Kubernetes configuration file is group-readable. This is insecure. Location: %s", kc) } if perm&0004 > 0 { - fmt.Fprintf(out, "WARNING: Kubernetes configuration file is world-readable. This is insecure. Location: %s\n", kc) + warning("Kubernetes configuration file is world-readable. This is insecure. Location: %s", kc) } } diff --git a/cmd/helm/root_unix_test.go b/cmd/helm/root_unix_test.go index b1fcfbc66..89c3c1eea 100644 --- a/cmd/helm/root_unix_test.go +++ b/cmd/helm/root_unix_test.go @@ -19,7 +19,7 @@ limitations under the License. package main import ( - "bytes" + "bufio" "io/ioutil" "os" "path/filepath" @@ -28,6 +28,14 @@ import ( ) func TestCheckPerms(t *testing.T) { + // NOTE(bacongobbler): have to open a new file handler here as the default os.Sterr cannot be read from + stderr, err := os.Open("/dev/stderr") + if err != nil { + t.Fatalf("could not open /dev/stderr for reading: %s", err) + } + defer stderr.Close() + reader := bufio.NewReader(stderr) + tdir, err := ioutil.TempDir("", "helmtest") if err != nil { t.Fatal(err) @@ -43,21 +51,26 @@ func TestCheckPerms(t *testing.T) { settings.KubeConfig = tfile defer func() { settings.KubeConfig = tconfig }() - var b bytes.Buffer - checkPerms(&b) + checkPerms() + text, err := reader.ReadString('\n') + if err != nil { + t.Fatalf("could not read from stderr: %s", err) + } expectPrefix := "WARNING: Kubernetes configuration file is group-readable. This is insecure. Location:" - if !strings.HasPrefix(b.String(), expectPrefix) { - t.Errorf("Expected to get a warning for group perms. Got %q", b.String()) + if !strings.HasPrefix(text, expectPrefix) { + t.Errorf("Expected to get a warning for group perms. Got %q", text) } if err := fh.Chmod(0404); err != nil { t.Errorf("Could not change mode on file: %s", err) } - b.Reset() - checkPerms(&b) + checkPerms() + text, err = reader.ReadString('\n') + if err != nil { + t.Fatalf("could not read from stderr: %s", err) + } expectPrefix = "WARNING: Kubernetes configuration file is world-readable. This is insecure. Location:" - if !strings.HasPrefix(b.String(), expectPrefix) { - t.Errorf("Expected to get a warning for world perms. Got %q", b.String()) + if !strings.HasPrefix(text, expectPrefix) { + t.Errorf("Expected to get a warning for world perms. Got %q", text) } - } diff --git a/cmd/helm/root_windows.go b/cmd/helm/root_windows.go index 243780d40..0b390f16c 100644 --- a/cmd/helm/root_windows.go +++ b/cmd/helm/root_windows.go @@ -18,7 +18,7 @@ package main import "io" -func checkPerms(out io.Writer) { +func checkPerms() { // Not yet implemented on Windows. If you know how to do a comprehensive perms // check on Windows, contributions welcomed! } diff --git a/cmd/helm/search_repo.go b/cmd/helm/search_repo.go index a7f27f179..bf82a6051 100644 --- a/cmd/helm/search_repo.go +++ b/cmd/helm/search_repo.go @@ -22,7 +22,6 @@ import ( "fmt" "io" "io/ioutil" - "os" "path/filepath" "strings" @@ -184,7 +183,7 @@ func (o *searchRepoOptions) buildIndex() (*search.Index, error) { f := filepath.Join(o.repoCacheDir, helmpath.CacheIndexFile(n)) ind, err := repo.LoadIndexFile(f) if err != nil { - fmt.Fprintf(os.Stderr, "WARNING: Repo %q is corrupt or missing. Try 'helm repo update'.", n) + warning("Repo %q is corrupt or missing. Try 'helm repo update'.", n) continue } From 3baaace868910d9b74f0d960e9441a2f3181fd00 Mon Sep 17 00:00:00 2001 From: lemonli Date: Tue, 22 Sep 2020 10:58:17 +0800 Subject: [PATCH 269/281] Update go version to 1.14 in go.mod Signed-off-by: lemonli --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 4b7c90f9d..0ebb377b0 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module helm.sh/helm/v3 -go 1.13 +go 1.14 require ( github.com/BurntSushi/toml v0.3.1 From 036832eba9dd604683d0803af96479bbfbe3b58f Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Tue, 22 Sep 2020 15:39:57 -0400 Subject: [PATCH 270/281] Fixing import package issue When #8779 was merged it introduced an issue with windows builds, which we do not test for in PR CI. This change fixes that problem. Signed-off-by: Matt Farina --- cmd/helm/root_windows.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/cmd/helm/root_windows.go b/cmd/helm/root_windows.go index 0b390f16c..7b5000f4f 100644 --- a/cmd/helm/root_windows.go +++ b/cmd/helm/root_windows.go @@ -16,8 +16,6 @@ limitations under the License. package main -import "io" - func checkPerms() { // Not yet implemented on Windows. If you know how to do a comprehensive perms // check on Windows, contributions welcomed! From 4c121c30851c5f607e6383c342d3a4be69dc4004 Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Wed, 23 Sep 2020 13:55:44 -0400 Subject: [PATCH 271/281] Adding annotation to index.yaml file Chart.yaml files have an annotation field that allow a chart to have custom information similar to the way Kubernetes annotations work. In an index.yaml file each chart version can have annotations in a similar manner to the Chart.yaml file. It is derived from the same underlying struct. These enable extension points where people can add their own info. One thing missing is the ability to extend the top level of an index file. This change adds annotations to the top level of an index.yaml file. This would provide top level support for vendors to extent index.yaml files. Closes #8767 Signed-off-by: Matt Farina --- pkg/repo/index.go | 4 ++ pkg/repo/index_test.go | 16 ++++++ .../testdata/local-index-annotations.yaml | 50 +++++++++++++++++++ 3 files changed, 70 insertions(+) create mode 100644 pkg/repo/testdata/local-index-annotations.yaml diff --git a/pkg/repo/index.go b/pkg/repo/index.go index 55b984eea..43f1e1c87 100644 --- a/pkg/repo/index.go +++ b/pkg/repo/index.go @@ -83,6 +83,10 @@ type IndexFile struct { Generated time.Time `json:"generated"` Entries map[string]ChartVersions `json:"entries"` PublicKeys []string `json:"publicKeys,omitempty"` + + // Annotations are additional mappings uninterpreted by Helm. They are made available for + // other applications to add information to the index file. + Annotations map[string]string `json:"annotations,omitempty"` } // NewIndexFile initializes an index. diff --git a/pkg/repo/index_test.go b/pkg/repo/index_test.go index c22588971..00135f97c 100644 --- a/pkg/repo/index_test.go +++ b/pkg/repo/index_test.go @@ -36,6 +36,7 @@ import ( const ( testfile = "testdata/local-index.yaml" + annotationstestfile = "testdata/local-index-annotations.yaml" chartmuseumtestfile = "testdata/chartmuseum-index.yaml" unorderedTestfile = "testdata/local-index-unordered.yaml" testRepo = "test-repo" @@ -153,6 +154,21 @@ func TestLoadIndexFile(t *testing.T) { verifyLocalIndex(t, i) } +func TestLoadIndexFileAnnotations(t *testing.T) { + i, err := LoadIndexFile(annotationstestfile) + if err != nil { + t.Fatal(err) + } + verifyLocalIndex(t, i) + + if len(i.Annotations) != 1 { + t.Fatalf("Expected 1 annotation but got %d", len(i.Annotations)) + } + if i.Annotations["helm.sh/test"] != "foo bar" { + t.Error("Did not get expected value for helm.sh/test annotation") + } +} + func TestLoadUnorderedIndex(t *testing.T) { b, err := ioutil.ReadFile(unorderedTestfile) if err != nil { diff --git a/pkg/repo/testdata/local-index-annotations.yaml b/pkg/repo/testdata/local-index-annotations.yaml new file mode 100644 index 000000000..ffaaa15aa --- /dev/null +++ b/pkg/repo/testdata/local-index-annotations.yaml @@ -0,0 +1,50 @@ +apiVersion: v1 +entries: + nginx: + - urls: + - https://kubernetes-charts.storage.googleapis.com/nginx-0.2.0.tgz + name: nginx + description: string + version: 0.2.0 + home: https://github.com/something/else + digest: "sha256:1234567890abcdef" + keywords: + - popular + - web server + - proxy + - urls: + - https://kubernetes-charts.storage.googleapis.com/nginx-0.1.0.tgz + name: nginx + description: string + version: 0.1.0 + home: https://github.com/something + digest: "sha256:1234567890abcdef" + keywords: + - popular + - web server + - proxy + alpine: + - urls: + - https://kubernetes-charts.storage.googleapis.com/alpine-1.0.0.tgz + - http://storage2.googleapis.com/kubernetes-charts/alpine-1.0.0.tgz + name: alpine + description: string + version: 1.0.0 + home: https://github.com/something + keywords: + - linux + - alpine + - small + - sumtin + digest: "sha256:1234567890abcdef" + chartWithNoURL: + - name: chartWithNoURL + description: string + version: 1.0.0 + home: https://github.com/something + keywords: + - small + - sumtin + digest: "sha256:1234567890abcdef" +annotations: + helm.sh/test: foo bar From b7c38c879a91cabec02699be1c4070206f84c88e Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Fri, 25 Sep 2020 12:15:06 -0400 Subject: [PATCH 272/281] Adding support for k8s 1.19 Closes #8806 Signed-off-by: Matt Farina --- go.mod | 15 ++-- go.sum | 171 +++++++++++++++++++++++++++++++++++++++++ pkg/action/validate.go | 2 +- pkg/kube/client.go | 4 +- 4 files changed, 182 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index 0ebb377b0..ed1ac1e7c 100644 --- a/go.mod +++ b/go.mod @@ -17,10 +17,11 @@ require ( github.com/docker/distribution v2.7.1+incompatible github.com/docker/docker v1.4.2-0.20200203170920-46ec8731fbce github.com/docker/go-units v0.4.0 - github.com/evanphx/json-patch v0.0.0-20200808040245-162e5629780b + github.com/evanphx/json-patch v4.9.0+incompatible github.com/gobwas/glob v0.2.3 github.com/gofrs/flock v0.7.1 github.com/gosuri/uitable v0.0.4 + github.com/jessevdk/go-flags v1.4.0 // indirect github.com/jmoiron/sqlx v1.2.0 github.com/lib/pq v1.7.0 github.com/mattn/go-shellwords v1.0.10 @@ -35,13 +36,13 @@ require ( github.com/stretchr/testify v1.6.1 github.com/xeipuuv/gojsonschema v1.2.0 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 - k8s.io/api v0.18.8 - k8s.io/apiextensions-apiserver v0.18.8 - k8s.io/apimachinery v0.18.8 - k8s.io/cli-runtime v0.18.8 - k8s.io/client-go v0.18.8 + k8s.io/api v0.19.2 + k8s.io/apiextensions-apiserver v0.19.2 + k8s.io/apimachinery v0.19.2 + k8s.io/cli-runtime v0.19.2 + k8s.io/client-go v0.19.2 k8s.io/klog v1.0.0 - k8s.io/kubectl v0.18.8 + k8s.io/kubectl v0.19.2 sigs.k8s.io/yaml v1.2.0 ) diff --git a/go.sum b/go.sum index 94f5fee82..e516e8d66 100644 --- a/go.sum +++ b/go.sum @@ -3,6 +3,17 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.51.0 h1:PvKAVQWCtlGUSlZkGW3QLelKaWq7KYv/MW1EboG8bfM= +cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= @@ -10,19 +21,27 @@ github.com/Azure/go-autorest v13.3.2+incompatible h1:VxzPyuhtnlBOzc4IWCZHqpyH2d+ github.com/Azure/go-autorest v13.3.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.9.0 h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs= github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= +github.com/Azure/go-autorest/autorest v0.9.6 h1:5YWtOnckcudzIw8lPPBcWOnmIFWMtHci1ZWAZulMSx0= +github.com/Azure/go-autorest/autorest v0.9.6/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630= github.com/Azure/go-autorest/autorest/adal v0.5.0 h1:q2gDruN08/guU9vAjuPWff0+QIrpH6ediguzdAzXAUU= github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= +github.com/Azure/go-autorest/autorest/adal v0.8.2 h1:O1X4oexUxnZCaEUGsvMnr8ZGj8HI37tNezwY4npRqA0= +github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= github.com/Azure/go-autorest/autorest/date v0.1.0 h1:YGrhWfrgtFs84+h0o46rJrlmsZtyZRg470CqAXTZaGM= github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSWlm5Ew6bxipnr/tbM= +github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= github.com/Azure/go-autorest/autorest/mocks v0.2.0 h1:Ww5g4zThfD/6cLb4z6xxgeyDa7QDkizMkJKe0ysZXp0= github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY= github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k= github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.4.1 h1:ThlnYciV1iM/V0OSF/dtkqWb6xo5qITT1TJBG1MRDJM= github.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= @@ -107,6 +126,9 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= @@ -181,6 +203,7 @@ github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arX github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QLSV/BsnenAOcDXdX4cMv4wP0B/5QbPg= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= @@ -201,6 +224,8 @@ github.com/evanphx/json-patch v4.2.0+incompatible h1:fUDGZCv/7iAN7u0puUVhvKCcsR6 github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M= github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses= +github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= @@ -210,6 +235,7 @@ github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVB github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7 h1:LofdAjjjqCSXMwLGgOgnE+rdPuvX9DxCqaHwKy7i/ko= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -217,6 +243,7 @@ github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= @@ -225,6 +252,8 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/logr v0.2.0 h1:QvGt2nLcHH0WK9orKa+ppBPAxREcH364nPUedEpK0TY= +github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= @@ -306,13 +335,24 @@ github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9 github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAONeMXrxql8uvOKuAZSu8aM5RUGv+1C6IJaEho= github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995/go.mod h1:lJgMEyOkYFkPcDKwRXegd+iM6E7matEszMG5HhwytU8= @@ -331,15 +371,20 @@ github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d h1:7XGaL1e6bYS1yIonGp9761ExpPPV1ui0SAC59Yube9k= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.1.0 h1:rVsPeBmXbYv4If/cumu1AzZPwV58q433hvONV1UEZoI= github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.4.1 h1:DLJCy1n/vrD4HPjOvYcT8aYQXpPIzoRZONaYwyycI+I= +github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/391+sT5o= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -391,6 +436,7 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= @@ -410,7 +456,10 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= @@ -424,6 +473,7 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= @@ -467,6 +517,8 @@ github.com/mattn/go-sqlite3 v1.12.0 h1:u/x3mp++qUxvYfulZ4HKOvVO0JWhk7HtE8lWhbGz/ github.com/mattn/go-sqlite3 v1.12.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= @@ -484,6 +536,8 @@ github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f h1:2+myh5ml7lgEU/5 github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/moby/term v0.0.0-20200312100748-672ec06f55cd h1:aY7OQNf2XqY/JQ6qREWamhI/81os/agb2BAGpcx5yWI= +github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -574,6 +628,8 @@ github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0 h1:miYCvYqFXtl/J9FIy8eNpBfYthAEFg+Ys0XyUVEcDsc= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= +github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= @@ -591,6 +647,8 @@ github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6 github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.7.0 h1:L+1lyG48J1zAQXA3RBX/nG/B3gjlHq0zTt2tlbJLyCY= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= @@ -600,6 +658,8 @@ github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQl github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= @@ -691,6 +751,7 @@ github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE= @@ -701,7 +762,9 @@ github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.etcd.io/etcd v0.5.0-alpha.5.0.20200819165624-17cef6e3e9d5/go.mod h1:skWido08r9w6Lq/w70DO5XYIKMu4QFu1+4VsqLQuJy8= go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= @@ -729,11 +792,13 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d h1:9FCpayM9Egr1baVnV1SX0H87m+XB0B8S0hAMi99X/3U= golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo= @@ -743,13 +808,27 @@ golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -766,6 +845,7 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -775,10 +855,17 @@ golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI= golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6 h1:pE8b58s1HRDMi8RDc79m0HISf9D4TzseP40cEA6IGfs= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -798,27 +885,43 @@ golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3 h1:7TYNF4UdlohbFwpNH04CoPMp1cHUZgO1Ebq5r2hIjfo= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7 h1:HmbHVPwrPEKPGLAcHSrMe6+hqSUlvZU0rab6x5EXfGU= golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f h1:68K/z8GLUxV76xGSqwTWw2gyk/jwn79LUL43rES2g8o= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4 h1:5/PjkGUjvEU5Gl6BxmvKRPpqo2uNMv4rcHBMwzk/st8= +golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= @@ -834,17 +937,30 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= @@ -852,10 +968,15 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= @@ -863,27 +984,45 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= @@ -911,62 +1050,94 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= k8s.io/api v0.18.4 h1:8x49nBRxuXGUlDlwlWd3RMY1SayZrzFfxea3UZSkFw4= k8s.io/api v0.18.4/go.mod h1:lOIQAKYgai1+vz9J7YcDZwC26Z0zQewYOGWdyIPUUQ4= k8s.io/api v0.18.8 h1:aIKUzJPb96f3fKec2lxtY7acZC9gQNDLVhfSGpxBAC4= k8s.io/api v0.18.8/go.mod h1:d/CXqwWv+Z2XEG1LgceeDmHQwpUJhROPx16SlxJgERY= +k8s.io/api v0.19.2 h1:q+/krnHWKsL7OBZg/rxnycsl9569Pud76UJ77MvKXms= +k8s.io/api v0.19.2/go.mod h1:IQpK0zFQ1xc5iNIQPqzgoOwuFugaYHK4iCknlAQP9nI= k8s.io/apiextensions-apiserver v0.18.4 h1:Y3HGERmS8t9u12YNUFoOISqefaoGRuTc43AYCLzWmWE= k8s.io/apiextensions-apiserver v0.18.4/go.mod h1:NYeyeYq4SIpFlPxSAB6jHPIdvu3hL0pc36wuRChybio= k8s.io/apiextensions-apiserver v0.18.8 h1:pkqYPKTHa0/3lYwH7201RpF9eFm0lmZDFBNzhN+k/sA= k8s.io/apiextensions-apiserver v0.18.8/go.mod h1:7f4ySEkkvifIr4+BRrRWriKKIJjPyg9mb/p63dJKnlM= +k8s.io/apiextensions-apiserver v0.19.2 h1:oG84UwiDsVDu7dlsGQs5GySmQHCzMhknfhFExJMz9tA= +k8s.io/apiextensions-apiserver v0.19.2/go.mod h1:EYNjpqIAvNZe+svXVx9j4uBaVhTB4C94HkY3w058qcg= k8s.io/apimachinery v0.18.4 h1:ST2beySjhqwJoIFk6p7Hp5v5O0hYY6Gngq/gUYXTPIA= k8s.io/apimachinery v0.18.4/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko= k8s.io/apimachinery v0.18.8 h1:jimPrycCqgx2QPearX3to1JePz7wSbVLq+7PdBTTwQ0= k8s.io/apimachinery v0.18.8/go.mod h1:6sQd+iHEqmOtALqOFjSWp2KZ9F0wlU/nWm0ZgsYWMig= +k8s.io/apimachinery v0.19.2 h1:5Gy9vQpAGTKHPVOh5c4plE274X8D/6cuEiTO2zve7tc= +k8s.io/apimachinery v0.19.2/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= k8s.io/apiserver v0.18.4/go.mod h1:q+zoFct5ABNnYkGIaGQ3bcbUNdmPyOCoEBcg51LChY8= k8s.io/apiserver v0.18.8/go.mod h1:12u5FuGql8Cc497ORNj79rhPdiXQC4bf53X/skR/1YM= +k8s.io/apiserver v0.19.2/go.mod h1:FreAq0bJ2vtZFj9Ago/X0oNGC51GfubKK/ViOKfVAOA= k8s.io/cli-runtime v0.18.4 h1:IUx7quIOb4gbQ4M+B1ksF/PTBovQuL5tXWzplX3t+FM= k8s.io/cli-runtime v0.18.4/go.mod h1:9/hS/Cuf7NVzWR5F/5tyS6xsnclxoPLVtwhnkJG1Y4g= k8s.io/cli-runtime v0.18.8 h1:ycmbN3hs7CfkJIYxJAOB10iW7BVPmXGXkfEyiV9NJ+k= k8s.io/cli-runtime v0.18.8/go.mod h1:7EzWiDbS9PFd0hamHHVoCY4GrokSTPSL32MA4rzIu0M= +k8s.io/cli-runtime v0.19.2 h1:d4uOtKhy3ImdaKqZJ8yQgLrdtUwsJLfP4Dw7L/kVPOo= +k8s.io/cli-runtime v0.19.2/go.mod h1:CMynmJM4Yf02TlkbhKxoSzi4Zf518PukJ5xep/NaNeY= k8s.io/client-go v0.18.4 h1:un55V1Q/B3JO3A76eS0kUSywgGK/WR3BQ8fHQjNa6Zc= k8s.io/client-go v0.18.4/go.mod h1:f5sXwL4yAZRkAtzOxRWUhA/N8XzGCb+nPZI8PfobZ9g= k8s.io/client-go v0.18.8 h1:SdbLpIxk5j5YbFr1b7fq8S7mDgDjYmUxSbszyoesoDM= k8s.io/client-go v0.18.8/go.mod h1:HqFqMllQ5NnQJNwjro9k5zMyfhZlOwpuTLVrxjkYSxU= +k8s.io/client-go v0.19.2 h1:gMJuU3xJZs86L1oQ99R4EViAADUPMHHtS9jFshasHSc= +k8s.io/client-go v0.19.2/go.mod h1:S5wPhCqyDNAlzM9CnEdgTGV4OqhsW3jGO1UM1epwfJA= k8s.io/code-generator v0.18.4/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c= k8s.io/code-generator v0.18.8/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c= +k8s.io/code-generator v0.19.2/go.mod h1:moqLn7w0t9cMs4+5CQyxnfA/HV8MF6aAVENF+WZZhgk= k8s.io/component-base v0.18.4 h1:Kr53Fp1iCGNsl9Uv4VcRvLy7YyIqi9oaJOQ7SXtKI98= k8s.io/component-base v0.18.4/go.mod h1:7jr/Ef5PGmKwQhyAz/pjByxJbC58mhKAhiaDu0vXfPk= k8s.io/component-base v0.18.8 h1:BW5CORobxb6q5mb+YvdwQlyXXS6NVH5fDXWbU7tf2L8= k8s.io/component-base v0.18.8/go.mod h1:00frPRDas29rx58pPCxNkhUfPbwajlyyvu8ruNgSErU= +k8s.io/component-base v0.19.2 h1:jW5Y9RcZTb79liEhW3XDVTW7MuvEGP0tQZnfSX6/+gs= +k8s.io/component-base v0.19.2/go.mod h1:g5LrsiTiabMLZ40AR6Hl45f088DevyGY+cCE2agEIVo= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= +k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= +k8s.io/klog/v2 v2.2.0 h1:XRvcwJozkgZ1UQJmfMGpvRthQHOvihEhYtDfAaxMz/A= +k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6 h1:Oh3Mzx5pJ+yIumsAD0MOECPVeXsVot0UkiaCGVyfGQY= k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= +k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6 h1:+WnxoVtG8TMiudHBSEtrVL1egv36TkkJm+bA8AxicmQ= +k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= k8s.io/kubectl v0.18.4 h1:l9DUYPTEMs1+qNtoqPpTyaJOosvj7l7tQqphCO1K52s= k8s.io/kubectl v0.18.4/go.mod h1:EzB+nfeUWk6fm6giXQ8P4Fayw3dsN+M7Wjy23mTRtB0= k8s.io/kubectl v0.18.8 h1:qTkHCz21YmK0+S0oE6TtjtxmjeDP42gJcZJyRKsIenA= k8s.io/kubectl v0.18.8/go.mod h1:PlEgIAjOMua4hDFTEkVf+W5M0asHUKfE4y7VDZkpLHM= +k8s.io/kubectl v0.19.2 h1:/Dxz9u7S0GnchLA6Avqi5k1qhZH4Fusgecj8dHsSnbk= +k8s.io/kubectl v0.19.2/go.mod h1:4ib3oj5ma6gF95QukTvC7ZBMxp60+UEAhDPjLuBIrV4= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/metrics v0.18.4/go.mod h1:luze4fyI9JG4eLDZy0kFdYEebqNfi0QrG4xNEbPkHOs= k8s.io/metrics v0.18.8/go.mod h1:j7JzZdiyhLP2BsJm/Fzjs+j5Lb1Y7TySjhPWqBPwRXA= +k8s.io/metrics v0.19.2/go.mod h1:IlLaAGXN0q7yrtB+SV0q3JIraf6VtlDr+iuTcX21fCU= k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89 h1:d4vVOjXm687F1iLSP2q3lyPPuyvTUt3aVoBpi2DqRsU= k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +k8s.io/utils v0.0.0-20200729134348-d5654de09c73 h1:uJmqzgNWG7XyClnU/mLPBWwfKKF1K8Hf8whTseBgJcg= +k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.9/go.mod h1:dzAXnQbTRyDlZPJX2SUPEqvnB+j7AJjtlox7PEwigU0= sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0= sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= sigs.k8s.io/structured-merge-diff/v3 v3.0.0 h1:dOmIZBMfhcHS09XZkMyUgkq5trg3/jRyJYFZUiaOp8E= sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= +sigs.k8s.io/structured-merge-diff/v4 v4.0.1 h1:YXTMot5Qz/X1iBRJhAt+vI+HVttY0WkSqqhKxQ0xVbA= +sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= diff --git a/pkg/action/validate.go b/pkg/action/validate.go index 0c40a9c3c..6e074f78b 100644 --- a/pkg/action/validate.go +++ b/pkg/action/validate.go @@ -46,7 +46,7 @@ func existingResourceConflict(resources kube.ResourceList, releaseName, releaseN } helper := resource.NewHelper(info.Client, info.Mapping) - existing, err := helper.Get(info.Namespace, info.Name, info.Export) + existing, err := helper.Get(info.Namespace, info.Name) if err != nil { if apierrors.IsNotFound(err) { return nil diff --git a/pkg/kube/client.go b/pkg/kube/client.go index decfc2e6e..83bebf51f 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -178,7 +178,7 @@ func (c *Client) Update(original, target ResourceList, force bool) (*Result, err } helper := resource.NewHelper(info.Client, info.Mapping) - if _, err := helper.Get(info.Namespace, info.Name, info.Export); err != nil { + if _, err := helper.Get(info.Namespace, info.Name); err != nil { if !apierrors.IsNotFound(err) { return errors.Wrap(err, "could not get information about the resource") } @@ -374,7 +374,7 @@ func createPatch(target *resource.Info, current runtime.Object) ([]byte, types.P // Fetch the current object for the three way merge helper := resource.NewHelper(target.Client, target.Mapping) - currentObj, err := helper.Get(target.Namespace, target.Name, target.Export) + currentObj, err := helper.Get(target.Namespace, target.Name) if err != nil && !apierrors.IsNotFound(err) { return nil, types.StrategicMergePatchType, errors.Wrapf(err, "unable to get data for current object %s/%s", target.Namespace, target.Name) } From 66034e403548417ac90d9769f5dcde2d354e0033 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Wed, 6 May 2020 13:43:17 -0700 Subject: [PATCH 273/281] ref(cmd): prevent klogs flags from polluting the help text Remove klog flags from help text. No change to behavior. ``` ... Flags: --debug enable verbose output -h, --help help for helm --kube-apiserver string the address and the port for the Kubernetes API server --kube-context string name of the kubeconfig context to use --kube-token string bearer token used for authentication --kubeconfig string path to the kubeconfig file -n, --namespace string namespace scope for this request --registry-config string path to the registry config file (default "/Users/areese/.config/helm/registry.json") --repository-cache string path to the file containing cached repository indexes (default "/Users/areese/.cache/helm/repository") --repository-config string path to the file containing repository names and URLs (default "/Users/areese/.config/helm/repositories.yaml") ``` Signed-off-by: Adam Reese --- cmd/helm/flags.go | 23 +++++++++++++++++++++++ cmd/helm/helm.go | 18 ------------------ cmd/helm/root.go | 1 + 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/cmd/helm/flags.go b/cmd/helm/flags.go index d1329c279..75b9056f0 100644 --- a/cmd/helm/flags.go +++ b/cmd/helm/flags.go @@ -17,6 +17,7 @@ limitations under the License. package main import ( + "flag" "fmt" "log" "path/filepath" @@ -24,6 +25,7 @@ import ( "github.com/spf13/cobra" "github.com/spf13/pflag" + "k8s.io/klog" "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/cli/output" @@ -155,3 +157,24 @@ func compVersionFlag(chartRef string, toComplete string) ([]string, cobra.ShellC return versions, cobra.ShellCompDirectiveNoFileComp } + +// addKlogFlags adds flags from k8s.io/klog +// marks the flags as hidden to avoid polluting the help text +func addKlogFlags(fs *pflag.FlagSet) { + local := flag.NewFlagSet("klog", flag.ExitOnError) + klog.InitFlags(local) + local.VisitAll(func(fl *flag.Flag) { + fl.Name = normalize(fl.Name) + if fs.Lookup(fl.Name) != nil { + return + } + newflag := pflag.PFlagFromGoFlag(fl) + newflag.Hidden = true + fs.AddFlag(newflag) + }) +} + +// normalize replaces underscores with hyphens +func normalize(s string) string { + return strings.ReplaceAll(s, "_", "-") +} diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index 98cb00f43..88a5ddcb9 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -17,7 +17,6 @@ limitations under the License. package main // import "helm.sh/helm/v3/cmd/helm" import ( - "flag" "fmt" "io/ioutil" "log" @@ -25,8 +24,6 @@ import ( "strings" "github.com/spf13/cobra" - "github.com/spf13/pflag" - "k8s.io/klog" "sigs.k8s.io/yaml" // Import to initialize client auth plugins. @@ -61,17 +58,7 @@ func warning(format string, v ...interface{}) { fmt.Fprintf(os.Stderr, format, v...) } -func initKubeLogs() { - pflag.CommandLine.SetNormalizeFunc(wordSepNormalizeFunc) - gofs := flag.NewFlagSet("klog", flag.ExitOnError) - klog.InitFlags(gofs) - pflag.CommandLine.AddGoFlagSet(gofs) - pflag.CommandLine.Set("logtostderr", "true") -} - func main() { - initKubeLogs() - actionConfig := new(action.Configuration) cmd, err := newRootCmd(actionConfig, os.Stdout, os.Args[1:]) if err != nil { @@ -101,11 +88,6 @@ func main() { } } -// wordSepNormalizeFunc changes all flags that contain "_" separators -func wordSepNormalizeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName { - return pflag.NormalizedName(strings.ReplaceAll(name, "_", "-")) -} - func checkOCIFeatureGate() func(_ *cobra.Command, _ []string) error { return func(_ *cobra.Command, _ []string) error { if !FeatureGateOCI.IsEnabled() { diff --git a/cmd/helm/root.go b/cmd/helm/root.go index 0a2b1be8f..cc97a5541 100644 --- a/cmd/helm/root.go +++ b/cmd/helm/root.go @@ -93,6 +93,7 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string flags := cmd.PersistentFlags() settings.AddFlags(flags) + addKlogFlags(flags) // Setup shell completion for the namespace flag err := cmd.RegisterFlagCompletionFunc("namespace", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { From a167b3fc8719db19e87280bc134a24801808272e Mon Sep 17 00:00:00 2001 From: zouyu Date: Sun, 27 Sep 2020 14:41:27 +0800 Subject: [PATCH 274/281] Fix wrong function's name in comment Signed-off-by: zouyu --- pkg/repo/chartrepo.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/repo/chartrepo.go b/pkg/repo/chartrepo.go index edb86eaeb..92892bb85 100644 --- a/pkg/repo/chartrepo.go +++ b/pkg/repo/chartrepo.go @@ -208,7 +208,7 @@ func FindChartInAuthRepoURL(repoURL, username, password, chartName, chartVersion return FindChartInAuthAndTLSRepoURL(repoURL, username, password, chartName, chartVersion, certFile, keyFile, caFile, false, getters) } -// FindChartInAuthRepoURL finds chart in chart repository pointed by repoURL +// FindChartInAuthAndTLSRepoURL finds chart in chart repository pointed by repoURL // without adding repo to repositories, like FindChartInRepoURL, // but it also receives credentials and TLS verify flag for the chart repository. // TODO Helm 4, FindChartInAuthAndTLSRepoURL should be integrated into FindChartInAuthRepoURL. From a6e76cbbbeee153ad93cb7a269714080b4c37e1d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Sep 2020 13:27:48 +0000 Subject: [PATCH 275/281] Bump github.com/lib/pq from 1.7.0 to 1.8.0 Bumps [github.com/lib/pq](https://github.com/lib/pq) from 1.7.0 to 1.8.0. - [Release notes](https://github.com/lib/pq/releases) - [Commits](https://github.com/lib/pq/compare/v1.7.0...v1.8.0) Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index ed1ac1e7c..6d69113fa 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/gosuri/uitable v0.0.4 github.com/jessevdk/go-flags v1.4.0 // indirect github.com/jmoiron/sqlx v1.2.0 - github.com/lib/pq v1.7.0 + github.com/lib/pq v1.8.0 github.com/mattn/go-shellwords v1.0.10 github.com/mitchellh/copystructure v1.0.0 github.com/opencontainers/go-digest v1.0.0 diff --git a/go.sum b/go.sum index e516e8d66..2dd3d7fca 100644 --- a/go.sum +++ b/go.sum @@ -486,6 +486,8 @@ github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.7.0 h1:h93mCPfUSkaul3Ka/VG8uZdmW1uMHDGxzu0NWHuJmHY= github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg= +github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= From 253a9500d7e4b1ae3a9c7943e39cfc19c55a1e25 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Sep 2020 13:28:25 +0000 Subject: [PATCH 276/281] Bump github.com/gofrs/flock from 0.7.1 to 0.8.0 Bumps [github.com/gofrs/flock](https://github.com/gofrs/flock) from 0.7.1 to 0.8.0. - [Release notes](https://github.com/gofrs/flock/releases) - [Commits](https://github.com/gofrs/flock/compare/v0.7.1...v0.8.0) Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index ed1ac1e7c..3b17e3e62 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/docker/go-units v0.4.0 github.com/evanphx/json-patch v4.9.0+incompatible github.com/gobwas/glob v0.2.3 - github.com/gofrs/flock v0.7.1 + github.com/gofrs/flock v0.8.0 github.com/gosuri/uitable v0.0.4 github.com/jessevdk/go-flags v1.4.0 // indirect github.com/jmoiron/sqlx v1.2.0 diff --git a/go.sum b/go.sum index e516e8d66..1a7e720a8 100644 --- a/go.sum +++ b/go.sum @@ -320,6 +320,8 @@ github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6 github.com/godror/godror v0.13.3/go.mod h1:2ouUT4kdhUBk7TAkHWD4SN0CdI0pgEQbo8FVHhbSKWg= github.com/gofrs/flock v0.7.1 h1:DP+LD/t0njgoPBvT5MJLeliUIVQR03hiKR6vezdwHlc= github.com/gofrs/flock v0.7.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gofrs/flock v0.8.0 h1:MSdYClljsF3PbENUUEx85nkWfJSGfzYI9yEBZOJz6CY= +github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= From 6aa54eacc52db045e82faa6b256678863e325a20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikula=CC=81s=CC=8C=20Di=CC=81te=CC=8C?= Date: Thu, 27 Aug 2020 11:45:14 +0200 Subject: [PATCH 277/281] feat(install): add requested version to error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mikuláš Dítě --- cmd/helm/show_test.go | 7 +++++++ pkg/action/install.go | 6 +++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/cmd/helm/show_test.go b/cmd/helm/show_test.go index ac5294d3c..9781a3de4 100644 --- a/cmd/helm/show_test.go +++ b/cmd/helm/show_test.go @@ -49,6 +49,13 @@ func TestShowPreReleaseChart(t *testing.T) { fail: true, expectedErr: "failed to download \"test/pre-release-chart\"", }, + { + name: "show pre-release chart", + args: "test/pre-release-chart", + fail: true, + flags: "--version 1.0.0", + expectedErr: "failed to download \"test/pre-release-chart\" at version \"1.0.0\"", + }, { name: "show pre-release chart with 'devel' flag", args: "test/pre-release-chart", diff --git a/pkg/action/install.go b/pkg/action/install.go index 9bfecd915..caeefca68 100644 --- a/pkg/action/install.go +++ b/pkg/action/install.go @@ -677,5 +677,9 @@ func (c *ChartPathOptions) LocateChart(name string, settings *cli.EnvSettings) ( return filename, err } - return filename, errors.Errorf("failed to download %q (hint: running `helm repo update` may help)", name) + atVersion := "" + if version != "" { + atVersion = fmt.Sprintf(" at version %q", version) + } + return filename, errors.Errorf("failed to download %q%s (hint: running `helm repo update` may help)", name, atVersion) } From 2bc79d32940a10d5221edfeb408f48f590ccc808 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Sep 2020 06:28:01 +0000 Subject: [PATCH 278/281] Bump github.com/sirupsen/logrus from 1.6.0 to 1.7.0 Bumps [github.com/sirupsen/logrus](https://github.com/sirupsen/logrus) from 1.6.0 to 1.7.0. - [Release notes](https://github.com/sirupsen/logrus/releases) - [Changelog](https://github.com/sirupsen/logrus/blob/master/CHANGELOG.md) - [Commits](https://github.com/sirupsen/logrus/compare/v1.6.0...v1.7.0) Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 4a51e257a..0a7f61be7 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,7 @@ require ( github.com/opencontainers/image-spec v1.0.1 github.com/pkg/errors v0.9.1 github.com/rubenv/sql-migrate v0.0.0-20200616145509-8d140a17f351 - github.com/sirupsen/logrus v1.6.0 + github.com/sirupsen/logrus v1.7.0 github.com/spf13/cobra v1.0.0 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.6.1 diff --git a/go.sum b/go.sum index 3b3ada8fd..9fdc12a5f 100644 --- a/go.sum +++ b/go.sum @@ -692,6 +692,8 @@ github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= @@ -908,6 +910,7 @@ golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7 h1:HmbHVPwrPEKPGLAcHSrMe6+hqSUlvZU0rab6x5EXfGU= golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f h1:68K/z8GLUxV76xGSqwTWw2gyk/jwn79LUL43rES2g8o= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= From b08c7d2429ed445b9571c6c275c26b7bac7236e3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Sep 2020 06:28:13 +0000 Subject: [PATCH 279/281] Bump github.com/DATA-DOG/go-sqlmock from 1.4.1 to 1.5.0 Bumps [github.com/DATA-DOG/go-sqlmock](https://github.com/DATA-DOG/go-sqlmock) from 1.4.1 to 1.5.0. - [Release notes](https://github.com/DATA-DOG/go-sqlmock/releases) - [Commits](https://github.com/DATA-DOG/go-sqlmock/compare/v1.4.1...v1.5.0) Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 4a51e257a..64415850c 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.14 require ( github.com/BurntSushi/toml v0.3.1 - github.com/DATA-DOG/go-sqlmock v1.4.1 + github.com/DATA-DOG/go-sqlmock v1.5.0 github.com/Masterminds/goutils v1.1.0 github.com/Masterminds/semver/v3 v3.1.0 github.com/Masterminds/sprig/v3 v3.1.0 diff --git a/go.sum b/go.sum index 3b3ada8fd..a7c3c210d 100644 --- a/go.sum +++ b/go.sum @@ -44,6 +44,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.4.1 h1:ThlnYciV1iM/V0OSF/dtkqWb6xo5qITT1TJBG1MRDJM= github.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= +github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd h1:sjQovDkwrZp8u+gxLtPgKGjk5hCxuy2hrRejBTA9xFU= github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= From 59c77716ad61331da28c37e9430d5f6a3ab23fed Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Wed, 30 Sep 2020 11:54:56 +0800 Subject: [PATCH 280/281] TestCheckPerms: utilize pipe to read stderr Refer to the stderr manpage: $ man 3 stderr *Note that mixing use of FILEs and raw file descriptors can produce unexpected results and should generally be avoided.* And actually, we noticed that the warning() will output the message to stdout instead of stderr sometimes. lizj@FNSTPC:~/workspace/k8s/helm$ while true; do timeout 1m go test -count=1 -run TestCheckPerms ./cmd/helm -v 2>/dev/null; done === RUN TestCheckPerms --- PASS: TestCheckPerms (0.00s) PASS ok helm.sh/helm/v3/cmd/helm 0.028s === RUN TestCheckPerms --- PASS: TestCheckPerms (0.00s) PASS ok helm.sh/helm/v3/cmd/helm 0.027s === RUN TestCheckPerms --- PASS: TestCheckPerms (0.00s) PASS ok helm.sh/helm/v3/cmd/helm 0.028s === RUN TestCheckPerms --- PASS: TestCheckPerms (0.00s) PASS ok helm.sh/helm/v3/cmd/helm 0.029s === RUN TestCheckPerms --- PASS: TestCheckPerms (0.00s) PASS ok helm.sh/helm/v3/cmd/helm 0.029s === RUN TestCheckPerms --- PASS: TestCheckPerms (0.00s) PASS ok helm.sh/helm/v3/cmd/helm 0.028s === RUN TestCheckPerms --- PASS: TestCheckPerms (0.00s) PASS ok helm.sh/helm/v3/cmd/helm 0.030s === RUN TestCheckPerms WARNING: Kubernetes configuration file is group-readable. This is insecure. Location: /tmp/helmtest093620773/testconfig === RUN TestCheckPerms WARNING: Kubernetes configuration file is group-readable. This is insecure. Location: /tmp/helmtest083469215/testconfig === RUN TestCheckPerms WARNING: Kubernetes configuration file is group-readable. This is insecure. Location: /tmp/helmtest101343249/testconfig === RUN TestCheckPerms --- PASS: TestCheckPerms (0.00s) PASS ok helm.sh/helm/v3/cmd/helm 0.032s === RUN TestCheckPerms --- PASS: TestCheckPerms (0.00s) PASS ok helm.sh/helm/v3/cmd/helm 0.040s === RUN TestCheckPerms --- PASS: TestCheckPerms (0.00s) PASS ok helm.sh/helm/v3/cmd/helm 0.031s === RUN TestCheckPerms WARNING: Kubernetes configuration file is group-readable. This is insecure. Location: /tmp/helmtest706352639/testconfig Signed-off-by: Li Zhijian Signed-off-by: Lu Fengqi --- cmd/helm/root_unix_test.go | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/cmd/helm/root_unix_test.go b/cmd/helm/root_unix_test.go index 89c3c1eea..c62776c2a 100644 --- a/cmd/helm/root_unix_test.go +++ b/cmd/helm/root_unix_test.go @@ -19,7 +19,8 @@ limitations under the License. package main import ( - "bufio" + "bytes" + "io" "io/ioutil" "os" "path/filepath" @@ -27,15 +28,27 @@ import ( "testing" ) -func TestCheckPerms(t *testing.T) { - // NOTE(bacongobbler): have to open a new file handler here as the default os.Sterr cannot be read from - stderr, err := os.Open("/dev/stderr") +func checkPermsStderr() (string, error) { + r, w, err := os.Pipe() if err != nil { - t.Fatalf("could not open /dev/stderr for reading: %s", err) + return "", err } - defer stderr.Close() - reader := bufio.NewReader(stderr) + stderr := os.Stderr + os.Stderr = w + defer func() { + os.Stderr = stderr + }() + + checkPerms() + w.Close() + + var text bytes.Buffer + io.Copy(&text, r) + return text.String(), nil +} + +func TestCheckPerms(t *testing.T) { tdir, err := ioutil.TempDir("", "helmtest") if err != nil { t.Fatal(err) @@ -51,8 +64,7 @@ func TestCheckPerms(t *testing.T) { settings.KubeConfig = tfile defer func() { settings.KubeConfig = tconfig }() - checkPerms() - text, err := reader.ReadString('\n') + text, err := checkPermsStderr() if err != nil { t.Fatalf("could not read from stderr: %s", err) } @@ -64,8 +76,7 @@ func TestCheckPerms(t *testing.T) { if err := fh.Chmod(0404); err != nil { t.Errorf("Could not change mode on file: %s", err) } - checkPerms() - text, err = reader.ReadString('\n') + text, err = checkPermsStderr() if err != nil { t.Fatalf("could not read from stderr: %s", err) } From e97975d7ad329779384d627d91e00b5d28041d0b Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Wed, 30 Sep 2020 16:04:21 +0800 Subject: [PATCH 281/281] Makefile: check and use GOBIN environment variable first 'go get' will install binaries into GOBIN when it's set which is not always same with GOPATH/bin this commit can fix below errors: ~/workspace/k8s/helm$ go env | grep -e GOPATH -e GOBIN -e GOROO GOBIN="/home/lizj/go/bin" GOPATH="/home/lizj/gosrc" GOROOT="/home/lizj/go" ~/workspace/k8s/helm$ make build-cross (cd /; GO111MODULE=on go get -u github.com/mitchellh/gox) go: github.com/mitchellh/gox upgrade => v1.0.1 go: github.com/hashicorp/go-version upgrade => v1.2.1 GO111MODULE=on CGO_ENABLED=0 /home/lizj/gosrc/bin/gox -parallel=3 -output="_dist/{{.OS}}-{{.Arch}}/helm" -osarch='darwin/amd64 linux/amd64 linux/386 linux/arm linux/arm64 linux/ppc64le linux/s390x windows/amd64' -tags '' -ldflags '-w -s -X helm.sh/helm/v3/internal/version.metadata=unreleased -X helm.sh/helm/v3/internal/version.gitCommit=59c77716ad61331da28c37e9430d5f6a3ab23fed -X helm.sh/helm/v3/internal/version.gitTreeState=dirty -extldflags "-static"' ./cmd/helm bash: /home/lizj/gosrc/bin/gox: No such file or directory Makefile:146: recipe for target 'build-cross' failed make: *** [build-cross] Error 127 Signed-off-by: Li Zhijian --- Makefile | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 79fc31976..931fe973d 100644 --- a/Makefile +++ b/Makefile @@ -5,9 +5,12 @@ TARGETS := darwin/amd64 linux/amd64 linux/386 linux/arm linux/arm64 linux/pp TARGET_OBJS ?= darwin-amd64.tar.gz darwin-amd64.tar.gz.sha256 darwin-amd64.tar.gz.sha256sum linux-amd64.tar.gz linux-amd64.tar.gz.sha256 linux-amd64.tar.gz.sha256sum linux-386.tar.gz linux-386.tar.gz.sha256 linux-386.tar.gz.sha256sum linux-arm.tar.gz linux-arm.tar.gz.sha256 linux-arm.tar.gz.sha256sum linux-arm64.tar.gz linux-arm64.tar.gz.sha256 linux-arm64.tar.gz.sha256sum linux-ppc64le.tar.gz linux-ppc64le.tar.gz.sha256 linux-ppc64le.tar.gz.sha256sum linux-s390x.tar.gz linux-s390x.tar.gz.sha256 linux-s390x.tar.gz.sha256sum windows-amd64.zip windows-amd64.zip.sha256 windows-amd64.zip.sha256sum BINNAME ?= helm -GOPATH = $(shell go env GOPATH) -GOX = $(GOPATH)/bin/gox -GOIMPORTS = $(GOPATH)/bin/goimports +GOBIN = $(shell go env GOBIN) +ifeq ($(GOBIN),) +GOBIN = $(shell go env GOPATH)/bin +endif +GOX = $(GOBIN)/gox +GOIMPORTS = $(GOBIN)/goimports ARCH = $(shell uname -p) ACCEPTANCE_DIR:=../acceptance-testing