diff --git a/Makefile b/Makefile index d228c5525..ec4911fb7 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,16 @@ BINDIR := $(CURDIR)/bin DIST_DIRS := find * -type d -exec -TARGETS := darwin/amd64 linux/amd64 linux/386 linux/arm linux/arm64 linux/ppc64le windows/amd64 -TARGET_OBJS ?= darwin-amd64.tar.gz darwin-amd64.tar.gz.sha256 linux-amd64.tar.gz linux-amd64.tar.gz.sha256 linux-386.tar.gz linux-386.tar.gz.sha256 linux-arm.tar.gz linux-arm.tar.gz.sha256 linux-arm64.tar.gz linux-arm64.tar.gz.sha256 linux-ppc64le.tar.gz linux-ppc64le.tar.gz.sha256 windows-amd64.zip windows-amd64.zip.sha256 +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 linux-amd64.tar.gz linux-amd64.tar.gz.sha256 linux-386.tar.gz linux-386.tar.gz.sha256 linux-arm.tar.gz linux-arm.tar.gz.sha256 linux-arm64.tar.gz linux-arm64.tar.gz.sha256 linux-ppc64le.tar.gz linux-ppc64le.tar.gz.sha256 linux-s390x.tar.gz linux-s390x.tar.gz.sha256 windows-amd64.zip windows-amd64.zip.sha256 BINNAME ?= helm GOPATH = $(shell go env GOPATH) DEP = $(GOPATH)/bin/dep GOX = $(GOPATH)/bin/gox GOIMPORTS = $(GOPATH)/bin/goimports +ARCH = $(shell uname -p) -ACCEPTANCE_DIR:=$(GOPATH)/src/helm.sh/acceptance-testing +ACCEPTANCE_DIR:=../acceptance-testing # To specify the subset of acceptance tests to run. '.' means all tests ACCEPTANCE_RUN_TESTS=. @@ -40,10 +41,13 @@ ifneq ($(BINARY_VERSION),) LDFLAGS += -X helm.sh/helm/v3/internal/version.version=${BINARY_VERSION} endif +VERSION_METADATA = unreleased # Clear the "unreleased" string in BuildMetadata ifneq ($(GIT_TAG),) - LDFLAGS += -X helm.sh/helm/v3/internal/version.metadata= + VERSION_METADATA = 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} @@ -64,7 +68,11 @@ $(BINDIR)/$(BINNAME): $(SRC) .PHONY: test test: build +ifeq ($(ARCH),s390x) +test: TESTFLAGS += -v +else test: TESTFLAGS += -race -v +endif test: test-style test: test-unit diff --git a/README.md b/README.md index 73fd189d7..745a60c2b 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Helm is a tool for managing Charts. Charts are packages of pre-configured Kubern Use Helm to: -- Find and use [popular software packaged as Helm Charts](https://github.com/helm/charts) to run in Kubernetes +- Find and use [popular software packaged as Helm Charts](https://hub.helm.sh) to run in Kubernetes - Share your own applications as Helm Charts - Create reproducible builds of your Kubernetes applications - Intelligently manage your Kubernetes manifest files diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..c84a6f866 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,3 @@ +# Helm Security Reporting and Policy + +The Helm project has [a common process and policy that can be found here](https://github.com/helm/community/blob/master/SECURITY.md). \ No newline at end of file diff --git a/cmd/helm/completion.go b/cmd/helm/completion.go index a44140d9f..1601cb448 100644 --- a/cmd/helm/completion.go +++ b/cmd/helm/completion.go @@ -139,7 +139,10 @@ __helm_compgen() { fi for w in "${completions[@]}"; do if [[ "${w}" = "$1"* ]]; then - echo "${w}" + # Use printf instead of echo beause it is possible that + # the value to print is -n, which would be interpreted + # as a flag to echo + printf "%s\n" "${w}" fi done } diff --git a/cmd/helm/flags.go b/cmd/helm/flags.go index 467abbd6e..65575a5c1 100644 --- a/cmd/helm/flags.go +++ b/cmd/helm/flags.go @@ -23,6 +23,7 @@ import ( "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" @@ -31,7 +32,7 @@ import ( const outputFlag = "output" func addValueOptionsFlags(f *pflag.FlagSet, v *values.Options) { - f.StringSliceVarP(&v.ValueFiles, "values", "f", []string{}, "specify values in a YAML file or a URL(can specify multiple)") + f.StringSliceVarP(&v.ValueFiles, "values", "f", []string{}, "specify values in a YAML file or a URL (can specify multiple)") f.StringArrayVar(&v.Values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)") f.StringArrayVar(&v.StringValues, "set-string", []string{}, "set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)") f.StringArrayVar(&v.FileValues, "set-file", []string{}, "set values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2)") @@ -52,10 +53,19 @@ 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) { - cmd.Flags().VarP(newOutputValue(output.Table, varRef), outputFlag, "o", + f := cmd.Flags() + flag := f.VarPF(newOutputValue(output.Table, varRef), outputFlag, "o", fmt.Sprintf("prints the output in the specified format. Allowed values: %s", strings.Join(output.Formats(), ", "))) - // Setup shell completion for the flag - cmd.MarkFlagCustom(outputFlag, "__helm_output_options") + + completion.RegisterFlagCompletionFunc(flag, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { + var formatNames []string + for _, format := range output.Formats() { + if strings.HasPrefix(format, toComplete) { + formatNames = append(formatNames, format) + } + } + return formatNames, completion.BashCompDirectiveDefault + }) } type outputValue output.Format diff --git a/cmd/helm/flags_test.go b/cmd/helm/flags_test.go new file mode 100644 index 000000000..d5576fe9f --- /dev/null +++ b/cmd/helm/flags_test.go @@ -0,0 +1,88 @@ +/* +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" + "testing" + + "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/release" + helmtime "helm.sh/helm/v3/pkg/time" +) + +func outputFlagCompletionTest(t *testing.T, cmdName string) { + releasesMockWithStatus := func(info *release.Info, hooks ...*release.Hook) []*release.Release { + info.LastDeployed = helmtime.Unix(1452902400, 0).UTC() + return []*release.Release{{ + Name: "athos", + Namespace: "default", + Info: info, + Chart: &chart.Chart{}, + Hooks: hooks, + }, { + Name: "porthos", + Namespace: "default", + Info: info, + Chart: &chart.Chart{}, + Hooks: hooks, + }, { + Name: "aramis", + Namespace: "default", + Info: info, + Chart: &chart.Chart{}, + Hooks: hooks, + }, { + Name: "dartagnan", + Namespace: "gascony", + Info: info, + Chart: &chart.Chart{}, + Hooks: hooks, + }} + } + + tests := []cmdTestCase{{ + name: "completion for output flag long and before arg", + cmd: fmt.Sprintf("__complete %s --output ''", cmdName), + golden: "output/output-comp.txt", + rels: releasesMockWithStatus(&release.Info{ + Status: release.StatusDeployed, + }), + }, { + name: "completion for output flag long and after arg", + cmd: fmt.Sprintf("__complete %s aramis --output ''", cmdName), + golden: "output/output-comp.txt", + rels: releasesMockWithStatus(&release.Info{ + Status: release.StatusDeployed, + }), + }, { + name: "completion for output flag short and before arg", + cmd: fmt.Sprintf("__complete %s -o ''", cmdName), + golden: "output/output-comp.txt", + rels: releasesMockWithStatus(&release.Info{ + Status: release.StatusDeployed, + }), + }, { + name: "completion for output flag short and after arg", + cmd: fmt.Sprintf("__complete %s aramis -o ''", cmdName), + golden: "output/output-comp.txt", + rels: releasesMockWithStatus(&release.Info{ + Status: release.StatusDeployed, + }), + }} + runTestCmd(t, tests) +} diff --git a/cmd/helm/get_all.go b/cmd/helm/get_all.go index 8e9ab4d6b..7d893d7e0 100644 --- a/cmd/helm/get_all.go +++ b/cmd/helm/get_all.go @@ -22,6 +22,7 @@ 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" ) @@ -56,8 +57,24 @@ 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) { + if len(args) == 1 { + return compListRevisions(cfg, args[0]) + } + return nil, completion.BashCompDirectiveNoFileComp + }) + f.StringVar(&template, "template", "", "go template for formatting the output, eg: {{.Release.Name}}") return cmd diff --git a/cmd/helm/get_all_test.go b/cmd/helm/get_all_test.go index 0b026fca4..a213ac583 100644 --- a/cmd/helm/get_all_test.go +++ b/cmd/helm/get_all_test.go @@ -41,3 +41,7 @@ func TestGetCmd(t *testing.T) { }} runTestCmd(t, tests) } + +func TestGetAllRevisionCompletion(t *testing.T) { + revisionFlagCompletionTest(t, "get all") +} diff --git a/cmd/helm/get_hooks.go b/cmd/helm/get_hooks.go index 0c50c8833..c2087b1ba 100644 --- a/cmd/helm/get_hooks.go +++ b/cmd/helm/get_hooks.go @@ -23,6 +23,7 @@ import ( "github.com/spf13/cobra" "helm.sh/helm/v3/cmd/helm/require" + "helm.sh/helm/v3/internal/completion" "helm.sh/helm/v3/pkg/action" ) @@ -52,7 +53,23 @@ func newGetHooksCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { }, } - cmd.Flags().IntVar(&client.Version, "revision", 0, "get the named release with revision") + // 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) { + if len(args) == 1 { + return compListRevisions(cfg, args[0]) + } + return nil, completion.BashCompDirectiveNoFileComp + }) return cmd } diff --git a/cmd/helm/get_hooks_test.go b/cmd/helm/get_hooks_test.go index f843f7d59..7b9142d12 100644 --- a/cmd/helm/get_hooks_test.go +++ b/cmd/helm/get_hooks_test.go @@ -36,3 +36,7 @@ func TestGetHooks(t *testing.T) { }} runTestCmd(t, tests) } + +func TestGetHooksRevisionCompletion(t *testing.T) { + revisionFlagCompletionTest(t, "get hooks") +} diff --git a/cmd/helm/get_manifest.go b/cmd/helm/get_manifest.go index d8fcd2e2c..f332befd9 100644 --- a/cmd/helm/get_manifest.go +++ b/cmd/helm/get_manifest.go @@ -23,6 +23,7 @@ import ( "github.com/spf13/cobra" "helm.sh/helm/v3/cmd/helm/require" + "helm.sh/helm/v3/internal/completion" "helm.sh/helm/v3/pkg/action" ) @@ -52,7 +53,23 @@ func newGetManifestCmd(cfg *action.Configuration, out io.Writer) *cobra.Command }, } - cmd.Flags().IntVar(&client.Version, "revision", 0, "get the named release with revision") + // 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) { + if len(args) == 1 { + return compListRevisions(cfg, args[0]) + } + return nil, completion.BashCompDirectiveNoFileComp + }) return cmd } diff --git a/cmd/helm/get_manifest_test.go b/cmd/helm/get_manifest_test.go index be54d4a5b..bd0ffc5d6 100644 --- a/cmd/helm/get_manifest_test.go +++ b/cmd/helm/get_manifest_test.go @@ -36,3 +36,7 @@ func TestGetManifest(t *testing.T) { }} runTestCmd(t, tests) } + +func TestGetManifestRevisionCompletion(t *testing.T) { + revisionFlagCompletionTest(t, "get manifest") +} diff --git a/cmd/helm/get_notes.go b/cmd/helm/get_notes.go index 1b0128989..4491bd9ba 100644 --- a/cmd/helm/get_notes.go +++ b/cmd/helm/get_notes.go @@ -23,6 +23,7 @@ import ( "github.com/spf13/cobra" "helm.sh/helm/v3/cmd/helm/require" + "helm.sh/helm/v3/internal/completion" "helm.sh/helm/v3/pkg/action" ) @@ -50,8 +51,23 @@ 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) { + if len(args) == 1 { + return compListRevisions(cfg, args[0]) + } + return nil, completion.BashCompDirectiveNoFileComp + }) return cmd } diff --git a/cmd/helm/get_notes_test.go b/cmd/helm/get_notes_test.go index a3ddea9a7..a59120b77 100644 --- a/cmd/helm/get_notes_test.go +++ b/cmd/helm/get_notes_test.go @@ -36,3 +36,7 @@ func TestGetNotesCmd(t *testing.T) { }} runTestCmd(t, tests) } + +func TestGetNotesRevisionCompletion(t *testing.T) { + revisionFlagCompletionTest(t, "get notes") +} diff --git a/cmd/helm/get_values.go b/cmd/helm/get_values.go index 2cccaeace..a8c5acc5e 100644 --- a/cmd/helm/get_values.go +++ b/cmd/helm/get_values.go @@ -23,6 +23,7 @@ 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" ) @@ -54,8 +55,23 @@ 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) { + if len(args) == 1 { + return compListRevisions(cfg, args[0]) + } + return nil, completion.BashCompDirectiveNoFileComp + }) f.BoolVarP(&client.AllValues, "all", "a", false, "dump all (computed) values") bindOutputFlag(cmd, &outfmt) diff --git a/cmd/helm/get_values_test.go b/cmd/helm/get_values_test.go index da3cc9e39..ecd92d354 100644 --- a/cmd/helm/get_values_test.go +++ b/cmd/helm/get_values_test.go @@ -52,3 +52,11 @@ func TestGetValuesCmd(t *testing.T) { }} runTestCmd(t, tests) } + +func TestGetValuesRevisionCompletion(t *testing.T) { + revisionFlagCompletionTest(t, "get values") +} + +func TestGetValuesOutputCompletion(t *testing.T) { + outputFlagCompletionTest(t, "get values") +} 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 { diff --git a/cmd/helm/helm_test.go b/cmd/helm/helm_test.go index 924e8e9d3..5f9d80a3a 100644 --- a/cmd/helm/helm_test.go +++ b/cmd/helm/helm_test.go @@ -18,6 +18,7 @@ package main import ( "bytes" + "fmt" "io/ioutil" "os" "os/exec" @@ -31,6 +32,7 @@ import ( "helm.sh/helm/v3/internal/test" "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/chartutil" + "helm.sh/helm/v3/pkg/cli" kubefake "helm.sh/helm/v3/pkg/kube/fake" "helm.sh/helm/v3/pkg/release" "helm.sh/helm/v3/pkg/storage" @@ -47,24 +49,26 @@ func init() { func runTestCmd(t *testing.T, tests []cmdTestCase) { t.Helper() for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - defer resetEnv()() - - storage := storageFixture() - for _, rel := range tt.rels { - if err := storage.Create(rel); err != nil { - t.Fatal(err) + for i := 0; i <= tt.repeat; i++ { + t.Run(tt.name, func(t *testing.T) { + defer resetEnv()() + + storage := storageFixture() + for _, rel := range tt.rels { + if err := storage.Create(rel); err != nil { + t.Fatal(err) + } } - } - t.Log("running cmd: ", tt.cmd) - _, out, err := executeActionCommandC(storage, tt.cmd) - if (err != nil) != tt.wantError { - t.Errorf("expected error, got '%v'", err) - } - if tt.golden != "" { - test.AssertGoldenString(t, out, tt.golden) - } - }) + t.Logf("running cmd (attempt %d): %s", i+1, tt.cmd) + _, out, err := executeActionCommandC(storage, tt.cmd) + if (err != nil) != tt.wantError { + t.Errorf("expected error, got '%v'", err) + } + if tt.golden != "" { + test.AssertGoldenString(t, out, tt.golden) + } + }) + } } } @@ -93,11 +97,39 @@ func storageFixture() *storage.Storage { return storage.Init(driver.NewMemory()) } +// go-shellwords does not handle empty arguments properly +// https://github.com/mattn/go-shellwords/issues/5#issuecomment-573431458 +// +// This method checks if the last argument was an empty one, +// and if go-shellwords missed it, we add it ourselves. +// +// This is important for completion tests as completion often +// uses an empty last parameter. +func checkLastEmpty(in string, out []string) []string { + lastIndex := len(in) - 1 + + if lastIndex >= 1 && (in[lastIndex] == '"' && in[lastIndex-1] == '"' || + in[lastIndex] == '\'' && in[lastIndex-1] == '\'') { + // The last parameter of 'in' was empty ("" or ''), let's make sure it was detected. + if len(out) > 0 && out[len(out)-1] != "" { + // Bug from go-shellwords: + // 'out' does not have the empty parameter. We add it ourselves as a workaround. + out = append(out, "") + } else { + fmt.Println("WARNING: go-shellwords seems to have been fixed. This workaround can be removed.") + } + } + return out +} + func executeActionCommandC(store *storage.Storage, cmd string) (*cobra.Command, string, error) { args, err := shellwords.Parse(cmd) if err != nil { return nil, "", err } + // Workaround the bug in shellwords + args = checkLastEmpty(cmd, args) + buf := new(bytes.Buffer) actionConfig := &action.Configuration{ @@ -124,6 +156,9 @@ type cmdTestCase struct { wantError bool // Rels are the available releases at the start of the test. rels []*release.Release + // Number of repeats (in case a feature was previously flaky and the test checks + // it's now stably producing identical results). 0 means test is run exactly once. + repeat int } func executeActionCommand(cmd string) (*cobra.Command, string, error) { @@ -131,14 +166,14 @@ func executeActionCommand(cmd string) (*cobra.Command, string, error) { } func resetEnv() func() { - origSettings, origEnv := settings, os.Environ() + origEnv := os.Environ() return func() { os.Clearenv() - settings = origSettings for _, pair := range origEnv { kv := strings.SplitN(pair, "=", 2) os.Setenv(kv[0], kv[1]) } + settings = cli.New() } } diff --git a/cmd/helm/history.go b/cmd/helm/history.go index aa873db1e..3ef542e58 100644 --- a/cmd/helm/history.go +++ b/cmd/helm/history.go @@ -19,12 +19,14 @@ package main import ( "fmt" "io" + "strconv" "time" "github.com/gosuri/uitable" "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" @@ -69,6 +71,14 @@ 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) @@ -176,3 +186,16 @@ func min(x, y int) int { } return y } + +func compListRevisions(cfg *action.Configuration, releaseName string) ([]string, completion.BashCompDirective) { + 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)) + } + return revisions, completion.BashCompDirectiveDefault + } + return nil, completion.BashCompDirectiveError +} diff --git a/cmd/helm/history_test.go b/cmd/helm/history_test.go index 3e750cefc..c9bfb648e 100644 --- a/cmd/helm/history_test.go +++ b/cmd/helm/history_test.go @@ -17,6 +17,7 @@ limitations under the License. package main import ( + "fmt" "testing" "helm.sh/helm/v3/pkg/release" @@ -68,3 +69,42 @@ func TestHistoryCmd(t *testing.T) { }} runTestCmd(t, tests) } + +func TestHistoryOutputCompletion(t *testing.T) { + outputFlagCompletionTest(t, "history") +} + +func revisionFlagCompletionTest(t *testing.T, cmdName string) { + 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), + } + + tests := []cmdTestCase{{ + name: "completion for revision flag", + cmd: fmt.Sprintf("__complete %s musketeers --revision ''", cmdName), + rels: releases, + golden: "output/revision-comp.txt", + }, { + name: "completion for revision flag with too few args", + cmd: fmt.Sprintf("__complete %s --revision ''", cmdName), + rels: releases, + golden: "output/revision-wrong-args-comp.txt", + }, { + name: "completion for revision flag with too many args", + cmd: fmt.Sprintf("__complete %s three musketeers --revision ''", cmdName), + rels: releases, + golden: "output/revision-wrong-args-comp.txt", + }} + runTestCmd(t, tests) +} diff --git a/cmd/helm/install.go b/cmd/helm/install.go index b75dfce74..dbdfb3418 100644 --- a/cmd/helm/install.go +++ b/cmd/helm/install.go @@ -26,6 +26,7 @@ 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" @@ -122,6 +123,11 @@ 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) @@ -139,6 +145,7 @@ func addInstallFlags(f *pflag.FlagSet, client *action.Install, valueOpts *values f.StringVar(&client.Description, "description", "", "add a custom description") 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.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") @@ -225,3 +232,15 @@ func isChartInstallable(ch *chart.Chart) (bool, error) { } return false, errors.Errorf("%s charts are not installable", ch.Metadata.Type) } + +// Provide dynamic auto-completion for the install and template commands +func compInstall(args []string, toComplete string, client *action.Install) ([]string, completion.BashCompDirective) { + requiredArgs := 1 + if client.GenerateName { + requiredArgs = 0 + } + if len(args) == requiredArgs { + return compListCharts(toComplete, true) + } + return nil, completion.BashCompDirectiveNoFileComp +} diff --git a/cmd/helm/install_test.go b/cmd/helm/install_test.go index e8b573dfc..57972024f 100644 --- a/cmd/helm/install_test.go +++ b/cmd/helm/install_test.go @@ -193,3 +193,7 @@ func TestInstall(t *testing.T) { runTestActionCmd(t, tests) } + +func TestInstallOutputCompletion(t *testing.T) { + outputFlagCompletionTest(t, "install") +} diff --git a/cmd/helm/list.go b/cmd/helm/list.go index 0da0c21d3..4b652088d 100644 --- a/cmd/helm/list.go +++ b/cmd/helm/list.go @@ -26,13 +26,14 @@ 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" ) var listHelp = ` -This command lists all of the releases. +This command lists all of the releases for a specified namespace (uses current namespace context if namespace not specified). By default, it lists only releases that are deployed or failed. Flags like '--uninstalled' and '--all' will alter this behavior. Such flags can be combined: @@ -96,7 +97,7 @@ func newListCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { f.BoolVarP(&client.Short, "short", "q", false, "output short (quiet) listing format") f.BoolVarP(&client.ByDate, "date", "d", false, "sort by release date") f.BoolVarP(&client.SortReverse, "reverse", "r", false, "reverse the sort order") - f.BoolVarP(&client.All, "all", "a", false, "show all releases, not just the ones marked deployed or failed") + f.BoolVarP(&client.All, "all", "a", false, "show all releases without any filter applied") f.BoolVar(&client.Uninstalled, "uninstalled", false, "show uninstalled releases (if 'helm uninstall --keep-history' was used)") f.BoolVar(&client.Superseded, "superseded", false, "show superseded releases") f.BoolVar(&client.Uninstalling, "uninstalling", false, "show releases that are currently being uninstalled") @@ -164,3 +165,26 @@ func (r *releaseListWriter) WriteJSON(out io.Writer) error { func (r *releaseListWriter) WriteYAML(out io.Writer) error { return output.EncodeYAML(out, r.releases) } + +// 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)) + + client := action.NewList(cfg) + client.All = true + client.Limit = 0 + client.Filter = fmt.Sprintf("^%s", toComplete) + + client.SetStateMask() + results, err := client.Run() + if err != nil { + return nil, completion.BashCompDirectiveDefault + } + + var choices []string + for _, res := range results { + choices = append(choices, res.Name) + } + + return choices, completion.BashCompDirectiveNoFileComp +} diff --git a/cmd/helm/list_test.go b/cmd/helm/list_test.go index b5833fd72..127a8a980 100644 --- a/cmd/helm/list_test.go +++ b/cmd/helm/list_test.go @@ -206,3 +206,7 @@ func TestListCmd(t *testing.T) { }} runTestCmd(t, tests) } + +func TestListOutputCompletion(t *testing.T) { + outputFlagCompletionTest(t, "list") +} diff --git a/cmd/helm/package.go b/cmd/helm/package.go index 9f7961f95..00fe0ef11 100644 --- a/cmd/helm/package.go +++ b/cmd/helm/package.go @@ -20,6 +20,7 @@ import ( "fmt" "io" "io/ioutil" + "os" "path/filepath" "github.com/pkg/errors" @@ -80,6 +81,9 @@ func newPackageCmd(out io.Writer) *cobra.Command { if err != nil { return err } + if _, err := os.Stat(args[i]); err != nil { + return err + } if client.DependencyUpdate { downloadManager := &downloader.Manager{ @@ -114,7 +118,6 @@ func newPackageCmd(out io.Writer) *cobra.Command { 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.") f.BoolVarP(&client.DependencyUpdate, "dependency-update", "u", false, `update dependencies from "Chart.yaml" to dir "charts/" before packaging`) - addValueOptionsFlags(f, valueOpts) return cmd } diff --git a/cmd/helm/package_test.go b/cmd/helm/package_test.go index 739857fa9..e0a5fabd6 100644 --- a/cmd/helm/package_test.go +++ b/cmd/helm/package_test.go @@ -17,13 +17,9 @@ package main import ( "bytes" - "fmt" - "io/ioutil" "os" "path/filepath" "regexp" - "runtime" - "strings" "testing" "github.com/spf13/cobra" @@ -31,15 +27,9 @@ import ( "helm.sh/helm/v3/internal/test/ensure" "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/chart/loader" - "helm.sh/helm/v3/pkg/chartutil" ) func TestPackage(t *testing.T) { - statFileMsg := "no such file or directory" - if runtime.GOOS == "windows" { - statFileMsg = "The system cannot find the file specified." - } - tests := []struct { name string flags map[string]string @@ -108,13 +98,6 @@ func TestPackage(t *testing.T) { hasfile: "chart-missing-deps-0.1.0.tgz", err: true, }, - { - name: "package --values does-not-exist", - args: []string{"testdata/testcharts/alpine"}, - flags: map[string]string{"values": "does-not-exist"}, - expect: fmt.Sprintf("does-not-exist: %s", statFileMsg), - err: true, - }, { name: "package testdata/testcharts/chart-bad-type", args: []string{"testdata/testcharts/chart-bad-type"}, @@ -213,124 +196,6 @@ func TestSetAppVersion(t *testing.T) { } } -func TestPackageValues(t *testing.T) { - defer resetEnv()() - - repoFile := "testdata/helmhome/helm/repositories.yaml" - - testCases := []struct { - desc string - args []string - valuefilesContents []string - flags map[string]string - expected []string - }{ - { - desc: "helm package, single values file", - args: []string{"testdata/testcharts/alpine"}, - flags: map[string]string{"repository-config": repoFile}, - valuefilesContents: []string{"Name: chart-name-foo"}, - expected: []string{"Name: chart-name-foo"}, - }, - { - desc: "helm package, multiple values files", - args: []string{"testdata/testcharts/alpine"}, - flags: map[string]string{"repository-config": repoFile}, - valuefilesContents: []string{"Name: chart-name-foo", "foo: bar"}, - expected: []string{"Name: chart-name-foo", "foo: bar"}, - }, - { - desc: "helm package, with set option", - args: []string{"testdata/testcharts/alpine"}, - flags: map[string]string{"set": "Name=chart-name-foo", "repository-config": repoFile}, - expected: []string{"Name: chart-name-foo"}, - }, - { - desc: "helm package, set takes precedence over value file", - args: []string{"testdata/testcharts/alpine"}, - valuefilesContents: []string{"Name: chart-name-foo"}, - flags: map[string]string{"set": "Name=chart-name-bar", "repository-config": repoFile}, - expected: []string{"Name: chart-name-bar"}, - }, - } - - for _, tc := range testCases { - var files []string - for _, contents := range tc.valuefilesContents { - f := createValuesFile(t, contents) - files = append(files, f) - } - valueFiles := strings.Join(files, ",") - - expected, err := chartutil.ReadValues([]byte(strings.Join(tc.expected, "\n"))) - if err != nil { - t.Errorf("unexpected error parsing values: %q", err) - } - - outputDir := ensure.TempDir(t) - - if len(tc.flags) == 0 { - tc.flags = make(map[string]string) - } - tc.flags["destination"] = outputDir - - if len(valueFiles) > 0 { - tc.flags["values"] = valueFiles - } - - cmd := newPackageCmd(&bytes.Buffer{}) - setFlags(cmd, tc.flags) - if err := cmd.RunE(cmd, tc.args); err != nil { - t.Fatalf("unexpected error: %q", err) - } - - outputFile := filepath.Join(outputDir, "alpine-0.1.0.tgz") - verifyOutputChartExists(t, outputFile) - - actual, err := getChartValues(outputFile) - if err != nil { - t.Fatalf("unexpected error extracting chart values: %q", err) - } - - verifyValues(t, actual, expected) - } -} - -func createValuesFile(t *testing.T, data string) string { - outputDir := ensure.TempDir(t) - - outputFile := filepath.Join(outputDir, "values.yaml") - if err := ioutil.WriteFile(outputFile, []byte(data), 0644); err != nil { - t.Fatalf("err: %s", err) - } - return outputFile -} - -func getChartValues(chartPath string) (chartutil.Values, error) { - chart, err := loader.Load(chartPath) - if err != nil { - return nil, err - } - return chart.Values, nil -} - -func verifyValues(t *testing.T, actual, expected chartutil.Values) { - t.Helper() - for key, value := range expected.AsMap() { - if got := actual[key]; got != value { - t.Errorf("Expected %q, got %q (%v)", value, got, actual) - } - } -} - -func verifyOutputChartExists(t *testing.T, chartPath string) { - if chartFile, err := os.Stat(chartPath); err != nil { - t.Errorf("expected file %q, got err %q", chartPath, err) - } else if chartFile.Size() == 0 { - t.Errorf("file %q has zero bytes.", chartPath) - } -} - func setFlags(cmd *cobra.Command, flags map[string]string) { dest := cmd.Flags() for f, v := range flags { diff --git a/cmd/helm/plugin_list.go b/cmd/helm/plugin_list.go index 2f37a8028..0440b0b5e 100644 --- a/cmd/helm/plugin_list.go +++ b/cmd/helm/plugin_list.go @@ -18,6 +18,7 @@ package main import ( "fmt" "io" + "strings" "github.com/gosuri/uitable" "github.com/spf13/cobra" @@ -46,3 +47,17 @@ func newPluginListCmd(out io.Writer) *cobra.Command { } return cmd } + +// Provide dynamic auto-completion for plugin names +func compListPlugins(toComplete string) []string { + var pNames []string + plugins, err := findPlugins(settings.PluginsDirectory) + if err == nil { + for _, p := range plugins { + if strings.HasPrefix(p.Metadata.Name, toComplete) { + pNames = append(pNames, p.Metadata.Name) + } + } + } + return pNames +} diff --git a/cmd/helm/plugin_uninstall.go b/cmd/helm/plugin_uninstall.go index 30f9bc91d..f703ddcfb 100644 --- a/cmd/helm/plugin_uninstall.go +++ b/cmd/helm/plugin_uninstall.go @@ -24,6 +24,7 @@ import ( "github.com/pkg/errors" "github.com/spf13/cobra" + "helm.sh/helm/v3/internal/completion" "helm.sh/helm/v3/pkg/plugin" ) @@ -33,6 +34,7 @@ type pluginUninstallOptions struct { func newPluginUninstallCmd(out io.Writer) *cobra.Command { o := &pluginUninstallOptions{} + cmd := &cobra.Command{ Use: "uninstall ...", Aliases: []string{"rm", "remove"}, @@ -44,6 +46,15 @@ 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 64e8bd6c7..a24e80518 100644 --- a/cmd/helm/plugin_update.go +++ b/cmd/helm/plugin_update.go @@ -24,6 +24,7 @@ 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" ) @@ -34,6 +35,7 @@ type pluginUpdateOptions struct { func newPluginUpdateCmd(out io.Writer) *cobra.Command { o := &pluginUpdateOptions{} + cmd := &cobra.Command{ Use: "update ...", Aliases: []string{"up"}, @@ -45,6 +47,15 @@ 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 3b00e9bca..16cd10467 100644 --- a/cmd/helm/pull.go +++ b/cmd/helm/pull.go @@ -23,6 +23,7 @@ import ( "github.com/spf13/cobra" "helm.sh/helm/v3/cmd/helm/require" + "helm.sh/helm/v3/internal/completion" "helm.sh/helm/v3/pkg/action" ) @@ -68,6 +69,14 @@ 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/pull_test.go b/cmd/helm/pull_test.go index 248195774..1aca66100 100644 --- a/cmd/helm/pull_test.go +++ b/cmd/helm/pull_test.go @@ -41,7 +41,10 @@ func TestPullCmd(t *testing.T) { tests := []struct { name string args string + existFile string + existDir string wantError bool + wantErrorMsg string failExpect string expectFile string expectDir bool @@ -87,10 +90,24 @@ func TestPullCmd(t *testing.T) { expectFile: "./signtest", expectDir: true, }, + { + name: "Fetch untar when file with same name existed", + args: "test/test1 --untar --untardir test1", + existFile: "test1", + wantError: true, + wantErrorMsg: fmt.Sprintf("failed to untar: a file or directory with the name %s already exists", filepath.Join(srv.Root(), "test1")), + }, + { + name: "Fetch untar when dir with same name existed", + args: "test/test2 --untar --untardir test2", + existDir: "test2", + wantError: true, + wantErrorMsg: fmt.Sprintf("failed to untar: a file or directory with the name %s already exists", filepath.Join(srv.Root(), "test2")), + }, { name: "Fetch, verify, untar", - args: "test/signtest --verify --keyring=testdata/helm-test-key.pub --untar --untardir signtest", - expectFile: "./signtest", + args: "test/signtest --verify --keyring=testdata/helm-test-key.pub --untar --untardir signtest2", + expectFile: "./signtest2", expectDir: true, expectVerify: true, }, @@ -127,9 +144,27 @@ func TestPullCmd(t *testing.T) { filepath.Join(outdir, "repositories.yaml"), outdir, ) + // Create file or Dir before helm pull --untar, see: https://github.com/helm/helm/issues/7182 + if tt.existFile != "" { + file := filepath.Join(outdir, tt.existFile) + _, err := os.Create(file) + if err != nil { + t.Fatal("err") + } + } + if tt.existDir != "" { + file := filepath.Join(outdir, tt.existDir) + err := os.Mkdir(file, 0755) + if err != nil { + t.Fatal(err) + } + } _, out, err := executeActionCommand(cmd) if err != nil { if tt.wantError { + if tt.wantErrorMsg != "" && tt.wantErrorMsg == err.Error() { + t.Fatalf("Actual error %s, not equal to expected error %s", err, tt.wantErrorMsg) + } return } t.Fatalf("%q reported error: %s", tt.name, err) diff --git a/cmd/helm/release_testing.go b/cmd/helm/release_testing.go index 7190ec736..e4690b9d4 100644 --- a/cmd/helm/release_testing.go +++ b/cmd/helm/release_testing.go @@ -24,6 +24,7 @@ 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" ) @@ -71,6 +72,14 @@ 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_add_test.go b/cmd/helm/repo_add_test.go index 1ac61fba8..d1b9bc0fc 100644 --- a/cmd/helm/repo_add_test.go +++ b/cmd/helm/repo_add_test.go @@ -19,13 +19,16 @@ package main import ( "fmt" "io/ioutil" + "os" "path/filepath" "sync" "testing" - "gopkg.in/yaml.v2" + "sigs.k8s.io/yaml" "helm.sh/helm/v3/internal/test/ensure" + "helm.sh/helm/v3/pkg/helmpath" + "helm.sh/helm/v3/pkg/helmpath/xdg" "helm.sh/helm/v3/pkg/repo" "helm.sh/helm/v3/pkg/repo/repotest" ) @@ -55,7 +58,8 @@ func TestRepoAdd(t *testing.T) { } defer ts.Stop() - repoFile := filepath.Join(ensure.TempDir(t), "repositories.yaml") + rootDir := ensure.TempDir(t) + repoFile := filepath.Join(rootDir, "repositories.yaml") const testRepoName = "test-name" @@ -65,6 +69,7 @@ func TestRepoAdd(t *testing.T) { noUpdate: true, repoFile: repoFile, } + os.Setenv(xdg.CacheHomeEnvVar, rootDir) if err := o.run(ioutil.Discard); err != nil { t.Error(err) @@ -79,6 +84,15 @@ func TestRepoAdd(t *testing.T) { t.Errorf("%s was not successfully inserted into %s", testRepoName, repoFile) } + idx := filepath.Join(helmpath.CachePath("repository"), helmpath.CacheIndexFile(testRepoName)) + if _, err := os.Stat(idx); os.IsNotExist(err) { + t.Errorf("Error cache index file was not created for repository %s", testRepoName) + } + idx = filepath.Join(helmpath.CachePath("repository"), helmpath.CacheChartsFile(testRepoName)) + if _, err := os.Stat(idx); os.IsNotExist(err) { + t.Errorf("Error cache charts file was not created for repository %s", testRepoName) + } + o.noUpdate = false if err := o.run(ioutil.Discard); err != nil { diff --git a/cmd/helm/repo_index_test.go b/cmd/helm/repo_index_test.go index 51c5db80a..e04ae1b59 100644 --- a/cmd/helm/repo_index_test.go +++ b/cmd/helm/repo_index_test.go @@ -119,7 +119,7 @@ func TestRepoIndexCmd(t *testing.T) { t.Error(err) } - _, err = repo.LoadIndexFile(destIndex) + index, err = repo.LoadIndexFile(destIndex) if err != nil { t.Fatal(err) } @@ -130,8 +130,8 @@ func TestRepoIndexCmd(t *testing.T) { } vs = index.Entries["compressedchart"] - if len(vs) != 3 { - t.Errorf("expected 3 versions, got %d: %#v", len(vs), vs) + if len(vs) != 1 { + t.Errorf("expected 1 versions, got %d: %#v", len(vs), vs) } expectedVersion = "0.3.0" diff --git a/cmd/helm/repo_list.go b/cmd/helm/repo_list.go index 2ff6162d1..25316bafc 100644 --- a/cmd/helm/repo_list.go +++ b/cmd/helm/repo_list.go @@ -18,6 +18,7 @@ package main import ( "io" + "strings" "github.com/gosuri/uitable" "github.com/pkg/errors" @@ -95,3 +96,18 @@ func (r *repoListWriter) encodeByFormat(out io.Writer, format output.Format) err // WriteJSON and WriteYAML, we shouldn't get invalid types return nil } + +// Provide dynamic auto-completion for repo names +func compListRepos(prefix string) []string { + var rNames []string + + f, err := repo.LoadFile(settings.RepositoryConfig) + if err == nil && len(f.Repositories) > 0 { + for _, repo := range f.Repositories { + if strings.HasPrefix(repo.Name, prefix) { + rNames = append(rNames, repo.Name) + } + } + } + return rNames +} diff --git a/cmd/helm/repo_list_test.go b/cmd/helm/repo_list_test.go new file mode 100644 index 000000000..f371452f2 --- /dev/null +++ b/cmd/helm/repo_list_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 TestRepoListOutputCompletion(t *testing.T) { + outputFlagCompletionTest(t, "repo list") +} diff --git a/cmd/helm/repo_remove.go b/cmd/helm/repo_remove.go index c1ecb1829..e8c0ec027 100644 --- a/cmd/helm/repo_remove.go +++ b/cmd/helm/repo_remove.go @@ -26,6 +26,7 @@ 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" ) @@ -38,6 +39,7 @@ type repoRemoveOptions struct { func newRepoRemoveCmd(out io.Writer) *cobra.Command { o := &repoRemoveOptions{} + cmd := &cobra.Command{ Use: "remove [NAME]", Aliases: []string{"rm"}, @@ -51,6 +53,14 @@ func newRepoRemoveCmd(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 compListRepos(toComplete), completion.BashCompDirectiveNoFileComp + }) + return cmd } @@ -76,7 +86,12 @@ func (o *repoRemoveOptions) run(out io.Writer) error { } func removeRepoCache(root, name string) error { - idx := filepath.Join(root, helmpath.CacheIndexFile(name)) + idx := filepath.Join(root, helmpath.CacheChartsFile(name)) + if _, err := os.Stat(idx); err == nil { + os.Remove(idx) + } + + idx = filepath.Join(root, helmpath.CacheIndexFile(name)) if _, err := os.Stat(idx); os.IsNotExist(err) { return nil } else if err != nil { diff --git a/cmd/helm/repo_remove_test.go b/cmd/helm/repo_remove_test.go index 3fdcbe9c3..85c76bb92 100644 --- a/cmd/helm/repo_remove_test.go +++ b/cmd/helm/repo_remove_test.go @@ -63,10 +63,13 @@ func TestRepoRemove(t *testing.T) { } 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() + b.Reset() if err := rmOpts.run(b); err != nil { @@ -77,7 +80,11 @@ func TestRepoRemove(t *testing.T) { } if _, err := os.Stat(idx); err == nil { - t.Errorf("Error cache file was not removed for repository %s", testRepoName) + 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) } f, err := repo.LoadFile(repoFile) diff --git a/cmd/helm/rollback.go b/cmd/helm/rollback.go index d44ef14f4..745e910b2 100644 --- a/cmd/helm/rollback.go +++ b/cmd/helm/rollback.go @@ -25,6 +25,7 @@ import ( "github.com/spf13/cobra" "helm.sh/helm/v3/cmd/helm/require" + "helm.sh/helm/v3/internal/completion" "helm.sh/helm/v3/pkg/action" ) @@ -64,6 +65,14 @@ 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 443d718d5..6ce1dcbf4 100644 --- a/cmd/helm/root.go +++ b/cmd/helm/root.go @@ -23,55 +23,16 @@ import ( "github.com/spf13/cobra" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "helm.sh/helm/v3/cmd/helm/require" + "helm.sh/helm/v3/internal/completion" "helm.sh/helm/v3/internal/experimental/registry" "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/cli/output" ) const ( - bashCompletionFunc = ` -__helm_override_flag_list=(--kubeconfig --kube-context --namespace -n) -__helm_override_flags() -{ - local ${__helm_override_flag_list[*]##*-} two_word_of of var - for w in "${words[@]}"; do - if [ -n "${two_word_of}" ]; then - eval "${two_word_of##*-}=\"${two_word_of}=\${w}\"" - two_word_of= - continue - fi - for of in "${__helm_override_flag_list[@]}"; do - case "${w}" in - ${of}=*) - eval "${of##*-}=\"${w}\"" - ;; - ${of}) - two_word_of="${of}" - ;; - esac - done - done - for var in "${__helm_override_flag_list[@]##*-}"; do - if eval "test -n \"\$${var}\""; then - eval "echo \${${var}}" - fi - done -} - -__helm_override_flags_to_kubectl_flags() -{ - # --kubeconfig, -n, --namespace stay the same for kubectl - # --kube-context becomes --context for kubectl - __helm_debug "${FUNCNAME[0]}: flags to convert: $1" - echo "$1" | \sed s/kube-context/context/ -} - -__helm_get_repos() -{ - eval $(__helm_binary_name) repo list 2>/dev/null | \tail +2 | \cut -f1 -} - + contextCompFunc = ` __helm_get_contexts() { __helm_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" @@ -81,219 +42,6 @@ __helm_get_contexts() COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) ) fi } - -__helm_get_namespaces() -{ - __helm_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" - local template out - template="{{ range .items }}{{ .metadata.name }} {{ end }}" - - flags=$(__helm_override_flags_to_kubectl_flags "$(__helm_override_flags)") - __helm_debug "${FUNCNAME[0]}: override flags for kubectl are: $flags" - - # Must use eval in case the flags contain a variable such as $HOME - if out=$(eval kubectl get ${flags} -o template --template=\"${template}\" namespace 2>/dev/null); then - COMPREPLY+=( $( compgen -W "${out[*]}" -- "$cur" ) ) - fi -} - -__helm_output_options() -{ - __helm_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" - COMPREPLY+=( $( compgen -W "%[1]s" -- "$cur" ) ) -} - -__helm_binary_name() -{ - local helm_binary - helm_binary="${words[0]}" - __helm_debug "${FUNCNAME[0]}: helm_binary is ${helm_binary}" - echo ${helm_binary} -} - -# This function prevents the zsh shell from adding a space after -# a completion by adding a second, fake completion -__helm_zsh_comp_nospace() { - __helm_debug "${FUNCNAME[0]}: in is ${in[*]}" - - local out in=("$@") - - # The shell will normally add a space after these completions. - # To avoid that we should use "compopt -o nospace". However, it is not - # available in zsh. - # Instead, we trick the shell by pretending there is a second, longer match. - # We only do this if there is a single choice left for completion - # to reduce the times the user could be presented with the fake completion choice. - - out=($(echo ${in[*]} | \tr " " "\n" | \grep "^${cur}")) - __helm_debug "${FUNCNAME[0]}: out is ${out[*]}" - - [ ${#out[*]} -eq 1 ] && out+=("${out}.") - - __helm_debug "${FUNCNAME[0]}: out is now ${out[*]}" - - echo "${out[*]}" -} - -# $1 = 1 if the completion should include local charts (which means file completion) -__helm_list_charts() -{ - __helm_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" - local repo url file out=() nospace=0 wantFiles=$1 - - # Handle completions for repos - for repo in $(__helm_get_repos); do - if [[ "${cur}" =~ ^${repo}/.* ]]; then - # We are doing completion from within a repo - out=$(eval $(__helm_binary_name) search repo ${cur} 2>/dev/null | \cut -f1 | \grep ^${cur}) - nospace=0 - elif [[ ${repo} =~ ^${cur}.* ]]; then - # We are completing a repo name - out+=(${repo}/) - nospace=1 - fi - done - __helm_debug "${FUNCNAME[0]}: out after repos is ${out[*]}" - - # Handle completions for url prefixes - for url in https:// http:// file://; do - if [[ "${cur}" =~ ^${url}.* ]]; then - # The user already put in the full url prefix. Return it - # back as a completion to avoid the shell doing path completion - out="${cur}" - nospace=1 - elif [[ ${url} =~ ^${cur}.* ]]; then - # We are completing a url prefix - out+=(${url}) - nospace=1 - fi - done - __helm_debug "${FUNCNAME[0]}: out after urls is ${out[*]}" - - # Handle completion for files. - # We only do this if: - # 1- There are other completions found (if there are no completions, - # the shell will do file completion itself) - # 2- If there is some input from the user (or else we will end up - # listing the entire content of the current directory which will - # be too many choices for the user to find the real repos) - if [ $wantFiles -eq 1 ] && [ -n "${out[*]}" ] && [ -n "${cur}" ]; then - for file in $(\ls); do - if [[ ${file} =~ ^${cur}.* ]]; then - # We are completing a file prefix - out+=(${file}) - nospace=1 - fi - done - fi - __helm_debug "${FUNCNAME[0]}: out after files is ${out[*]}" - - # If the user didn't provide any input to completion, - # we provide a hint that a path can also be used - [ $wantFiles -eq 1 ] && [ -z "${cur}" ] && out+=(./ /) - - __helm_debug "${FUNCNAME[0]}: out after checking empty input is ${out[*]}" - - if [ $nospace -eq 1 ]; then - if [[ -n "${ZSH_VERSION}" ]]; then - # Don't let the shell add a space after the completion - local tmpout=$(__helm_zsh_comp_nospace "${out[@]}") - unset out - out=$tmpout - elif [[ $(type -t compopt) = "builtin" ]]; then - compopt -o nospace - fi - fi - - __helm_debug "${FUNCNAME[0]}: final out is ${out[*]}" - COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) ) -} - -__helm_list_releases() -{ - __helm_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" - local out filter - # Use ^ to map from the start of the release name - filter="^${words[c]}" - # Use eval in case helm_binary_name or __helm_override_flags contains a variable (e.g., $HOME/bin/h3) - if out=$(eval $(__helm_binary_name) list $(__helm_override_flags) -a -q -m 1000 -f ${filter} 2>/dev/null); then - COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) ) - fi -} - -__helm_list_repos() -{ - __helm_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" - local out - # Use eval in case helm_binary_name contains a variable (e.g., $HOME/bin/h3) - if out=$(__helm_get_repos); then - COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) ) - fi -} - -__helm_list_plugins() -{ - __helm_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" - local out - # Use eval in case helm_binary_name contains a variable (e.g., $HOME/bin/h3) - if out=$(eval $(__helm_binary_name) plugin list 2>/dev/null | \tail +2 | \cut -f1); then - COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) ) - fi -} - -__helm_list_charts_after_name() { - __helm_debug "${FUNCNAME[0]}: last_command is $last_command" - if [[ ${#nouns[@]} -eq 1 ]]; then - __helm_list_charts 1 - fi -} - -__helm_list_releases_then_charts() { - __helm_debug "${FUNCNAME[0]}: last_command is $last_command" - if [[ ${#nouns[@]} -eq 0 ]]; then - __helm_list_releases - elif [[ ${#nouns[@]} -eq 1 ]]; then - __helm_list_charts 1 - fi -} - -__helm_custom_func() -{ - __helm_debug "${FUNCNAME[0]}: last_command is $last_command" - case ${last_command} in - helm_pull) - __helm_list_charts 0 - return - ;; - helm_show_*) - __helm_list_charts 1 - return - ;; - helm_install | helm_template) - __helm_list_charts_after_name - return - ;; - helm_upgrade) - __helm_list_releases_then_charts - return - ;; - helm_uninstall | helm_history | helm_status | helm_test |\ - helm_rollback | helm_get_*) - __helm_list_releases - return - ;; - helm_repo_remove) - __helm_list_repos - return - ;; - helm_plugin_uninstall | helm_plugin_update) - __helm_list_plugins - return - ;; - *) - ;; - esac -} ` ) @@ -301,7 +49,8 @@ var ( // Mapping of global flags that can have dynamic completion and the // completion function to be used. bashCompletionFlags = map[string]string{ - "namespace": "__helm_get_namespaces", + // Cannot convert the kube-context flag to Go completion yet because + // an incomplete kube-context will make actionConfig.Init() fail at the very start "kube-context": "__helm_get_contexts", } ) @@ -352,12 +101,34 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string Long: globalUsage, SilenceUsage: true, Args: require.NoArgs, - BashCompletionFunction: fmt.Sprintf(bashCompletionFunc, strings.Join(output.Formats(), " ")), + BashCompletionFunction: fmt.Sprintf("%s%s", contextCompFunc, completion.GetBashCustomFunction()), } flags := cmd.PersistentFlags() settings.AddFlags(flags) + flag := flags.Lookup("namespace") + // Setup shell completion for the namespace flag + completion.RegisterFlagCompletionFunc(flag, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { + 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)) + + nsNames := []string{} + if namespaces, err := client.CoreV1().Namespaces().List(metav1.ListOptions{TimeoutSeconds: &to}); err == nil { + for _, ns := range namespaces.Items { + if strings.HasPrefix(ns.Name, toComplete) { + nsNames = append(nsNames, ns.Name) + } + } + return nsNames, completion.BashCompDirectiveNoFileComp + } + } + return nil, completion.BashCompDirectiveDefault + }) + // 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 @@ -397,6 +168,9 @@ 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 annotation to flags for which we can generate completion choices diff --git a/cmd/helm/root_test.go b/cmd/helm/root_test.go index f3eef8b6d..df592a96d 100644 --- a/cmd/helm/root_test.go +++ b/cmd/helm/root_test.go @@ -35,23 +35,23 @@ func TestRootCmd(t *testing.T) { }{ { name: "defaults", - args: "home", + args: "env", }, { name: "with $XDG_CACHE_HOME set", - args: "home", + args: "env", envvars: map[string]string{xdg.CacheHomeEnvVar: "/bar"}, cachePath: "/bar/helm", }, { name: "with $XDG_CONFIG_HOME set", - args: "home", + args: "env", envvars: map[string]string{xdg.ConfigHomeEnvVar: "/bar"}, configPath: "/bar/helm", }, { name: "with $XDG_DATA_HOME set", - args: "home", + args: "env", envvars: map[string]string{xdg.DataHomeEnvVar: "/bar"}, dataPath: "/bar/helm", }, diff --git a/cmd/helm/search_hub_test.go b/cmd/helm/search_hub_test.go index dfe0cacc2..7b0f3a389 100644 --- a/cmd/helm/search_hub_test.go +++ b/cmd/helm/search_hub_test.go @@ -49,5 +49,8 @@ func TestSearchHubCmd(t *testing.T) { t.Log(out) t.Log(expected) } +} +func TestSearchHubOutputCompletion(t *testing.T) { + outputFlagCompletionTest(t, "search hub") } diff --git a/cmd/helm/search_repo.go b/cmd/helm/search_repo.go index 063a72a27..9f5af1e3c 100644 --- a/cmd/helm/search_repo.go +++ b/cmd/helm/search_repo.go @@ -17,8 +17,11 @@ limitations under the License. package main import ( + "bufio" + "bytes" "fmt" "io" + "io/ioutil" "path/filepath" "strings" @@ -28,6 +31,7 @@ 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" @@ -246,3 +250,137 @@ func (r *repoSearchWriter) encodeByFormat(out io.Writer, format output.Format) e // WriteJSON and WriteYAML, we shouldn't get invalid types return nil } + +// Provides the list of charts that are part of the specified repo, and that starts with 'prefix'. +func compListChartsOfRepo(repoName string, prefix string) []string { + var charts []string + + path := filepath.Join(settings.RepositoryCache, helmpath.CacheChartsFile(repoName)) + content, err := ioutil.ReadFile(path) + if err == nil { + scanner := bufio.NewScanner(bytes.NewReader(content)) + for scanner.Scan() { + fullName := fmt.Sprintf("%s/%s", repoName, scanner.Text()) + if strings.HasPrefix(fullName, prefix) { + charts = append(charts, fullName) + } + } + return charts + } + + if isNotExist(err) { + // If there is no cached charts file, fallback to the full index file. + // This is much slower but can happen after the caching feature is first + // installed but before the user does a 'helm repo update' to generate the + // first cached charts file. + path = filepath.Join(settings.RepositoryCache, helmpath.CacheIndexFile(repoName)) + if indexFile, err := repo.LoadIndexFile(path); err == nil { + for name := range indexFile.Entries { + fullName := fmt.Sprintf("%s/%s", repoName, name) + if strings.HasPrefix(fullName, prefix) { + charts = append(charts, fullName) + } + } + return charts + } + } + + return []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)) + + noSpace := false + noFile := false + var completions []string + + // First check completions for repos + repos := compListRepos("") + for _, repo := range repos { + repoWithSlash := fmt.Sprintf("%s/", repo) + if strings.HasPrefix(toComplete, repoWithSlash) { + // Must complete with charts within the specified repo + completions = append(completions, compListChartsOfRepo(repo, toComplete)...) + noSpace = false + break + } else if strings.HasPrefix(repo, toComplete) { + // Must complete the repo name + completions = append(completions, repoWithSlash) + noSpace = true + } + } + completion.CompDebugln(fmt.Sprintf("Completions after repos: %v", completions)) + + // Now handle completions for url prefixes + for _, url := range []string{"https://", "http://", "file://"} { + if strings.HasPrefix(toComplete, url) { + // The user already put in the full url prefix; we don't have + // anything to add, but make sure the shell does not default + // to file completion since we could be returning an empty array. + noFile = true + noSpace = true + } else if strings.HasPrefix(url, toComplete) { + // We are completing a url prefix + completions = append(completions, url) + noSpace = true + } + } + completion.CompDebugln(fmt.Sprintf("Completions after urls: %v", completions)) + + // Finally, provide file completion if we need to. + // We only do this if: + // 1- There are other completions found (if there are no completions, + // the shell will do file completion itself) + // 2- If there is some input from the user (or else we will end up + // listing the entire content of the current directory which will + // be too many choices for the user to find the real repos) + if includeFiles && len(completions) > 0 && len(toComplete) > 0 { + if files, err := ioutil.ReadDir("."); err == nil { + for _, file := range files { + if strings.HasPrefix(file.Name(), toComplete) { + // We are completing a file prefix + completions = append(completions, file.Name()) + } + } + } + } + completion.CompDebugln(fmt.Sprintf("Completions after files: %v", completions)) + + // 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)) + + directive := completion.BashCompDirectiveDefault + if noFile { + directive = directive | completion.BashCompDirectiveNoFileComp + } + if noSpace { + directive = directive | completion.BashCompDirectiveNoSpace + // The completion.BashCompDirective flags do not work for zsh right now. + // We handle it ourselves instead. + completions = compEnforceNoSpace(completions) + } + return completions, directive +} + +// This function prevents the shell from adding a space after +// a completion by adding a second, fake completion. +// It is only needed for zsh, but we cannot tell which shell +// is being used here, so we do the fake completion all the time; +// there are no real downsides to doing this for bash as well. +func compEnforceNoSpace(completions []string) []string { + // To prevent the shell from adding space after the completion, + // we trick it by pretending there is a second, longer match. + // 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)) + } + return completions +} diff --git a/cmd/helm/search_repo_test.go b/cmd/helm/search_repo_test.go index 6ece55505..402ef2970 100644 --- a/cmd/helm/search_repo_test.go +++ b/cmd/helm/search_repo_test.go @@ -83,3 +83,7 @@ func TestSearchRepositoriesCmd(t *testing.T) { } runTestCmd(t, tests) } + +func TestSearchRepoOutputCompletion(t *testing.T) { + outputFlagCompletionTest(t, "search repo") +} diff --git a/cmd/helm/show.go b/cmd/helm/show.go index e9e9cca8b..a82ad2777 100644 --- a/cmd/helm/show.go +++ b/cmd/helm/show.go @@ -23,6 +23,7 @@ import ( "github.com/spf13/cobra" "helm.sh/helm/v3/cmd/helm/require" + "helm.sh/helm/v3/internal/completion" "helm.sh/helm/v3/pkg/action" ) @@ -61,6 +62,14 @@ func newShowCmd(out io.Writer) *cobra.Command { Args: require.NoArgs, } + // Function providing dynamic auto-completion + validArgsFunc := func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { + if len(args) != 0 { + return nil, completion.BashCompDirectiveNoFileComp + } + return compListCharts(toComplete, true) + } + all := &cobra.Command{ Use: "all [CHART]", Short: "shows all information of the chart", @@ -145,6 +154,9 @@ func newShowCmd(out io.Writer) *cobra.Command { for _, subCmd := range cmds { addChartPathOptionsFlags(subCmd.Flags(), &client.ChartPathOptions) 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 92a947c26..34543c6cb 100644 --- a/cmd/helm/status.go +++ b/cmd/helm/status.go @@ -25,6 +25,7 @@ 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/output" @@ -65,8 +66,25 @@ 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.PersistentFlags() + 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) { + if len(args) == 1 { + return compListRevisions(cfg, args[0]) + } + return nil, completion.BashCompDirectiveNoFileComp + }) + bindOutputFlag(cmd, &outfmt) return cmd diff --git a/cmd/helm/status_test.go b/cmd/helm/status_test.go index 91a008e5a..0d2500e65 100644 --- a/cmd/helm/status_test.go +++ b/cmd/helm/status_test.go @@ -108,3 +108,66 @@ func mustParseTime(t string) helmtime.Time { res, _ := helmtime.Parse(time.RFC3339, t) return res } + +func TestStatusCompletion(t *testing.T) { + releasesMockWithStatus := func(info *release.Info, hooks ...*release.Hook) []*release.Release { + info.LastDeployed = helmtime.Unix(1452902400, 0).UTC() + return []*release.Release{{ + Name: "athos", + Namespace: "default", + Info: info, + Chart: &chart.Chart{}, + Hooks: hooks, + }, { + Name: "porthos", + Namespace: "default", + Info: info, + Chart: &chart.Chart{}, + Hooks: hooks, + }, { + Name: "aramis", + Namespace: "default", + Info: info, + Chart: &chart.Chart{}, + Hooks: hooks, + }, { + Name: "dartagnan", + Namespace: "gascony", + Info: info, + Chart: &chart.Chart{}, + Hooks: hooks, + }} + } + + tests := []cmdTestCase{{ + name: "completion for status", + cmd: "__complete status a", + golden: "output/status-comp.txt", + rels: releasesMockWithStatus(&release.Info{ + Status: release.StatusDeployed, + }), + }, { + name: "completion for status with too many arguments", + cmd: "__complete status dartagnan ''", + golden: "output/status-wrong-args-comp.txt", + rels: releasesMockWithStatus(&release.Info{ + Status: release.StatusDeployed, + }), + }, { + name: "completion for status with too many arguments", + cmd: "__complete status --debug a", + golden: "output/status-comp.txt", + rels: releasesMockWithStatus(&release.Info{ + Status: release.StatusDeployed, + }), + }} + runTestCmd(t, tests) +} + +func TestStatusRevisionCompletion(t *testing.T) { + revisionFlagCompletionTest(t, "status") +} + +func TestStatusOutputCompletion(t *testing.T) { + outputFlagCompletionTest(t, "status") +} diff --git a/cmd/helm/template.go b/cmd/helm/template.go index a47631c0d..36c029e69 100644 --- a/cmd/helm/template.go +++ b/cmd/helm/template.go @@ -29,6 +29,7 @@ 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" @@ -111,9 +112,9 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { if missing { return fmt.Errorf("could not find template %s in chart", f) } - for _, m := range manifestsToRender { - fmt.Fprintf(out, "---\n%s\n", m) - } + } + for _, m := range manifestsToRender { + fmt.Fprintf(out, "---\n%s\n", m) } } else { fmt.Fprintf(out, "%s", manifests.String()) @@ -123,6 +124,11 @@ 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") @@ -131,6 +137,7 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { f.BoolVar(&includeCrds, "include-crds", false, "include CRDs in the templated output") f.BoolVar(&client.IsUpgrade, "is-upgrade", false, "set .Release.IsUpgrade instead of .Release.IsInstall") f.StringArrayVarP(&extraAPIs, "api-versions", "a", []string{}, "Kubernetes api versions used for Capabilities.APIVersions") + f.BoolVar(&client.UseReleaseName, "release-name", false, "use release name in the output-dir path.") return cmd } diff --git a/cmd/helm/template_test.go b/cmd/helm/template_test.go index 735829432..dc7987d01 100644 --- a/cmd/helm/template_test.go +++ b/cmd/helm/template_test.go @@ -84,6 +84,24 @@ func TestTemplateCmd(t *testing.T) { cmd: fmt.Sprintf("template '%s' --include-crds", chartPath), golden: "output/template-with-crds.txt", }, + { + name: "template with show-only one", + cmd: fmt.Sprintf("template '%s' --show-only templates/service.yaml", chartPath), + golden: "output/template-show-only-one.txt", + }, + { + name: "template with show-only multiple", + 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: "sorted output of manifests (order of filenames, then order of objects within each YAML file)", + cmd: fmt.Sprintf("template '%s'", "testdata/testcharts/object-order"), + golden: "output/object-order.txt", + // Helm previously used random file order. Repeat the test so we + // don't accidentally get the expected result. + repeat: 10, + }, } runTestCmd(t, tests) } diff --git a/cmd/helm/testdata/output/object-order.txt b/cmd/helm/testdata/output/object-order.txt new file mode 100644 index 000000000..307f928f2 --- /dev/null +++ b/cmd/helm/testdata/output/object-order.txt @@ -0,0 +1,191 @@ +--- +# Source: object-order/templates/01-a.yml +# 1 +kind: NetworkPolicy +apiVersion: networking.k8s.io/v1 +metadata: + name: first +spec: + podSelector: {} + policyTypes: + - Egress + - Ingress +--- +# Source: object-order/templates/01-a.yml +# 2 +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: second +spec: + podSelector: {} + policyTypes: + - Egress + - Ingress +--- +# Source: object-order/templates/01-a.yml +# 3 +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: third +spec: + podSelector: {} + policyTypes: + - Egress + - Ingress +--- +# Source: object-order/templates/02-b.yml +# 5 +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: fifth +spec: + podSelector: {} + policyTypes: + - Egress + - Ingress +--- +# Source: object-order/templates/02-b.yml +# 7 +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: seventh +spec: + podSelector: {} + policyTypes: + - Egress + - Ingress +--- +# Source: object-order/templates/02-b.yml +# 8 +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: eighth +spec: + podSelector: {} + policyTypes: + - Egress + - Ingress +--- +# Source: object-order/templates/02-b.yml +# 9 +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: ninth +spec: + podSelector: {} + policyTypes: + - Egress + - Ingress +--- +# Source: object-order/templates/02-b.yml +# 10 +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: tenth +spec: + podSelector: {} + policyTypes: + - Egress + - Ingress +--- +# Source: object-order/templates/02-b.yml +# 11 +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: eleventh +spec: + podSelector: {} + policyTypes: + - Egress + - Ingress +--- +# Source: object-order/templates/02-b.yml +# 12 +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: twelfth +spec: + podSelector: {} + policyTypes: + - Egress + - Ingress +--- +# Source: object-order/templates/02-b.yml +# 13 +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: thirteenth +spec: + podSelector: {} + policyTypes: + - Egress + - Ingress +--- +# Source: object-order/templates/02-b.yml +# 14 +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: fourteenth +spec: + podSelector: {} + policyTypes: + - Egress + - Ingress +--- +# Source: object-order/templates/02-b.yml +# 15 (11th object within 02-b.yml, in order to test `SplitManifests` which assigns `manifest-10` +# to this object which should then come *after* `manifest-9`) +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: fifteenth +spec: + podSelector: {} + policyTypes: + - Egress + - Ingress +--- +# Source: object-order/templates/01-a.yml +# 4 (Deployment should come after all NetworkPolicy manifests, since 'helm template' outputs in install order) +apiVersion: apps/v1 +kind: Deployment +metadata: + name: fourth +spec: + selector: + matchLabels: + pod: fourth + replicas: 1 + template: + metadata: + labels: + pod: fourth + spec: + containers: + - name: hello-world + image: gcr.io/google-samples/node-hello:1.0 +--- +# Source: object-order/templates/02-b.yml +# 6 (implementation detail: currently, 'helm template' outputs hook manifests last; and yes, NetworkPolicy won't make a reasonable hook, this is just a dummy unit test manifest) +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + annotations: + "helm.sh/hook": pre-install + name: sixth +spec: + podSelector: {} + policyTypes: + - Egress + - Ingress diff --git a/cmd/helm/testdata/output/output-comp.txt b/cmd/helm/testdata/output/output-comp.txt new file mode 100644 index 000000000..de5f16f1d --- /dev/null +++ b/cmd/helm/testdata/output/output-comp.txt @@ -0,0 +1,4 @@ +table +json +yaml +:0 diff --git a/cmd/helm/testdata/output/revision-comp.txt b/cmd/helm/testdata/output/revision-comp.txt new file mode 100644 index 000000000..b4799f059 --- /dev/null +++ b/cmd/helm/testdata/output/revision-comp.txt @@ -0,0 +1,5 @@ +8 +9 +10 +11 +:0 diff --git a/cmd/helm/testdata/output/revision-wrong-args-comp.txt b/cmd/helm/testdata/output/revision-wrong-args-comp.txt new file mode 100644 index 000000000..b6f867176 --- /dev/null +++ b/cmd/helm/testdata/output/revision-wrong-args-comp.txt @@ -0,0 +1 @@ +:4 diff --git a/cmd/helm/testdata/output/status-comp.txt b/cmd/helm/testdata/output/status-comp.txt new file mode 100644 index 000000000..c97882964 --- /dev/null +++ b/cmd/helm/testdata/output/status-comp.txt @@ -0,0 +1,3 @@ +aramis +athos +:4 diff --git a/cmd/helm/testdata/output/status-wrong-args-comp.txt b/cmd/helm/testdata/output/status-wrong-args-comp.txt new file mode 100644 index 000000000..b6f867176 --- /dev/null +++ b/cmd/helm/testdata/output/status-wrong-args-comp.txt @@ -0,0 +1 @@ +:4 diff --git a/cmd/helm/testdata/output/template-show-only-multiple.txt b/cmd/helm/testdata/output/template-show-only-multiple.txt new file mode 100644 index 000000000..abb9a2e10 --- /dev/null +++ b/cmd/helm/testdata/output/template-show-only-multiple.txt @@ -0,0 +1,39 @@ +--- +# Source: subchart1/templates/service.yaml +apiVersion: v1 +kind: Service +metadata: + name: subchart1 + labels: + helm.sh/chart: "subchart1-0.1.0" + app.kubernetes.io/instance: "RELEASE-NAME" + kube-version/major: "1" + kube-version/minor: "16" + kube-version/version: "v1.16.0" + kube-api-version/test: v1 +spec: + type: ClusterIP + ports: + - port: 80 + targetPort: 80 + protocol: TCP + name: nginx + selector: + app.kubernetes.io/name: subchart1 +--- +# Source: subchart1/charts/subcharta/templates/service.yaml +apiVersion: v1 +kind: Service +metadata: + name: subcharta + labels: + helm.sh/chart: "subcharta-0.1.0" +spec: + type: ClusterIP + ports: + - port: 80 + targetPort: 80 + protocol: TCP + name: apache + selector: + app.kubernetes.io/name: subcharta diff --git a/cmd/helm/testdata/output/template-show-only-one.txt b/cmd/helm/testdata/output/template-show-only-one.txt new file mode 100644 index 000000000..f0dd0834e --- /dev/null +++ b/cmd/helm/testdata/output/template-show-only-one.txt @@ -0,0 +1,22 @@ +--- +# Source: subchart1/templates/service.yaml +apiVersion: v1 +kind: Service +metadata: + name: subchart1 + labels: + helm.sh/chart: "subchart1-0.1.0" + app.kubernetes.io/instance: "RELEASE-NAME" + kube-version/major: "1" + kube-version/minor: "16" + kube-version/version: "v1.16.0" + kube-api-version/test: v1 +spec: + type: ClusterIP + ports: + - port: 80 + targetPort: 80 + protocol: TCP + name: nginx + selector: + app.kubernetes.io/name: subchart1 diff --git a/cmd/helm/testdata/testcharts/alpine/templates/alpine-pod.yaml b/cmd/helm/testdata/testcharts/alpine/templates/alpine-pod.yaml index ae19a1127..a1a44e53f 100644 --- a/cmd/helm/testdata/testcharts/alpine/templates/alpine-pod.yaml +++ b/cmd/helm/testdata/testcharts/alpine/templates/alpine-pod.yaml @@ -14,7 +14,7 @@ metadata: app.kubernetes.io/version: {{ .Chart.AppVersion }} # This makes it easy to audit chart usage. helm.sh/chart: "{{.Chart.Name}}-{{.Chart.Version}}" - values: {{.Values.test.Name}} + values: {{.Values.Name}} 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. diff --git a/cmd/helm/testdata/testcharts/object-order/Chart.yaml b/cmd/helm/testdata/testcharts/object-order/Chart.yaml new file mode 100644 index 000000000..d2eb42fd7 --- /dev/null +++ b/cmd/helm/testdata/testcharts/object-order/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v2 +name: object-order +description: Test ordering of manifests in output +type: application +version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/object-order/templates/01-a.yml b/cmd/helm/testdata/testcharts/object-order/templates/01-a.yml new file mode 100644 index 000000000..32aa4a475 --- /dev/null +++ b/cmd/helm/testdata/testcharts/object-order/templates/01-a.yml @@ -0,0 +1,57 @@ +# 1 +kind: NetworkPolicy +apiVersion: networking.k8s.io/v1 +metadata: + name: first +spec: + podSelector: {} + policyTypes: + - Egress + - Ingress + +--- + +# 2 +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: second +spec: + podSelector: {} + policyTypes: + - Egress + - Ingress + +--- + +# 3 +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: third +spec: + podSelector: {} + policyTypes: + - Egress + - Ingress + +--- + +# 4 (Deployment should come after all NetworkPolicy manifests, since 'helm template' outputs in install order) +apiVersion: apps/v1 +kind: Deployment +metadata: + name: fourth +spec: + selector: + matchLabels: + pod: fourth + replicas: 1 + template: + metadata: + labels: + pod: fourth + spec: + containers: + - name: hello-world + image: gcr.io/google-samples/node-hello:1.0 diff --git a/cmd/helm/testdata/testcharts/object-order/templates/02-b.yml b/cmd/helm/testdata/testcharts/object-order/templates/02-b.yml new file mode 100644 index 000000000..895db8cf7 --- /dev/null +++ b/cmd/helm/testdata/testcharts/object-order/templates/02-b.yml @@ -0,0 +1,143 @@ +# 5 +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: fifth +spec: + podSelector: {} + policyTypes: + - Egress + - Ingress + +--- + +# 6 (implementation detail: currently, 'helm template' outputs hook manifests last; and yes, NetworkPolicy won't make a reasonable hook, this is just a dummy unit test manifest) +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + annotations: + "helm.sh/hook": pre-install + name: sixth +spec: + podSelector: {} + policyTypes: + - Egress + - Ingress + +--- + +# 7 +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: seventh +spec: + podSelector: {} + policyTypes: + - Egress + - Ingress + +--- + +# 8 +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: eighth +spec: + podSelector: {} + policyTypes: + - Egress + - Ingress + +--- + +# 9 +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: ninth +spec: + podSelector: {} + policyTypes: + - Egress + - Ingress + +--- + +# 10 +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: tenth +spec: + podSelector: {} + policyTypes: + - Egress + - Ingress + +--- + +# 11 +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: eleventh +spec: + podSelector: {} + policyTypes: + - Egress + - Ingress + +--- + +# 12 +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: twelfth +spec: + podSelector: {} + policyTypes: + - Egress + - Ingress + +--- + +# 13 +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: thirteenth +spec: + podSelector: {} + policyTypes: + - Egress + - Ingress + +--- + +# 14 +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: fourteenth +spec: + podSelector: {} + policyTypes: + - Egress + - Ingress + +--- + +# 15 (11th object within 02-b.yml, in order to test `SplitManifests` which assigns `manifest-10` +# to this object which should then come *after* `manifest-9`) +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: fifteenth +spec: + podSelector: {} + policyTypes: + - Egress + - Ingress diff --git a/cmd/helm/testdata/testcharts/object-order/values.yaml b/cmd/helm/testdata/testcharts/object-order/values.yaml new file mode 100644 index 000000000..e69de29bb diff --git a/cmd/helm/uninstall.go b/cmd/helm/uninstall.go index 7096d7873..85fa822bd 100644 --- a/cmd/helm/uninstall.go +++ b/cmd/helm/uninstall.go @@ -24,6 +24,7 @@ import ( "github.com/spf13/cobra" "helm.sh/helm/v3/cmd/helm/require" + "helm.sh/helm/v3/internal/completion" "helm.sh/helm/v3/pkg/action" ) @@ -64,6 +65,14 @@ 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 acfc23198..6c967b796 100644 --- a/cmd/helm/upgrade.go +++ b/cmd/helm/upgrade.go @@ -25,6 +25,7 @@ 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" @@ -144,6 +145,17 @@ 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.BoolVarP(&client.Install, "install", "i", false, "if a release by this name doesn't already exist, run an install") f.BoolVar(&client.Devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored") diff --git a/cmd/helm/upgrade_test.go b/cmd/helm/upgrade_test.go index bd1ccec35..3cecbe6d3 100644 --- a/cmd/helm/upgrade_test.go +++ b/cmd/helm/upgrade_test.go @@ -258,3 +258,7 @@ func prepareMockRelease(releaseName string, t *testing.T) (func(n string, v int, return relMock, ch, chartPath } + +func TestUpgradeOutputCompletion(t *testing.T) { + outputFlagCompletionTest(t, "upgrade") +} diff --git a/go.mod b/go.mod index 8e972fdfe..696c2b6d2 100644 --- a/go.mod +++ b/go.mod @@ -4,89 +4,42 @@ go 1.13 require ( github.com/BurntSushi/toml v0.3.1 - github.com/MakeNowJust/heredoc v0.0.0-20171113091838-e9091a26100e // indirect github.com/Masterminds/semver/v3 v3.0.3 github.com/Masterminds/sprig/v3 v3.0.2 - github.com/Masterminds/vcs v1.13.0 - github.com/Microsoft/go-winio v0.4.12 // indirect - github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a - github.com/containerd/containerd v1.3.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/cyphar/filepath-securejoin v0.2.2 - github.com/deislabs/oras v0.7.0 + github.com/deislabs/oras v0.8.1 github.com/docker/distribution v2.7.1+incompatible - github.com/docker/docker v1.4.2-0.20181221150755-2cb26cfe9cbf + github.com/docker/docker v1.4.2-0.20200203170920-46ec8731fbce github.com/docker/go-units v0.4.0 - github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c // indirect - github.com/emicklei/go-restful v2.11.1+incompatible // indirect github.com/evanphx/json-patch v4.5.0+incompatible - github.com/ghodss/yaml v1.0.0 // indirect - github.com/go-openapi/jsonreference v0.19.3 // indirect - github.com/go-openapi/spec v0.19.4 // indirect github.com/gobwas/glob v0.2.3 github.com/gofrs/flock v0.7.1 - github.com/gogo/protobuf v1.3.1 // indirect - github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9 // indirect - github.com/google/btree v1.0.0 // indirect - github.com/google/go-cmp v0.3.1 // indirect - github.com/googleapis/gnostic v0.3.1 // indirect - github.com/gosuri/uitable v0.0.1 - github.com/gregjones/httpcache v0.0.0-20181110185634-c63ab54fda8f // indirect - github.com/hashicorp/golang-lru v0.5.3 // indirect - github.com/imdario/mergo v0.3.8 // indirect - github.com/mailru/easyjson v0.7.0 // indirect - github.com/mattn/go-runewidth v0.0.4 // indirect - github.com/mattn/go-shellwords v1.0.5 + github.com/gosuri/uitable v0.0.4 + github.com/mattn/go-shellwords v1.0.9 github.com/mitchellh/copystructure v1.0.0 - github.com/onsi/ginkgo v1.10.1 // indirect - github.com/onsi/gomega v1.7.0 // indirect github.com/opencontainers/go-digest v1.0.0-rc1 github.com/opencontainers/image-spec v1.0.1 - github.com/pkg/errors v0.8.1 - github.com/prometheus/client_golang v1.2.1 // indirect + github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.4.2 github.com/spf13/cobra v0.0.5 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.4.0 - github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect - github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.1.0 - github.com/xenolf/lego v0.3.2-0.20160613233155-a9d8cec0e656 // indirect - golang.org/x/crypto v0.0.0-20191028145041-f83a4685e152 - golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271 // indirect - golang.org/x/sys v0.0.0-20191028164358-195ce5e7f934 // indirect - golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect - google.golang.org/appengine v1.6.5 // indirect - google.golang.org/genproto v0.0.0-20191028173616-919d9bdd9fe6 // indirect - google.golang.org/grpc v1.24.0 // indirect - gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v2 v2.2.4 - k8s.io/api v0.0.0-20191016110408-35e52d86657a - k8s.io/apiextensions-apiserver v0.0.0-20191016113550-5357c4baaf65 - k8s.io/apimachinery v0.0.0-20191004115801-a2eda9f80ab8 - k8s.io/cli-runtime v0.0.0-20191016114015-74ad18325ed5 - k8s.io/client-go v0.0.0-20191016111102-bec269661e48 + golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d + k8s.io/api v0.17.2 + k8s.io/apiextensions-apiserver v0.17.2 + k8s.io/apimachinery v0.17.2 + k8s.io/cli-runtime v0.17.2 + k8s.io/client-go v0.17.2 k8s.io/klog v1.0.0 - k8s.io/kube-openapi v0.0.0-20190918143330-0270cf2f1c1d // indirect - k8s.io/kubectl v0.0.0-20191016120415-2ed914427d51 - k8s.io/utils v0.0.0-20191010214722-8d271d903fe4 // indirect + k8s.io/kubectl v0.17.2 sigs.k8s.io/yaml v1.1.0 ) replace ( - // github.com/Azure/go-autorest/autorest has different versions for the Go - // modules than it does for releases on the repository. Note the correct - // version when updating. - github.com/Azure/go-autorest/autorest => github.com/Azure/go-autorest/autorest v0.9.0 - github.com/docker/docker => github.com/moby/moby v0.7.3-0.20190826074503-38ab9da00309 - - // Kubernetes imports github.com/miekg/dns at a newer version but it is used - // by a package Helm does not need. Go modules resolves all packages rather - // than just those in use (like Glide and dep do). This sets the version - // to the one oras needs. If oras is updated the version should be updated - // as well. - github.com/miekg/dns => github.com/miekg/dns v0.0.0-20181005163659-0d29b283ac0f - gopkg.in/inf.v0 v0.9.1 => github.com/go-inf/inf v0.9.1 - gopkg.in/square/go-jose.v2 v2.3.0 => github.com/square/go-jose v2.3.0+incompatible - - rsc.io/letsencrypt => github.com/dmcgowan/letsencrypt v0.0.0-20160928181947-1847a81d2087 + github.com/Azure/go-autorest => github.com/Azure/go-autorest v13.3.2+incompatible + github.com/docker/distribution => github.com/docker/distribution v0.0.0-20191216044856-a8371794149d ) diff --git a/go.sum b/go.sum index fb11fdbc4..cc6b8dd9c 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,13 @@ +bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 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= +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= +github.com/Azure/go-autorest v13.3.2+incompatible h1:VxzPyuhtnlBOzc4IWCZHqpyH2d+QMLQEuy3wREyY4oc= +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/adal v0.5.0 h1:q2gDruN08/guU9vAjuPWff0+QIrpH6ediguzdAzXAUU= @@ -20,27 +24,20 @@ github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbt 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/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/MakeNowJust/heredoc v0.0.0-20171113091838-e9091a26100e h1:eb0Pzkt15Bm7f2FFYv7sjY7NPFi3cPkS3tv1CcrFBWA= -github.com/MakeNowJust/heredoc v0.0.0-20171113091838-e9091a26100e/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.1 h1:2kKm5lb7dKVrt5TYUiAavE6oFc1cFT0057UVGT+JqLk= -github.com/Masterminds/semver/v3 v3.0.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= 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/sprig/v3 v3.0.0 h1:KSQz7Nb08/3VU9E4ns29dDxcczhOD1q7O1UfM4G3t3g= -github.com/Masterminds/sprig/v3 v3.0.0/go.mod h1:NEUY/Qq8Gdm2xgYA+NwJM6wmfdRV9xkh8h/Rld20R0U= 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/vcs v1.13.0 h1:USF5TvZGYgIpcbNAEMLfFhHqP08tFZVlUVrmTSpqnyA= -github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= -github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= -github.com/Microsoft/go-winio v0.4.12 h1:xAfWHN1IrQ0NJ9TBC0KBZoqLjzDTr1ML+4MywiUOryc= -github.com/Microsoft/go-winio v0.4.12/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= -github.com/Microsoft/hcsshim v0.8.6 h1:ZfF0+zZeYdzMIVMZHKtDKJvLHj76XCuVae/jNkjj0IA= -github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46 h1:lsxEuwrXEAokXB9qhlbKWPpo3KMLZQ5WB5WLQRW1uq0= +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= +github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= +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/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= @@ -51,59 +48,64 @@ 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/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/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= +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/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= +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= -github.com/blang/semver v3.5.0+incompatible h1:CGxCgetQ64DKk7rdZ++Vfnb1+ogGNnB17OJKJXD2Cfs= +github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/bshuster-repo/logrus-logstash-hook v0.4.1 h1:pgAtgj+A31JBVtEHu2uHuEx0n+2ukqUJnS2vVe5pQNA= github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= -github.com/bugsnag/bugsnag-go v1.5.0 h1:tP8hiPv1pGGW3LA6LKy5lW6WG+y9J2xWUdPd3WC452k= -github.com/bugsnag/bugsnag-go v1.5.0/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= -github.com/bugsnag/panicwrap v1.2.0 h1:OzrKrRvXis8qEvOkfcxNcYbOd2O7xXS2nnKMEMABFQA= -github.com/bugsnag/panicwrap v1.2.0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= -github.com/cespare/xxhash/v2 v2.1.0 h1:yTUvW7Vhb89inJ+8irsUqiWjh8iT6sQPZiQzI6ReGkA= -github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM= -github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 h1:7aWHqerlJ41y6FOsEUvknqgXnGmJyJSbjhAWq5pO4F8= +github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZkEHchZRSq9OQbsSzIT/OrI8YFFmRIng= +github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= +github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ= +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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 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/containerd/containerd v1.3.0-beta.2.0.20190823190603-4a2f61c4f2b4 h1:aMyA5J7j6D07U7pf8BFEY67BKoDcz0zWleAbQj3zVng= -github.com/containerd/containerd v1.3.0-beta.2.0.20190823190603-4a2f61c4f2b4/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.0 h1:xjvXQWABwS2uiv3TWgQt5Uth60Gu86LTGZXMJkjc7rY= -github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/continuity v0.0.0-20181203112020-004b46473808 h1:4BX8f882bXEDKfWIf0wa8HRvpnBoPszJJXL+TVbBw4M= -github.com/containerd/continuity v0.0.0-20181203112020-004b46473808/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/coreos/bbolt v1.3.1-coreos.6 h1:uTXKg9gY70s9jMAKdfljFQcuh4e/BXOM+V+d00KFj3A= -github.com/coreos/bbolt v1.3.1-coreos.6/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +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/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/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/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/etcd v3.3.15+incompatible h1:+9RjdC18gMxNQVvSiXvObLu29mOFmkgdsB4cRTlV+EE= -github.com/coreos/etcd v3.3.15+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= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7 h1:u9SHYsPQNyt5tgDm3YN7+9dYrpK96E5wFilTFWIDZOM= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea h1:n2Ltr3SrfQlf/9nOna1DoGKxLx3qTSI8Ttl6Xrqp6mw= +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/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +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= @@ -111,142 +113,129 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs 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.7.0 h1:RnDoFd3tQYODMiUqxgQ8JxlrlWL0/VMKIKRD01MmNYk= -github.com/deislabs/oras v0.7.0/go.mod h1:sqMKPG3tMyIX9xwXUBRLhZ24o+uT4y6jgBD2RzUTKDM= +github.com/deislabs/oras v0.8.1 h1:If674KraJVpujYR00rzdi0QAmW4BxzMJPVAZJKuhQ0c= +github.com/deislabs/oras v0.8.1/go.mod h1:Mx0rMSbBNaNfY9hjpccEnxkOqJL6KGjtxNHPLC4G4As= +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= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dmcgowan/letsencrypt v0.0.0-20160928181947-1847a81d2087 h1:8AJxBXuUPcBVAvoz6fi3fpSyozBxvF2DgQ0f/yn9nkE= -github.com/dmcgowan/letsencrypt v0.0.0-20160928181947-1847a81d2087/go.mod h1:pRqVcLnLZeet910LRIAzx73MR48LxCRA84OcsivAkSs= -github.com/docker/cli v0.0.0-20190506213505-d88565df0c2d h1:qdD+BtyCE1XXpDyhvn0yZVcZOLILdj9Cw4pKu0kQbPQ= -github.com/docker/cli v0.0.0-20190506213505-d88565df0c2d/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= -github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker-credential-helpers v0.6.1 h1:Dq4iIfcM7cNtddhLVWe9h4QDjsi4OER3Z8voPu/I52g= -github.com/docker/docker-credential-helpers v0.6.1/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= +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= +github.com/docker/distribution v0.0.0-20191216044856-a8371794149d h1:jC8tT/S0OGx2cswpeUTn4gOIea8P08lD3VFQT0cOZ50= +github.com/docker/distribution v0.0.0-20191216044856-a8371794149d/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= +github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v1.4.2-0.20200203170920-46ec8731fbce h1:KXS1Jg+ddGcWA8e1N7cupxaHHZhit5rB9tfDU+mfjyY= +github.com/docker/docker v1.4.2-0.20200203170920-46ec8731fbce/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker-credential-helpers v0.6.3 h1:zI2p9+1NQYdnG6sMU26EX4aVGlqbInSQxQXLvzJ4RPQ= +github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-metrics v0.0.0-20181218153428-b84716841b82 h1:X0fj836zx99zFu83v/M79DuBn84IL/Syx1SY6Y5ZEMA= -github.com/docker/go-metrics v0.0.0-20181218153428-b84716841b82/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= -github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk= +github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916 h1:yWHOI+vFjEsAakUTSrtqc/SAHrhSkmn48pqjidZX3QA= +github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4= -github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= +github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= +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/docker/spdystream v0.0.0-20181023171402-6480d4af844c h1:ZfSZ3P3BedhKGUhzj7BQlPSU4OvT6tfOKe3DVHzOA7s= -github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/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/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/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/emicklei/go-restful v2.11.1+incompatible h1:CjKsv3uWcCMvySPQYKxO8XX3f9zD4FeZRsW4G0B4ffE= -github.com/emicklei/go-restful v2.11.1+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/evanphx/json-patch v4.2.0+incompatible h1:fUDGZCv/7iAN7u0puUVhvKCcsR6vRfwrJatElLBEf0I= +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/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/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= +github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 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 v1.6.0 h1:0VruCpn7yAIIu7pWVClQC8wxCJEcG3nyzpMSHKi1PQc= -github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= +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= -github.com/ghodss/yaml v0.0.0-20180820084758-c7ce16629ff4/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 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 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= -github.com/go-inf/inf v0.9.1 h1:F4sloU4SED74gTeM3mWLrf8yyMAgVCV0puw3vhtKWrk= -github.com/go-inf/inf v0.9.1/go.mod h1:ZWwB6rTV+0pO94RdIMKue59tExzQp6/pj/BMuPQkXaA= +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-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= github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= -github.com/go-openapi/analysis v0.19.2 h1:ophLETFestFZHk3ji7niPEL4d466QjW+0Tdg5VyDq7E= github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= +github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= -github.com/go-openapi/errors v0.19.2 h1:a2kIyV3w+OS3S97zxUndRVD46+FhGOUBDFY7nmu4CsY= github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= -github.com/go-openapi/jsonpointer v0.19.2 h1:A9+F4Dc/MCNB5jibxf6rRvOvR/iFgQdyNx9eIhnGqq0= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= -github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.19.2 h1:rf5ArTHmIJxyV5Oiks+Su0mUens1+AjpkPoWr5xFRcI= github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= +github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk= github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= -github.com/go-openapi/runtime v0.19.0 h1:sU6pp4dSV2sGlNKKyHxZzi1m1kG4WnYtWcJ+HYbygjE= github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= +github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/spec v0.19.2 h1:SStNd1jRcYtfKCN7R0laGNs80WYYvn5CbBjM2sOmCrE= github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= -github.com/go-openapi/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo= -github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/spec v0.19.3 h1:0XRyw8kguri6Yw4SxhsQA/atC88yqrk0+G4YhI2wabc= +github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= -github.com/go-openapi/strfmt v0.19.0 h1:0Dn9qy1G9+UJfRU7TR8bmdGxb4uifB7HNrJjOnV0yPk= github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= +github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= -github.com/go-openapi/swag v0.19.2 h1:jvO6bCMBEilGwMfHhrd61zIID4oIFdwb76V17SM88dE= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= -github.com/go-openapi/validate v0.19.2 h1:ky5l57HjyVRrsJfd2+Ro5Z9PjGuKbsmftwyMtk8H7js= github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= -github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= +github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 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/gofrs/flock v0.7.1 h1:DP+LD/t0njgoPBvT5MJLeliUIVQR03hiKR6vezdwHlc= github.com/gofrs/flock v0.7.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= -github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= -github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= 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 h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I= 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/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= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9 h1:uHTyIjqVhYRhLbJ8nIiOJHkEZZ+5YoOsAbD3sk82NiE= -github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= 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= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= 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= @@ -257,10 +246,9 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z 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 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= -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 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= @@ -270,32 +258,29 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ 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/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.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk= -github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= 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/gorilla/handlers v1.4.0 h1:XulKRWSQK5uChr4pEgSE4Tc/OcmnU9GJuSwdog/tZsA= -github.com/gorilla/handlers v1.4.0/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= -github.com/gorilla/mux v1.7.0 h1:tOSd0UKHQd6urX6ApfOn4XdBMY6Sh1MfxV3kmaazO+U= -github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +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.7.2 h1:zoNxOV7WjqXptQOVngLmcSQgXmgk4NMz1HibBchjl/I= +github.com/gorilla/mux v1.7.2/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.1 h1:M9sMNgSZPyAu1FJZJLpJ16ofL8q5ko2EDUkICsynvlY= -github.com/gosuri/uitable v0.0.1/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= -github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/gregjones/httpcache v0.0.0-20181110185634-c63ab54fda8f h1:ShTPMJQes6tubcjzGMODIVG5hlrCeImaBnZzKF2N8SM= -github.com/gregjones/httpcache v0.0.0-20181110185634-c63ab54fda8f/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20190222133341-cfaf5686ec79 h1:lR9ssWAqp9qL0bALxqEEkuudiP1eweOdv9jsRK3e7lE= -github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20190222133341-cfaf5686ec79/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= +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.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.3.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= +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= 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/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= -github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= 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= @@ -304,20 +289,19 @@ github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63 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/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= +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/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 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo= 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/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +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/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 h1:PJPDf8OUfOK1bb/NeTKd4f1QXZItOX389VN3B6qC8ro= -github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= 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= @@ -337,30 +321,31 @@ github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP 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= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63 h1:nTT4s92Dgz2HlrB2NaMgvlfqHH39OgMhA7z3PK7PGD4= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -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.5 h1:JhhFTIOslh5ZsPrpa3Wdg8bF0WI3b44EMblmU9wIsXc= -github.com/mattn/go-shellwords v1.0.5/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= +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.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-shellwords v1.0.9 h1:eaB5JspOwiKKcHdqcjbfe5lA9cNn/4NRRtddXJCimqk= +github.com/mattn/go-shellwords v1.0.9/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= 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 v0.0.0-20181005163659-0d29b283ac0f h1:wVzAD6PG9MIDNQMZ6zc2YpzE/9hhJ3EN+b+a4B1thVs= -github.com/miekg/dns v0.0.0-20181005163659-0d29b283ac0f/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 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= 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/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= 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= 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/moby v0.7.3-0.20190826074503-38ab9da00309 h1:cvy4lBOYN3gKfKj8Lzz5Q9TfviP+L7koMHY7SvkyTKs= -github.com/moby/moby v0.7.3-0.20190826074503-38ab9da00309/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc= 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= @@ -368,89 +353,91 @@ github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lN 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= -github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c h1:nXxl5PrvVm2L/wCy8dQu6DMTwH4oIuGN8GJDAlqDdVE= -github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +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 h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= 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/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= -github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w= -github.com/onsi/ginkgo v1.8.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/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo= -github.com/onsi/gomega v1.5.0/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/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/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/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= +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/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/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-20171002181615-b8543db493a5 h1:rZQtoozkfsiNs36c7Tdv/gyGNzD1X1XWKO8rptVNZuM= -github.com/phayes/freeport v0.0.0-20171002181615-b8543db493a5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= +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/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +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/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/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.2 h1:awm861/B8OKDd2I/6o1dy3ra4BamzKhYOiGItCeZ740= -github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= 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.2.1 h1:JnMpQc6ppsNgw9QPAGF6Dod479itz7lvlsMzzNayLOI= -github.com/prometheus/client_golang v1.2.1/go.mod h1:XMU6Z2MjaRKVu/dC1qupJI9SiNkDYzz3xecMgSW/F+U= +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-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= 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/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.2.0 h1:kUZDBDTdBVBYBj5Tmh2NZLlF60mfjA27rM34b+cVwNU= -github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +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= -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-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190129233650-316cf8ccfec5 h1:Etei0Wx6pooT/DeOKcGTr1M/01ggz95Ajq8BBwCOKBU= -github.com/prometheus/procfs v0.0.0-20190129233650-316cf8ccfec5/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs= 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/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/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/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= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/soheilhy/cmux v0.1.3 h1:09wy7WZk4AqO03yH85Ex1X+Uo3vDsil3Fa9AgF8Emss= -github.com/soheilhy/cmux v0.1.3/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +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/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= 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/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= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= 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.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= +github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -460,54 +447,59 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ 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 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= 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/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8 h1:ndzgwNDnKIqyCvHTXaCqh9KlOWKvBry6nuXMJmonVsE= +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/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/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/xenolf/lego v0.0.0-20160613233155-a9d8cec0e656/go.mod h1:fwiGnfsIjG7OHPfOvgK7Y/Qo6+2Ox0iozjNTkZICKbY= -github.com/xenolf/lego v0.3.2-0.20160613233155-a9d8cec0e656 h1:BTvU+npm3/yjuBd53EvgiFLl5+YLikf2WvHsjRQ4KrY= -github.com/xenolf/lego v0.3.2-0.20160613233155-a9d8cec0e656/go.mod h1:fwiGnfsIjG7OHPfOvgK7Y/Qo6+2Ox0iozjNTkZICKbY= -github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18 h1:MPPkRncZLN9Kh4MEFmbnK4h3BD7AUmskWv2+EeZJCCs= -github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +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/yvasiyarov/go-metrics v0.0.0-20150112132944-c25f46c4b940 h1:p7OofyZ509h8DmPLh8Hn+EIIZm/xYhdZHJ9GnXHdr6U= -github.com/yvasiyarov/go-metrics v0.0.0-20150112132944-c25f46c4b940/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= -github.com/yvasiyarov/gorelic v0.0.6 h1:qMJQYPNdtJ7UNYHjX38KXZtltKTqimMuoQjNnSVIuJg= -github.com/yvasiyarov/gorelic v0.0.6/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= +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= +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.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= +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.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.uber.org/atomic v0.0.0-20181018215023-8dc6146f7569 h1:nSQar3Y0E3VQF/VdZ8PTAilaXpER+d7ypdABCrpwMdg= -go.uber.org/atomic v0.0.0-20181018215023-8dc6146f7569/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/multierr v0.0.0-20180122172545-ddea229ff1df h1:shvkWr0NAZkg4nPuE3XrKP0VuBPijjk3TfX6Y6acFNg= -go.uber.org/multierr v0.0.0-20180122172545-ddea229ff1df/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/zap v0.0.0-20180814183419-67bc79d13d15 h1:Z2sc4+v0JHV6Mn4kX1f2a5nruNjmV+Th32sugE8zwz8= -go.uber.org/zap v0.0.0-20180814183419-67bc79d13d15/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +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/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= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/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-20190611184440-5c40567a22f8 h1:1wopBVtVdWnn03fZelqdXTqk7U7zPQCb+T4rbU9ZEoU= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7 h1:0hQKqeLdqlt5iIwVOBErRisrHJAN57yOiPRQItI20fU= +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-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191028145041-f83a4685e152 h1:ZC1Xn5A1nlpSmQCIva4bZ3ob3lmhYIefc+GU+DLg1Ow= -golang.org/x/crypto v0.0.0-20191028145041-f83a4685e152/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/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-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 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= @@ -518,21 +510,21 @@ golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73r 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-20181106065722-10aee1819953/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-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= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc h1:gkKoSkUmnU6bpS/VhkuO27bzQeSA51uaEfbOW5dNb68= -golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +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= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271 h1:N66aaryRB3Ax92gH0v3hp1QYZ3zWWCCUR/j8Ifh45Ss= -golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271/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/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= @@ -540,14 +532,15 @@ golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4Iltr 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= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +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-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-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-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -556,127 +549,120 @@ golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7w 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-20190616124812-15dcb6c0061f h1:25KHgbfyiSm6vwQLbM3zZIe1v9p/3ea4Rz+nnM5K/i4= +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-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-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191028164358-195ce5e7f934 h1:u/E0NqCIWRDAo9WCFo6Ko49njPFDLSd3z+X1HgWDMpE= -golang.org/x/sys v0.0.0-20191028164358-195ce5e7f934/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/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/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg= +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-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/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/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= 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-20190621195816-6e04913cbbac h1:MQEvx39qSf8vyrx3XRaOe+j1UDIzKwkYOVObRgGPVqI= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485 h1:OB/uP/Puiu5vS5QMRPrXCDWUPb+kt8f1KW8oQzFejQw= +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/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-20190128161407-8ac453e89fca/go.mod h1:L3J43x8/uS+qIUoksaLKe6OS3nUKxOKuIFz1sl2/jx4= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7 h1:ZUjXAXmrAyrmmCPHgCA/vChHcpsX27MZ3yBonD/z1KE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873 h1:nfPFGzJkUDX6uBmpN/pSw7MbOAWegH5QDQuoXFHedLg= +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-20191028173616-919d9bdd9fe6 h1:UXl+Zk3jqqcbEVV7ace5lrt4YdA4tXiz3f/KbmD29Vo= -google.golang.org/genproto v0.0.0-20191028173616-919d9bdd9fe6/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= +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.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1 h1:Hz2g2wirWK7H0qIIhGIqRGTuMwTE8HEKFnDZZ7lm9NU= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.24.0 h1:vb/1TCsVn3DcJlQ0Gs1yB1pKI6Do2/QNwxdKqmc/b0s= -google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +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= 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/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/inf.v0 v0.9.0 h1:3zYtXIO92bvsdS3ggAdA8Gb4Azj0YU+TVY1uGYNFA8o= -gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= +gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +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= -gopkg.in/square/go-jose.v1 v1.1.2 h1:/5jmADZB+RiKtZGr4HxsEFOEfbfsjTKsVnqpThUpE30= -gopkg.in/square/go-jose.v1 v1.1.2/go.mod h1:QpYS+a4WhS+DTlyQIi6Ka7MS3SuR9a055rgXNEe6EiA= -gopkg.in/square/go-jose.v2 v2.2.2 h1:orlkJ3myw8CN1nVQHBFfloD+L3egixIa4FvUP6RosSA= +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/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 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 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= 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.0.0-20191016110408-35e52d86657a h1:VVUE9xTCXP6KUPMf92cQmN88orz600ebexcRRaBTepQ= -k8s.io/api v0.0.0-20191016110408-35e52d86657a/go.mod h1:/L5qH+AD540e7Cetbui1tuJeXdmNhO8jM6VkXeDdDhQ= -k8s.io/apiextensions-apiserver v0.0.0-20191016113550-5357c4baaf65 h1:kThoiqgMsSwBdMK/lPgjtYTsEjbUU9nXCA9DyU3feok= -k8s.io/apiextensions-apiserver v0.0.0-20191016113550-5357c4baaf65/go.mod h1:5BINdGqggRXXKnDgpwoJ7PyQH8f+Ypp02fvVNcIFy9s= -k8s.io/apimachinery v0.0.0-20191004115801-a2eda9f80ab8 h1:Iieh/ZEgT3BWwbLD5qEKcY06jKuPEl6zC7gPSehoLw4= -k8s.io/apimachinery v0.0.0-20191004115801-a2eda9f80ab8/go.mod h1:llRdnznGEAqC3DcNm6yEj472xaFVfLM7hnYofMb12tQ= -k8s.io/apiserver v0.0.0-20191016112112-5190913f932d h1:leksCBKKBrPJmW1jV4dZUvwqmVtXpKdzpHsqXfFS094= -k8s.io/apiserver v0.0.0-20191016112112-5190913f932d/go.mod h1:7OqfAolfWxUM/jJ/HBLyE+cdaWFBUoo5Q5pHgJVj2ws= -k8s.io/cli-runtime v0.0.0-20191016114015-74ad18325ed5 h1:8ZfMjkMBzcXEawLsYHg9lDM7aLEVso3NiVKfUTnN56A= -k8s.io/cli-runtime v0.0.0-20191016114015-74ad18325ed5/go.mod h1:sDl6WKSQkDM6zS1u9F49a0VooQ3ycYFBFLqd2jf2Xfo= -k8s.io/client-go v0.0.0-20191016111102-bec269661e48 h1:C2XVy2z0dV94q9hSSoCuTPp1KOG7IegvbdXuz9VGxoU= -k8s.io/client-go v0.0.0-20191016111102-bec269661e48/go.mod h1:hrwktSwYGI4JK+TJA3dMaFyyvHVi/aLarVHpbs8bgCU= -k8s.io/code-generator v0.0.0-20191004115455-8e001e5d1894 h1:NMYlxaF7rYQJk2E2IyrUhaX81zX24+dmoZdkPw0gJqI= -k8s.io/code-generator v0.0.0-20191004115455-8e001e5d1894/go.mod h1:mJUgkl06XV4kstAnLHAIzJPVCOzVR+ZcfPIv4fUsFCY= -k8s.io/component-base v0.0.0-20191016111319-039242c015a9 h1:2D+G/CCNVdYc0h9D+tX+0SmtcyQmby6uzNityrps1s0= -k8s.io/component-base v0.0.0-20191016111319-039242c015a9/go.mod h1:SuWowIgd/dtU/m/iv8OD9eOxp3QZBBhTIiWMsBQvKjI= +k8s.io/api v0.17.2 h1:NF1UFXcKN7/OOv1uxdRz3qfra8AHsPav5M93hlV9+Dc= +k8s.io/api v0.17.2/go.mod h1:BS9fjjLc4CMuqfSO8vgbHPKMt5+SF0ET6u/RVDihTo4= +k8s.io/apiextensions-apiserver v0.17.2 h1:cP579D2hSZNuO/rZj9XFRzwJNYb41DbNANJb6Kolpss= +k8s.io/apiextensions-apiserver v0.17.2/go.mod h1:4KdMpjkEjjDI2pPfBA15OscyNldHWdBCfsWMDWAmSTs= +k8s.io/apimachinery v0.17.2 h1:hwDQQFbdRlpnnsR64Asdi55GyCaIP/3WQpMmbNBeWr4= +k8s.io/apimachinery v0.17.2/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= +k8s.io/apiserver v0.17.2/go.mod h1:lBmw/TtQdtxvrTk0e2cgtOxHizXI+d0mmGQURIHQZlo= +k8s.io/cli-runtime v0.17.2 h1:YH4txSplyGudvxjhAJeHEtXc7Tr/16clKGfN076ydGk= +k8s.io/cli-runtime v0.17.2/go.mod h1:aa8t9ziyQdbkuizkNLAw3qe3srSyWh9zlSB7zTqRNPI= +k8s.io/client-go v0.17.2 h1:ndIfkfXEGrNhLIgkr0+qhRguSD3u6DCmonepn1O6NYc= +k8s.io/client-go v0.17.2/go.mod h1:QAzRgsa0C2xl4/eVpeVAZMvikCn8Nm81yqVx3Kk9XYI= +k8s.io/code-generator v0.17.2/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s= +k8s.io/component-base v0.17.2 h1:0XHf+cerTvL9I5Xwn9v+0jmqzGAZI7zNydv4tL6Cw6A= +k8s.io/component-base v0.17.2/go.mod h1:zMPW3g5aH7cHJpKYQ/ZsGMcgbsA/VyhEugF3QT1awLs= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20190822140433-26a664648505 h1:ZY6yclUKVbZ+SdWnkfY+Je5vrMpKOxmGeKRbsXVmqYM= k8s.io/gengo v0.0.0-20190822140433-26a664648505/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 v0.4.0 h1:lCJCxf/LIowc2IGS9TPjWDyXY4nOmdGdfcwwDQCOURQ= -k8s.io/klog v0.4.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= 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-20190816220812-743ec37842bf h1:EYm5AW/UUDbnmnI+gK0TJDVK9qPLhM+sRHYanNKw0EQ= -k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= -k8s.io/kube-openapi v0.0.0-20190918143330-0270cf2f1c1d h1:Xpe6sK+RY4ZgCTyZ3y273UmFmURhjtoJiwOMbQsXitY= -k8s.io/kube-openapi v0.0.0-20190918143330-0270cf2f1c1d/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= -k8s.io/kubectl v0.0.0-20191016120415-2ed914427d51 h1:RBkTKVMF+xsNsSOVc0+HdC0B5gD1sr6s6Cu5w9qNbuQ= -k8s.io/kubectl v0.0.0-20191016120415-2ed914427d51/go.mod h1:gL826ZTIfD4vXTGlmzgTbliCAT9NGiqpCqK2aNYv5MQ= -k8s.io/metrics v0.0.0-20191016113814-3b1a734dba6e h1:VbAmCGT95GvCZaGtW3oLhf7d2FXT+lnWiTtPE8hzPVo= -k8s.io/metrics v0.0.0-20191016113814-3b1a734dba6e/go.mod h1:ve7/vMWeY5lEBkZf6Bt5TTbGS3b8wAxwGbdXAsufjRs= -k8s.io/utils v0.0.0-20190801114015-581e00157fb1 h1:+ySTxfHnfzZb9ys375PXNlLhkJPLKgHajBU0N62BDvE= -k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= -k8s.io/utils v0.0.0-20191010214722-8d271d903fe4 h1:Gi+/O1saihwDqnlmC8Vhv1M5Sp4+rbOmK9TbsLn8ZEA= -k8s.io/utils v0.0.0-20191010214722-8d271d903fe4/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +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/kubectl v0.17.2 h1:QZR8Q6lWiVRjwKslekdbN5WPMp53dS/17j5e+oi5XVU= +k8s.io/kubectl v0.17.2/go.mod h1:y4rfLV0n6aPmvbRCqZQjvOp3ezxsFgpqL+zF5jH/lxk= +k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= +k8s.io/metrics v0.17.2/go.mod h1:3TkNHET4ROd+NfzNxkjoVfQ0Ob4iZnaHmSEA4vYpwLw= +k8s.io/utils v0.0.0-20191114184206-e782cd3c129f h1:GiPwtSzdP43eI1hpPCbROQCCIgCuiMMNF8YUVLF3vJo= +k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/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= @@ -685,7 +671,7 @@ modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= 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 v0.0.0-20190817042607-6149e4549fca h1:6dsH6AYQWbyZmtttJNe8Gq1cXOeS1BdV3eW37zHilAQ= -sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA= +sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI= diff --git a/internal/completion/complete.go b/internal/completion/complete.go new file mode 100644 index 000000000..a24390fc0 --- /dev/null +++ b/internal/completion/complete.go @@ -0,0 +1,388 @@ +/* +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)) + + flag, trimmedArgs, toComplete, err := checkIfFlagCompletion(cmd.Root(), args[:len(args)-1], args[len(args)-1]) + if err != nil { + // Error while attempting to parse flags + CompErrorln(err.Error()) + return + } + // 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 + return + } + + CompDebugln(fmt.Sprintf("Found final command '%s', with finalArgs %v", finalCmd.Name(), finalArgs)) + + // 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 + } + argsWoFlags := finalCmd.Flags().Args() + CompDebugln(fmt.Sprintf("Args without flags are '%v' with length %d", argsWoFlags, len(argsWoFlags))) + + var key interface{} + var keyStr string + if flag != nil { + key = flag + keyStr = flag.Name + } else { + key = finalCmd + keyStr = finalCmd.Name() + } + + // Find completion function for the flag or command + 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(), argsWoFlags, toComplete)) + completions, directive := completionFn(finalCmd, argsWoFlags, toComplete) + for _, comp := range completions { + // Print each possible completion to stdout for the completion script to consume. + fmt.Fprintln(out, comp) + } + + // 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.Fprintln(out, fmt.Sprintf(":%d", 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(rootCmd *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 + } + + // Find the real command for which completion must be performed + finalCmd, _, err := rootCmd.Find(trimmedArgs) + if err != nil { + // Unable to find the real command. E.g., helm invalidCmd + return nil, nil, "", errors.New("Unable to find final command for completion") + } + + CompDebugln(fmt.Sprintf("checkIfFlagCompletion: found final command '%s'", finalCmd.Name())) + + 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.Fprintf(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.Fprintf(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)) +} diff --git a/internal/experimental/registry/client.go b/internal/experimental/registry/client.go index d52d9f3e0..f664c9f38 100644 --- a/internal/experimental/registry/client.go +++ b/internal/experimental/registry/client.go @@ -21,6 +21,7 @@ import ( "fmt" "io" "io/ioutil" + "net/http" "sort" auth "github.com/deislabs/oras/pkg/auth/docker" @@ -69,7 +70,7 @@ func NewClient(opts ...ClientOption) (*Client, error) { } } if client.resolver == nil { - resolver, err := client.authorizer.Resolver(context.Background()) + resolver, err := client.authorizer.Resolver(context.Background(), http.DefaultClient, false) if err != nil { return nil, err } diff --git a/internal/experimental/registry/client_test.go b/internal/experimental/registry/client_test.go index 0861c8984..33799f5fa 100644 --- a/internal/experimental/registry/client_test.go +++ b/internal/experimental/registry/client_test.go @@ -23,6 +23,7 @@ import ( "io" "io/ioutil" "net" + "net/http" "os" "path/filepath" "testing" @@ -66,7 +67,7 @@ func (suite *RegistryClientTestSuite) SetupSuite() { client, err := auth.NewClient(credentialsFile) suite.Nil(err, "no error creating auth client") - resolver, err := client.Resolver(context.Background()) + resolver, err := client.Resolver(context.Background(), http.DefaultClient, false) suite.Nil(err, "no error creating resolver") // create cache diff --git a/internal/monocular/search.go b/internal/monocular/search.go index 61695f554..10e1f2136 100644 --- a/internal/monocular/search.go +++ b/internal/monocular/search.go @@ -58,7 +58,7 @@ type Chart struct { Icon string `json:"icon"` } -// Repo contains the name in monocular the the url for the repository +// Repo contains the name in monocular the url for the repository type Repo struct { Name string `json:"name"` URL string `json:"url"` diff --git a/internal/resolver/resolver_test.go b/internal/resolver/resolver_test.go index 3828771cc..bb3d3e6ac 100644 --- a/internal/resolver/resolver_test.go +++ b/internal/resolver/resolver_test.go @@ -216,3 +216,61 @@ func TestHashReq(t *testing.T) { }) } } + +func TestGetLocalPath(t *testing.T) { + tests := []struct { + name string + repo string + chartpath string + expect string + err bool + }{ + { + name: "absolute path", + repo: "file:////tmp", + expect: "/tmp", + }, + { + name: "relative path", + repo: "file://../../../../cmd/helm/testdata/testcharts/signtest", + chartpath: "foo/bar", + expect: "../../cmd/helm/testdata/testcharts/signtest", + }, + { + name: "current directory path", + repo: "../charts/localdependency", + chartpath: "testdata/chartpath/charts", + expect: "testdata/chartpath/charts/localdependency", + }, + { + name: "invalid local path", + repo: "file://../testdata/notexist", + chartpath: "testdata/chartpath", + err: true, + }, + { + name: "invalid path under current directory", + repo: "../charts/nonexistentdependency", + chartpath: "testdata/chartpath/charts", + err: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p, err := GetLocalPath(tt.repo, tt.chartpath) + if err != nil { + if tt.err { + return + } + t.Fatal(err) + } + if tt.err { + t.Fatalf("Expected error in test %q", tt.name) + } + if p != tt.expect { + t.Errorf("%q: expected %q, got %q", tt.name, tt.expect, p) + } + }) + } +} 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 diff --git a/pkg/action/action.go b/pkg/action/action.go index 16c5d3546..9405cc401 100644 --- a/pkg/action/action.go +++ b/pkg/action/action.go @@ -167,8 +167,8 @@ func (c *Configuration) releaseContent(name string, version int) (*release.Relea // GetVersionSet retrieves a set of available k8s API versions func GetVersionSet(client discovery.ServerResourcesInterface) (chartutil.VersionSet, error) { groups, resources, err := client.ServerGroupsAndResources() - if err != nil { - return chartutil.DefaultVersionSet, err + if err != nil && !discovery.IsGroupDiscoveryFailedError(err) { + return chartutil.DefaultVersionSet, errors.Wrap(err, "could not get apiVersions from Kubernetes") } // FIXME: The Kubernetes test fixture for cli appears to always return nil diff --git a/pkg/action/action_test.go b/pkg/action/action_test.go index a5baec97d..df6a48e7f 100644 --- a/pkg/action/action_test.go +++ b/pkg/action/action_test.go @@ -19,6 +19,7 @@ import ( "context" "flag" "io/ioutil" + "net/http" "path/filepath" "testing" @@ -45,7 +46,7 @@ func actionConfigFixture(t *testing.T) *Configuration { t.Fatal(err) } - resolver, err := client.Resolver(context.Background()) + resolver, err := client.Resolver(context.Background(), http.DefaultClient, false) if err != nil { t.Fatal(err) } diff --git a/pkg/action/install.go b/pkg/action/install.go index e368bcb28..c4ee50857 100644 --- a/pkg/action/install.go +++ b/pkg/action/install.go @@ -67,28 +67,32 @@ type Install struct { ChartPathOptions - ClientOnly bool - DryRun bool - DisableHooks bool - Replace bool - Wait bool - Devel bool - DependencyUpdate bool - Timeout time.Duration - Namespace string - ReleaseName string - GenerateName bool - NameTemplate string - Description string - OutputDir string - Atomic bool - SkipCRDs bool - SubNotes bool + ClientOnly bool + DryRun bool + DisableHooks bool + Replace bool + Wait bool + Devel bool + DependencyUpdate bool + Timeout time.Duration + Namespace string + ReleaseName string + GenerateName bool + NameTemplate string + Description string + OutputDir string + Atomic bool + SkipCRDs bool + SubNotes bool + DisableOpenAPIValidation bool // APIVersions allows a manual set of supported API Versions to be passed // (for things like templating). These are ignored if ClientOnly is false APIVersions chartutil.VersionSet // Used by helm template to render charts with .Release.IsUpgrade. Ignored if Dry-Run is false IsUpgrade bool + // Used by helm template to add the release as part of OutputDir path + // OutputDir/ + UseReleaseName bool } // ChartPathOptions captures common options used for controlling chart paths @@ -217,7 +221,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.OutputDir, i.SubNotes) + rel.Hooks, manifestDoc, rel.Info.Notes, err = i.cfg.renderResources(chrt, valuesToRender, i.ReleaseName, i.OutputDir, i.SubNotes, i.UseReleaseName) // Even for errors, attach this if available if manifestDoc != nil { rel.Manifest = manifestDoc.String() @@ -232,7 +236,7 @@ func (i *Install) Run(chrt *chart.Chart, vals map[string]interface{}) (*release. // Mark this release as in-progress rel.SetStatus(release.StatusPendingInstall, "Initial install underway") - resources, err := i.cfg.KubeClient.Build(bytes.NewBufferString(rel.Manifest), true) + resources, err := i.cfg.KubeClient.Build(bytes.NewBufferString(rel.Manifest), !i.DisableOpenAPIValidation) if err != nil { return nil, errors.Wrap(err, "unable to build kubernetes objects from release manifest") } @@ -421,7 +425,7 @@ func (i *Install) replaceRelease(rel *release.Release) error { } // renderResources renders the templates in a chart -func (c *Configuration) renderResources(ch *chart.Chart, values chartutil.Values, outputDir string, subNotes bool) ([]*release.Hook, *bytes.Buffer, string, error) { +func (c *Configuration) renderResources(ch *chart.Chart, values chartutil.Values, releaseName string, outputDir string, subNotes, useReleaseName bool) ([]*release.Hook, *bytes.Buffer, string, error) { hs := []*release.Hook{} b := bytes.NewBuffer(nil) @@ -436,8 +440,20 @@ func (c *Configuration) renderResources(ch *chart.Chart, values chartutil.Values } } - files, err := engine.Render(ch, values) - if err != nil { + 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, "", err } @@ -486,7 +502,11 @@ func (c *Configuration) renderResources(ch *chart.Chart, values chartutil.Values if outputDir == "" { fmt.Fprintf(b, "---\n# Source: %s\n%s\n", m.Name, m.Content) } else { - err = writeToFile(outputDir, m.Name, m.Content, fileWritten[m.Name]) + newDir := outputDir + if useReleaseName { + newDir = filepath.Join(outputDir, releaseName) + } + err = writeToFile(newDir, m.Name, m.Content, fileWritten[m.Name]) if err != nil { return hs, b, "", err } diff --git a/pkg/action/install_test.go b/pkg/action/install_test.go index d6f1c88cd..ba350819d 100644 --- a/pkg/action/install_test.go +++ b/pkg/action/install_test.go @@ -471,6 +471,46 @@ func TestInstallReleaseOutputDir(t *testing.T) { is.True(os.IsNotExist(err)) } +func TestInstallOutputDirWithReleaseName(t *testing.T) { + is := assert.New(t) + instAction := installAction(t) + vals := map[string]interface{}{} + + dir, err := ioutil.TempDir("", "output-dir") + if err != nil { + log.Fatal(err) + } + defer os.RemoveAll(dir) + + instAction.OutputDir = dir + instAction.UseReleaseName = true + instAction.ReleaseName = "madra" + + newDir := filepath.Join(dir, instAction.ReleaseName) + + _, err = instAction.Run(buildChart(withSampleTemplates(), withMultipleManifestTemplate()), vals) + if err != nil { + t.Fatalf("Failed install: %s", err) + } + + _, err = os.Stat(filepath.Join(newDir, "hello/templates/goodbye")) + is.NoError(err) + + _, err = os.Stat(filepath.Join(newDir, "hello/templates/hello")) + is.NoError(err) + + _, err = os.Stat(filepath.Join(newDir, "hello/templates/with-partials")) + is.NoError(err) + + _, err = os.Stat(filepath.Join(newDir, "hello/templates/rbac")) + is.NoError(err) + + test.AssertGoldenFile(t, filepath.Join(newDir, "hello/templates/rbac"), "rbac.txt") + + _, err = os.Stat(filepath.Join(newDir, "hello/templates/empty")) + is.True(os.IsNotExist(err)) +} + func TestNameAndChart(t *testing.T) { is := assert.New(t) instAction := installAction(t) diff --git a/pkg/action/list.go b/pkg/action/list.go index 5d3417203..5be60ac42 100644 --- a/pkg/action/list.go +++ b/pkg/action/list.go @@ -26,7 +26,7 @@ import ( // ListStates represents zero or more status codes that a list item may have set // -// Because this is used as a bitmask filter, more than one one bit can be flipped +// Because this is used as a bitmask filter, more than one bit can be flipped // in the ListStates. type ListStates uint diff --git a/pkg/action/package.go b/pkg/action/package.go index 5c85ebe0d..b48fc65f0 100644 --- a/pkg/action/package.go +++ b/pkg/action/package.go @@ -60,12 +60,6 @@ func (p *Package) Run(path string, vals map[string]interface{}) (string, error) return "", err } - combinedVals, err := chartutil.CoalesceValues(ch, vals) - if err != nil { - return "", err - } - ch.Values = combinedVals - // If version is set, modify the version. if p.Version != "" { if err := setVersion(ch, p.Version); err != nil { diff --git a/pkg/action/pull.go b/pkg/action/pull.go index b0a3d2598..4ff5f5c3e 100644 --- a/pkg/action/pull.go +++ b/pkg/action/pull.go @@ -110,13 +110,21 @@ func (p *Pull) Run(chartRef string) (string, error) { if !filepath.IsAbs(ud) { ud = filepath.Join(p.DestDir, ud) } - if fi, err := os.Stat(ud); err != nil { - if err := os.MkdirAll(ud, 0755); err != nil { + // Let udCheck to check conflict file/dir without replacing ud when untarDir is the current directory(.). + udCheck := ud + if udCheck == "." { + _, udCheck = filepath.Split(chartRef) + } else { + _, chartName := filepath.Split(chartRef) + udCheck = filepath.Join(udCheck, chartName) + } + if _, err := os.Stat(udCheck); err != nil { + if err := os.MkdirAll(udCheck, 0755); err != nil { return out.String(), errors.Wrap(err, "failed to untar (mkdir)") } - } else if !fi.IsDir() { - return out.String(), errors.Errorf("failed to untar: %s is not a directory", ud) + } else { + return out.String(), errors.Errorf("failed to untar: a file or directory with the name %s already exists", udCheck) } return out.String(), chartutil.ExpandFile(ud, saved) diff --git a/pkg/action/upgrade.go b/pkg/action/upgrade.go index cdc40eaaa..ad3a235e6 100644 --- a/pkg/action/upgrade.go +++ b/pkg/action/upgrade.go @@ -161,7 +161,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) + hooks, manifestDoc, notesTxt, err := u.cfg.renderResources(chart, valuesToRender, "", "", u.SubNotes, false) if err != nil { return nil, nil, err } @@ -291,7 +291,7 @@ func (u *Upgrade) failRelease(rel *release.Release, created kube.ResourceList, e rel.Info.Status = release.StatusFailed rel.Info.Description = msg u.cfg.recordRelease(rel) - if u.CleanupOnFail { + if u.CleanupOnFail && len(created) > 0 { u.cfg.Log("Cleanup on fail set, cleaning up %d resources", len(created)) _, errs := u.cfg.KubeClient.Delete(created) if errs != nil { diff --git a/pkg/action/upgrade_test.go b/pkg/action/upgrade_test.go index e7bfeefc5..f25d115c4 100644 --- a/pkg/action/upgrade_test.go +++ b/pkg/action/upgrade_test.go @@ -60,6 +60,31 @@ func TestUpgradeRelease_Wait(t *testing.T) { is.Equal(res.Info.Status, release.StatusFailed) } +func TestUpgradeRelease_CleanupOnFail(t *testing.T) { + is := assert.New(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) + + failer := upAction.cfg.KubeClient.(*kubefake.FailingKubeClient) + failer.WaitError = fmt.Errorf("I timed out") + failer.DeleteError = fmt.Errorf("I tried to delete nil") + upAction.cfg.KubeClient = failer + upAction.Wait = true + upAction.CleanupOnFail = true + vals := map[string]interface{}{} + + res, err := upAction.Run(rel.Name, buildChart(), vals) + req.Error(err) + is.NotContains(err.Error(), "unable to cleanup resources") + is.Contains(res.Info.Description, "I timed out") + is.Equal(res.Info.Status, release.StatusFailed) +} + func TestUpgradeRelease_Atomic(t *testing.T) { is := assert.New(t) req := require.New(t) diff --git a/pkg/action/validate.go b/pkg/action/validate.go index 526a4c7fc..6bbfc5e8d 100644 --- a/pkg/action/validate.go +++ b/pkg/action/validate.go @@ -33,7 +33,8 @@ func existingResourceConflict(resources kube.ResourceList) error { } helper := resource.NewHelper(info.Client, info.Mapping) - if _, err := helper.Get(info.Namespace, info.Name, info.Export); err != nil { + existing, err := helper.Get(info.Namespace, info.Name, info.Export) + if err != nil { if apierrors.IsNotFound(err) { return nil } @@ -41,7 +42,7 @@ func existingResourceConflict(resources kube.ResourceList) error { return errors.Wrap(err, "could not get information about the resource") } - return fmt.Errorf("existing resource conflict: kind: %s, namespace: %s, name: %s", info.Mapping.GroupVersionKind.Kind, info.Namespace, info.Name) + return fmt.Errorf("existing resource conflict: namespace: %s, name: %s, existing_kind: %s, new_kind: %s", info.Namespace, info.Name, existing.GetObjectKind().GroupVersionKind(), info.Mapping.GroupVersionKind) }) return err } diff --git a/pkg/chart/chart.go b/pkg/chart/chart.go index c3e99eae6..5eb4d4d94 100644 --- a/pkg/chart/chart.go +++ b/pkg/chart/chart.go @@ -33,7 +33,7 @@ type Chart struct { Raw []*File `json:"-"` // Metadata is the contents of the Chartfile. Metadata *Metadata `json:"metadata"` - // LocK is the contents of Chart.lock. + // Lock is the contents of Chart.lock. Lock *Lock `json:"lock"` // Templates for this chart. Templates []*File `json:"templates"` diff --git a/pkg/chartutil/chartfile_test.go b/pkg/chartutil/chartfile_test.go index 9a6e608f6..fb5f15376 100644 --- a/pkg/chartutil/chartfile_test.go +++ b/pkg/chartutil/chartfile_test.go @@ -39,7 +39,6 @@ func verifyChartfile(t *testing.T, f *chart.Metadata, name string) { t.Fatal("Failed verifyChartfile because f is nil") } - // Api instead of API because it was generated via protobuf. if f.APIVersion != chart.APIVersionV1 { t.Errorf("Expected API Version %q, got %q", chart.APIVersionV1, f.APIVersion) } diff --git a/pkg/chartutil/create.go b/pkg/chartutil/create.go index c67cde04f..496f20166 100644 --- a/pkg/chartutil/create.go +++ b/pkg/chartutil/create.go @@ -103,6 +103,8 @@ fullnameOverride: "" serviceAccount: # Specifies whether a service account should be created create: true + # Annotations to add to the service account + 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: @@ -170,6 +172,7 @@ const defaultIgnore = `# Patterns to ignore when building packages. *.swp *.bak *.tmp +*.orig *~ # Various IDEs .project @@ -301,7 +304,11 @@ kind: ServiceAccount metadata: name: {{ include ".serviceAccountName" . }} labels: -{{ include ".labels" . | nindent 4 }} + {{- include ".labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} {{- end -}} ` @@ -398,7 +405,7 @@ kind: Pod metadata: name: "{{ include ".fullname" . }}-test-connection" labels: -{{ include ".labels" . | nindent 4 }} + {{- include ".labels" . | nindent 4 }} annotations: "helm.sh/hook": test-success spec: @@ -406,7 +413,7 @@ spec: - name: wget image: busybox command: ['wget'] - args: ['{{ include ".fullname" . }}:{{ .Values.service.port }}'] + args: ['{{ include ".fullname" . }}:{{ .Values.service.port }}'] restartPolicy: Never ` diff --git a/pkg/chartutil/doc.go b/pkg/chartutil/doc.go index efcda2cfa..8f06bcc9a 100644 --- a/pkg/chartutil/doc.go +++ b/pkg/chartutil/doc.go @@ -16,7 +16,7 @@ limitations under the License. /*Package chartutil contains tools for working with charts. -Charts are described in the protocol buffer definition (pkg/proto/charts). +Charts are described in the chart package (pkg/chart). This package provides utilities for serializing and deserializing charts. A chart can be represented on the file system in one of two ways: @@ -38,7 +38,7 @@ For accepting raw compressed tar file data from an io.Reader, the 'loader.LoadArchive()' will read in the data, uncompress it, and unpack it into a Chart. -When creating charts in memory, use the 'helm.sh/helm/pkg/proto/chart' +When creating charts in memory, use the 'helm.sh/helm/pkg/chart' package directly. */ package chartutil // import "helm.sh/helm/v3/pkg/chartutil" diff --git a/pkg/chartutil/save.go b/pkg/chartutil/save.go index fb985bb59..be0dfdc24 100644 --- a/pkg/chartutil/save.go +++ b/pkg/chartutil/save.go @@ -50,11 +50,12 @@ func SaveDir(c *chart.Chart, dest string) error { } // Save values.yaml - if c.Values != nil { - vf := filepath.Join(outdir, ValuesfileName) - b, _ := yaml.Marshal(c.Values) - if err := writeFile(vf, b); err != nil { - return err + for _, f := range c.Raw { + if f.Name == ValuesfileName { + vf := filepath.Join(outdir, ValuesfileName) + if err := writeFile(vf, f.Data); err != nil { + return err + } } } @@ -161,12 +162,12 @@ func writeTarContents(out *tar.Writer, c *chart.Chart, prefix string) error { } // Save values.yaml - ydata, err := yaml.Marshal(c.Values) - if err != nil { - return err - } - if err := writeToTar(out, filepath.Join(base, ValuesfileName), ydata); err != nil { - return err + for _, f := range c.Raw { + if f.Name == ValuesfileName { + if err := writeToTar(out, filepath.Join(base, ValuesfileName), f.Data); err != nil { + return err + } + } } // Save values.schema.json if it exists diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index d0261dca2..490561037 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -27,6 +27,7 @@ import ( "text/template" "github.com/pkg/errors" + "k8s.io/client-go/rest" "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/chartutil" @@ -39,6 +40,8 @@ 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 + config *rest.Config } // Render takes a chart, optional values, and value overrides, and attempts to render the Go templates. @@ -71,6 +74,15 @@ func Render(chrt *chart.Chart, values chartutil.Values) (map[string]string, erro return new(Engine).Render(chrt, values) } +// RenderWithClient takes a chart, optional values, and value overrides, and attempts to +// render the Go templates using the default options. This engine is client aware and so can have template +// functions that interact with the client +func RenderWithClient(chrt *chart.Chart, values chartutil.Values, config *rest.Config) (map[string]string, error) { + return Engine{ + config: config, + }.Render(chrt, values) +} + // renderable is an object that can be rendered. type renderable struct { // tpl is the current template. @@ -83,6 +95,7 @@ type renderable struct { const warnStartDelim = "HELM_ERR_START" const warnEndDelim = "HELM_ERR_END" +const recursionMaxNums = 1000 var warnRegex = regexp.MustCompile(warnStartDelim + `(.*)` + warnEndDelim) @@ -93,19 +106,20 @@ func warnWrap(warn string) string { // initFunMap creates the Engine's FuncMap and adds context-specific functions. func (e Engine) initFunMap(t *template.Template, referenceTpls map[string]renderable) { funcMap := funcMap() - includedNames := make([]string, 0) + includedNames := make(map[string]int) // Add the 'include' function here so we can close over t. funcMap["include"] = func(name string, data interface{}) (string, error) { var buf strings.Builder - for _, n := range includedNames { - if n == name { + if v, ok := includedNames[name]; ok { + if v > recursionMaxNums { return "", errors.Wrapf(fmt.Errorf("unable to execute template"), "rendering template has a nested reference name: %s", name) } + includedNames[name]++ + } else { + includedNames[name] = 1 } - includedNames = append(includedNames, name) err := t.ExecuteTemplate(&buf, name, data) - includedNames = includedNames[:len(includedNames)-1] return buf.String(), err } @@ -157,6 +171,9 @@ func (e Engine) initFunMap(t *template.Template, referenceTpls map[string]render } return val, nil } + if e.config != nil { + funcMap["lookup"] = NewLookupFunction(e.config) + } t.Funcs(funcMap) } diff --git a/pkg/engine/lookup_func.go b/pkg/engine/lookup_func.go new file mode 100644 index 000000000..5dde29443 --- /dev/null +++ b/pkg/engine/lookup_func.go @@ -0,0 +1,119 @@ +/* +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 engine + +import ( + "log" + "strings" + + "github.com/pkg/errors" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/discovery" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/rest" +) + +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. +func NewLookupFunction(config *rest.Config) lookupFunc { + return func(apiversion string, resource string, namespace string, name string) (map[string]interface{}, error) { + var client dynamic.ResourceInterface + c, namespaced, err := getDynamicClientOnKind(apiversion, resource, config) + if err != nil { + return map[string]interface{}{}, err + } + if namespaced && namespace != "" { + client = c.Namespace(namespace) + } else { + client = c + } + if name != "" { + // this will return a single object + obj, err := client.Get(name, metav1.GetOptions{}) + if err != nil { + if apierrors.IsNotFound(err) { + // Just return an empty interface when the object was not found. + // That way, users can use `if not (lookup ...)` in their templates. + return map[string]interface{}{}, nil + } + return map[string]interface{}{}, err + } + return obj.UnstructuredContent(), nil + } + //this will return a list + obj, err := client.List(metav1.ListOptions{}) + if err != nil { + if apierrors.IsNotFound(err) { + // Just return an empty interface when the object was not found. + // That way, users can use `if not (lookup ...)` in their templates. + return map[string]interface{}{}, nil + } + return map[string]interface{}{}, err + } + return obj.UnstructuredContent(), nil + } +} + +// getDynamicClientOnUnstructured returns a dynamic client on an Unstructured type. This client can be further namespaced. +func getDynamicClientOnKind(apiversion string, kind string, config *rest.Config) (dynamic.NamespaceableResourceInterface, bool, error) { + gvk := schema.FromAPIVersionAndKind(apiversion, kind) + apiRes, err := getAPIReourceForGVK(gvk, config) + if err != nil { + log.Printf("[ERROR] unable to get apiresource from unstructured: %s , error %s", gvk.String(), err) + return nil, false, errors.Wrapf(err, "unable to get apiresource from unstructured: %s", gvk.String()) + } + gvr := schema.GroupVersionResource{ + Group: apiRes.Group, + Version: apiRes.Version, + Resource: apiRes.Name, + } + intf, err := dynamic.NewForConfig(config) + if err != nil { + log.Printf("[ERROR] unable to get dynamic client %s", err) + return nil, false, err + } + res := intf.Resource(gvr) + return res, apiRes.Namespaced, nil +} + +func getAPIReourceForGVK(gvk schema.GroupVersionKind, config *rest.Config) (metav1.APIResource, error) { + res := metav1.APIResource{} + discoveryClient, err := discovery.NewDiscoveryClientForConfig(config) + if err != nil { + log.Printf("[ERROR] unable to create discovery client %s", err) + return res, err + } + resList, err := discoveryClient.ServerResourcesForGroupVersion(gvk.GroupVersion().String()) + if err != nil { + log.Printf("[ERROR] unable to retrieve resource list for: %s , error: %s", gvk.GroupVersion().String(), err) + return res, err + } + for _, resource := range resList.APIResources { + //if a resource contains a "/" it's referencing a subresource. we don't support suberesource for now. + if resource.Kind == gvk.Kind && !strings.Contains(resource.Name, "/") { + res = resource + res.Group = gvk.Group + res.Version = gvk.Version + break + } + } + return res, nil +} diff --git a/pkg/getter/getter.go b/pkg/getter/getter.go index e11dbfcae..68638c2ca 100644 --- a/pkg/getter/getter.go +++ b/pkg/getter/getter.go @@ -64,7 +64,7 @@ func WithUserAgent(userAgent string) Option { } } -// WithTLSClientConfig sets the client client auth with the provided credentials. +// WithTLSClientConfig sets the client auth with the provided credentials. func WithTLSClientConfig(certFile, keyFile, caFile string) Option { return func(opts *options) { opts.certFile = certFile diff --git a/pkg/helmpath/home.go b/pkg/helmpath/home.go index 0b0f110a5..bd43e8890 100644 --- a/pkg/helmpath/home.go +++ b/pkg/helmpath/home.go @@ -11,6 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +// Package helmpath calculates filesystem paths to Helm's configuration, cache and data. package helmpath // This helper builds paths to Helm's configuration, cache and data paths. @@ -25,10 +26,19 @@ func CachePath(elem ...string) string { return lp.cachePath(elem...) } // DataPath returns the path where Helm stores data. func DataPath(elem ...string) string { return lp.dataPath(elem...) } -// CacheIndex returns the path to an index for the given named repository. +// CacheIndexFile returns the path to an index for the given named repository. func CacheIndexFile(name string) string { if name != "" { name += "-" } return name + "index.yaml" } + +// CacheChartsFile returns the path to a text file listing all the charts +// within the given named repository. +func CacheChartsFile(name string) string { + if name != "" { + name += "-" + } + return name + "charts.txt" +} diff --git a/pkg/helmpath/lazypath.go b/pkg/helmpath/lazypath.go index 5fecfc75f..0b9068671 100644 --- a/pkg/helmpath/lazypath.go +++ b/pkg/helmpath/lazypath.go @@ -10,6 +10,7 @@ // 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 helmpath import ( diff --git a/pkg/helmpath/xdg/xdg.go b/pkg/helmpath/xdg/xdg.go index 010e1138b..eaa3e6864 100644 --- a/pkg/helmpath/xdg/xdg.go +++ b/pkg/helmpath/xdg/xdg.go @@ -1,16 +1,22 @@ -// 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 +/* +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 +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. +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 xdg holds constants pertaining to XDG Base Directory Specification. +// +// The XDG Base Directory Specification https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html +// specifies the environment variables that define user-specific base directories for various categories of files. package xdg const ( diff --git a/pkg/kube/client.go b/pkg/kube/client.go index 96179d19d..3853e9036 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -21,7 +21,6 @@ import ( "encoding/json" "fmt" "io" - "log" "strings" "sync" "time" @@ -30,6 +29,7 @@ import ( "github.com/pkg/errors" batch "k8s.io/api/batch/v1" v1 "k8s.io/api/core/v1" + apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -56,16 +56,23 @@ type Client struct { Log func(string, ...interface{}) } +var addToScheme sync.Once + // New creates a new Client. func New(getter genericclioptions.RESTClientGetter) *Client { if getter == nil { getter = genericclioptions.NewConfigFlags(true) } // Add CRDs to the scheme. They are missing by default. - if err := apiextv1beta1.AddToScheme(scheme.Scheme); err != nil { - // This should never happen. - panic(err) - } + addToScheme.Do(func() { + if err := apiextv1.AddToScheme(scheme.Scheme); err != nil { + // This should never happen. + panic(err) + } + if err := apiextv1beta1.AddToScheme(scheme.Scheme); err != nil { + panic(err) + } + }) return &Client{ Factory: cmdutil.NewFactory(getter), Log: nopLogger, @@ -197,6 +204,11 @@ func (c *Client) Update(original, target ResourceList, force bool) (*Result, err } for _, info := range original.Difference(target) { + if info.Mapping.GroupVersionKind.Kind == "CustomResourceDefinition" { + c.Log("Skipping the deletion of CustomResourceDefinition %q", info.Name) + continue + } + c.Log("Deleting %q in %s...", info.Name, info.Namespace) res.Deleted = append(res.Deleted, info) if err := deleteResource(info); err != nil { @@ -219,6 +231,11 @@ func (c *Client) Delete(resources ResourceList) (*Result, []error) { var errs []error res := &Result{} err := perform(resources, func(info *resource.Info) error { + if info.Mapping.GroupVersionKind.Kind == "CustomResourceDefinition" { + c.Log("Skipping the deletion of CustomResourceDefinition %q", info.Name) + return nil + } + c.Log("Starting delete for %q %s", info.Name, info.Mapping.GroupVersionKind.Kind) if err := c.skipIfNotFound(deleteResource(info)); err != nil { // Collect the error and continue on @@ -355,7 +372,12 @@ func createPatch(target *resource.Info, current runtime.Object) ([]byte, types.P // returned from ConvertToVersion. Anything that's unstructured should // use the jsonpatch.CreateMergePatch. Strategic Merge Patch is not supported // on objects like CRDs. - if _, ok := versionedObject.(runtime.Unstructured); ok { + _, isUnstructured := versionedObject.(runtime.Unstructured) + + // On newer K8s versions, CRDs aren't unstructured but has this dedicated type + _, isCRD := versionedObject.(*apiextv1beta1.CustomResourceDefinition) + + if isUnstructured || isCRD { // fall back to generic JSON merge patch patch, err := jsonpatch.CreateMergePatch(oldData, newData) return patch, types.MergePatchType, err @@ -398,7 +420,7 @@ func updateResource(c *Client, target *resource.Info, currentObj runtime.Object, if err != nil { return errors.Wrap(err, "failed to replace object") } - log.Printf("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\n", target.Name, currentObj.GetObjectKind().GroupVersionKind().Kind, kind) } else { // send patch to server obj, err = helper.Patch(target.Namespace, target.Name, patchType, patch, nil) diff --git a/pkg/kube/fake/printer.go b/pkg/kube/fake/printer.go index ec75aa790..58b389ab5 100644 --- a/pkg/kube/fake/printer.go +++ b/pkg/kube/fake/printer.go @@ -33,7 +33,7 @@ type PrintingKubeClient struct { Out io.Writer } -// isReachable checks if the cluster is reachable +// IsReachable checks if the cluster is reachable func (p *PrintingKubeClient) IsReachable() error { return nil } diff --git a/pkg/kube/resource_test.go b/pkg/kube/resource_test.go index 2e13c3253..3c906ceca 100644 --- a/pkg/kube/resource_test.go +++ b/pkg/kube/resource_test.go @@ -37,6 +37,10 @@ func TestResourceList(t *testing.T) { r1 = []*resource.Info{info("foo"), info("bar")} r2 = []*resource.Info{info("bar")} + if r1.Get(info("bar")).Mapping.Resource.Resource != "pod" { + t.Error("expected get pod") + } + diff := r1.Difference(r2) if len(diff) != 1 { t.Error("expected 1 result") diff --git a/pkg/plugin/plugin_test.go b/pkg/plugin/plugin_test.go index f637eca28..c869e4c86 100644 --- a/pkg/plugin/plugin_test.go +++ b/pkg/plugin/plugin_test.go @@ -87,6 +87,9 @@ func TestPlatformPrepareCommand(t *testing.T) { PlatformCommand: []PlatformCommand{ {OperatingSystem: "linux", Architecture: "i386", Command: "echo -n linux-i386"}, {OperatingSystem: "linux", Architecture: "amd64", Command: "echo -n linux-amd64"}, + {OperatingSystem: "linux", Architecture: "arm64", Command: "echo -n linux-arm64"}, + {OperatingSystem: "linux", Architecture: "ppc64le", Command: "echo -n linux-ppc64le"}, + {OperatingSystem: "linux", Architecture: "s390x", Command: "echo -n linux-s390x"}, {OperatingSystem: "windows", Architecture: "amd64", Command: "echo -n win-64"}, }, }, @@ -98,6 +101,12 @@ func TestPlatformPrepareCommand(t *testing.T) { osStrCmp = "linux-i386" } else if os == "linux" && arch == "amd64" { osStrCmp = "linux-amd64" + } else if os == "linux" && arch == "arm64" { + osStrCmp = "linux-arm64" + } else if os == "linux" && arch == "ppc64le" { + osStrCmp = "linux-ppc64le" + } else if os == "linux" && arch == "s390x" { + osStrCmp = "linux-s390x" } else if os == "windows" && arch == "amd64" { osStrCmp = "win-64" } else { diff --git a/pkg/releaseutil/kind_sorter.go b/pkg/releaseutil/kind_sorter.go index a5110a100..92ffa03f2 100644 --- a/pkg/releaseutil/kind_sorter.go +++ b/pkg/releaseutil/kind_sorter.go @@ -31,12 +31,12 @@ var InstallOrder KindSortOrder = []string{ "LimitRange", "PodSecurityPolicy", "PodDisruptionBudget", + "ServiceAccount", "Secret", "ConfigMap", "StorageClass", "PersistentVolume", "PersistentVolumeClaim", - "ServiceAccount", "CustomResourceDefinition", "ClusterRole", "ClusterRoleList", @@ -85,12 +85,12 @@ var UninstallOrder KindSortOrder = []string{ "ClusterRoleList", "ClusterRole", "CustomResourceDefinition", - "ServiceAccount", "PersistentVolumeClaim", "PersistentVolume", "StorageClass", "ConfigMap", "Secret", + "ServiceAccount", "PodDisruptionBudget", "PodSecurityPolicy", "LimitRange", @@ -101,10 +101,10 @@ var UninstallOrder KindSortOrder = []string{ // sortByKind does an in-place sort of manifests by Kind. // -// Results are sorted by 'ordering' +// Results are sorted by 'ordering', keeping order of items with equal kind/priority func sortByKind(manifests []Manifest, ordering KindSortOrder) []Manifest { ks := newKindSorter(manifests, ordering) - sort.Sort(ks) + sort.Stable(ks) return ks.manifests } @@ -134,13 +134,13 @@ func (k *kindSorter) Less(i, j int) bool { b := k.manifests[j] first, aok := k.ordering[a.Head.Kind] second, bok := k.ordering[b.Head.Kind] - // if same kind (including unknown) sub sort alphanumeric - if first == second { - // if both are unknown and of different kind sort by kind alphabetically - if !aok && !bok && a.Head.Kind != b.Head.Kind { + + 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 } - return a.Name < b.Name + return first < second } // unknown kind is last if !aok { @@ -149,6 +149,6 @@ func (k *kindSorter) Less(i, j int) bool { if !bok { return true } - // sort different kinds + // sort different kinds, keep original order if same priority return first < second } diff --git a/pkg/releaseutil/kind_sorter_test.go b/pkg/releaseutil/kind_sorter_test.go index 93d8ae782..4747e8252 100644 --- a/pkg/releaseutil/kind_sorter_test.go +++ b/pkg/releaseutil/kind_sorter_test.go @@ -40,7 +40,7 @@ func TestKindSorter(t *testing.T) { Head: &SimpleHead{Kind: "ClusterRoleBindingList"}, }, { - Name: "e", + Name: "f", Head: &SimpleHead{Kind: "ConfigMap"}, }, { @@ -84,11 +84,11 @@ func TestKindSorter(t *testing.T) { Head: &SimpleHead{Kind: "NetworkPolicy"}, }, { - Name: "f", + Name: "g", Head: &SimpleHead{Kind: "PersistentVolume"}, }, { - Name: "g", + Name: "h", Head: &SimpleHead{Kind: "PersistentVolumeClaim"}, }, { @@ -132,7 +132,7 @@ func TestKindSorter(t *testing.T) { Head: &SimpleHead{Kind: "RoleBindingList"}, }, { - Name: "d", + Name: "e", Head: &SimpleHead{Kind: "Secret"}, }, { @@ -140,7 +140,7 @@ func TestKindSorter(t *testing.T) { Head: &SimpleHead{Kind: "Service"}, }, { - Name: "h", + Name: "d", Head: &SimpleHead{Kind: "ServiceAccount"}, }, { @@ -166,8 +166,8 @@ func TestKindSorter(t *testing.T) { order KindSortOrder expected string }{ - {"install", InstallOrder, "aAbcC3de1fgh2iIjJkKlLmnopqrxstuvw!"}, - {"uninstall", UninstallOrder, "wvmutsxrqponLlKkJjIi2hgf1ed3CcbAa!"}, + {"install", InstallOrder, "aAbcC3def1gh2iIjJkKlLmnopqrxstuvw!"}, + {"uninstall", UninstallOrder, "wvmutsxrqponLlKkJjIi2hg1fed3CcbAa!"}, } { var buf bytes.Buffer t.Run(test.description, func(t *testing.T) { @@ -185,8 +185,8 @@ func TestKindSorter(t *testing.T) { } } -// TestKindSorterSubSort verifies manifests of same kind are also sorted alphanumeric -func TestKindSorterSubSort(t *testing.T) { +// TestKindSorterKeepOriginalOrder verifies manifests of same kind are kept in original order +func TestKindSorterKeepOriginalOrder(t *testing.T) { manifests := []Manifest{ { Name: "a", @@ -230,8 +230,8 @@ func TestKindSorterSubSort(t *testing.T) { order KindSortOrder expected string }{ - // expectation is sorted by kind (unknown is last) and then sub sorted alphabetically within each group - {"cm,clusterRole,clusterRoleBinding,Unknown,Unknown2", InstallOrder, "01Aa!zu1u2t3"}, + // expectation is sorted by kind (unknown is last) and within each group of same kind, the order is kept + {"cm,clusterRole,clusterRoleBinding,Unknown,Unknown2", InstallOrder, "01aAz!u2u1t3"}, } { var buf bytes.Buffer t.Run(test.description, func(t *testing.T) { @@ -245,3 +245,24 @@ func TestKindSorterSubSort(t *testing.T) { }) } } + +func TestKindSorterNamespaceAgainstUnknown(t *testing.T) { + unknown := Manifest{ + Name: "a", + Head: &SimpleHead{Kind: "Unknown"}, + } + namespace := Manifest{ + Name: "b", + Head: &SimpleHead{Kind: "Namespace"}, + } + + manifests := []Manifest{unknown, namespace} + sortByKind(manifests, InstallOrder) + + expectedOrder := []Manifest{namespace, unknown} + for i, manifest := range manifests { + if expectedOrder[i].Name != manifest.Name { + t.Errorf("Expected %s, got %s", expectedOrder[i].Name, manifest.Name) + } + } +} diff --git a/pkg/releaseutil/manifest.go b/pkg/releaseutil/manifest.go index 37503b390..0b04a4599 100644 --- a/pkg/releaseutil/manifest.go +++ b/pkg/releaseutil/manifest.go @@ -19,6 +19,7 @@ package releaseutil import ( "fmt" "regexp" + "strconv" "strings" ) @@ -37,8 +38,9 @@ var sep = regexp.MustCompile("(?:^|\\s*\n)---\\s*") // SplitManifests takes a string of manifest and returns a map contains individual manifests func SplitManifests(bigFile string) map[string]string { // Basically, we're quickly splitting a stream of YAML documents into an - // array of YAML docs. In the current implementation, the file name is just - // a place holder, and doesn't have any further meaning. + // array of YAML docs. The file name is just a place holder, but should be + // integer-sortable so that manifests get output in the same order as the + // input (see `BySplitManifestsOrder`). tpl := "manifest-%d" res := map[string]string{} // Making sure that any extra whitespace in YAML stream doesn't interfere in splitting documents correctly. @@ -56,3 +58,15 @@ func SplitManifests(bigFile string) map[string]string { } return res } + +// BySplitManifestsOrder sorts by in-file manifest order, as provided in function `SplitManifests` +type BySplitManifestsOrder []string + +func (a BySplitManifestsOrder) Len() int { return len(a) } +func (a BySplitManifestsOrder) Less(i, j int) bool { + // Split `manifest-%d` + anum, _ := strconv.ParseInt(a[i][len("manifest-"):], 10, 0) + bnum, _ := strconv.ParseInt(a[j][len("manifest-"):], 10, 0) + return anum < bnum +} +func (a BySplitManifestsOrder) Swap(i, j int) { a[i], a[j] = a[j], a[i] } diff --git a/pkg/releaseutil/manifest_sorter.go b/pkg/releaseutil/manifest_sorter.go index 5f4b4e8d9..24b0c3c95 100644 --- a/pkg/releaseutil/manifest_sorter.go +++ b/pkg/releaseutil/manifest_sorter.go @@ -19,6 +19,7 @@ package releaseutil import ( "log" "path" + "sort" "strconv" "strings" @@ -74,10 +75,17 @@ var events = map[string]release.HookEvent{ // // Files that do not parse into the expected format are simply placed into a map and // returned. -func SortManifests(files map[string]string, apis chartutil.VersionSet, sort KindSortOrder) ([]*release.Hook, []Manifest, error) { +func SortManifests(files map[string]string, apis chartutil.VersionSet, ordering KindSortOrder) ([]*release.Hook, []Manifest, error) { result := &result{} - for filePath, c := range files { + var sortedFilePaths []string + for filePath := range files { + sortedFilePaths = append(sortedFilePaths, filePath) + } + sort.Strings(sortedFilePaths) + + for _, filePath := range sortedFilePaths { + content := files[filePath] // Skip partials. We could return these as a separate map, but there doesn't // seem to be any need for that at this time. @@ -85,12 +93,12 @@ func SortManifests(files map[string]string, apis chartutil.VersionSet, sort Kind continue } // Skip empty files and log this. - if strings.TrimSpace(c) == "" { + if strings.TrimSpace(content) == "" { continue } manifestFile := &manifestFile{ - entries: SplitManifests(c), + entries: SplitManifests(content), path: filePath, apis: apis, } @@ -100,7 +108,7 @@ func SortManifests(files map[string]string, apis chartutil.VersionSet, sort Kind } } - return result.hooks, sortByKind(result.generic, sort), nil + return result.hooks, sortByKind(result.generic, ordering), nil } // sort takes a manifestFile object which may contain multiple resource definition @@ -123,7 +131,16 @@ func SortManifests(files map[string]string, apis chartutil.VersionSet, sort Kind // annotations: // helm.sh/hook-delete-policy: hook-succeeded func (file *manifestFile) sort(result *result) error { - for _, m := range file.entries { + // Go through manifests in order found in file (function `SplitManifests` creates integer-sortable keys) + var sortedEntryKeys []string + for entryKey := range file.entries { + sortedEntryKeys = append(sortedEntryKeys, entryKey) + } + sort.Sort(BySplitManifestsOrder(sortedEntryKeys)) + + for _, entryKey := range sortedEntryKeys { + m := file.entries[entryKey] + var entry SimpleHead if err := yaml.Unmarshal([]byte(m), &entry); err != nil { return errors.Wrapf(err, "YAML parse error on %s", file.path) diff --git a/pkg/repo/chartrepo.go b/pkg/repo/chartrepo.go index c8d0d6a3d..38b6b8fb0 100644 --- a/pkg/repo/chartrepo.go +++ b/pkg/repo/chartrepo.go @@ -133,10 +133,21 @@ func (r *ChartRepository) DownloadIndexFile() (string, error) { return "", err } - if _, err := loadIndex(index); err != nil { + indexFile, err := loadIndex(index) + if err != nil { return "", err } + // Create the chart list file in the cache directory + var charts strings.Builder + for name := range indexFile.Entries { + fmt.Fprintln(&charts, name) + } + chartsFile := filepath.Join(r.CachePath, helmpath.CacheChartsFile(r.Config.Name)) + os.MkdirAll(filepath.Dir(chartsFile), 0755) + ioutil.WriteFile(chartsFile, []byte(charts.String()), 0644) + + // Create the index file in the cache directory fname := filepath.Join(r.CachePath, helmpath.CacheIndexFile(r.Config.Name)) os.MkdirAll(filepath.Dir(fname), 0755) return fname, ioutil.WriteFile(fname, index, 0644) diff --git a/pkg/repo/index.go b/pkg/repo/index.go index a7fea673b..36386665e 100644 --- a/pkg/repo/index.go +++ b/pkg/repo/index.go @@ -17,8 +17,6 @@ limitations under the License. package repo import ( - "encoding/json" - "fmt" "io/ioutil" "os" "path" @@ -290,48 +288,3 @@ func loadIndex(data []byte) (*IndexFile, error) { } return i, nil } - -// unversionedEntry represents a deprecated pre-Alpha.5 format. -// -// This will be removed prior to v2.0.0 -type unversionedEntry struct { - Checksum string `json:"checksum"` - URL string `json:"url"` - Chartfile *chart.Metadata `json:"chartfile"` -} - -// loadUnversionedIndex loads a pre-Alpha.5 index.yaml file. -// -// This format is deprecated. This function will be removed prior to v2.0.0. -func loadUnversionedIndex(data []byte) (*IndexFile, error) { - fmt.Fprintln(os.Stderr, "WARNING: Deprecated index file format. Try 'helm repo update'") - i := map[string]unversionedEntry{} - - // This gets around an error in the YAML parser. Instead of parsing as YAML, - // we convert to JSON, and then decode again. - var err error - data, err = yaml.YAMLToJSON(data) - if err != nil { - return nil, err - } - if err := json.Unmarshal(data, &i); err != nil { - return nil, err - } - - if len(i) == 0 { - return nil, ErrNoAPIVersion - } - ni := NewIndexFile() - for n, item := range i { - if item.Chartfile == nil || item.Chartfile.Name == "" { - parts := strings.Split(n, "-") - ver := "" - if len(parts) > 1 { - ver = strings.TrimSuffix(parts[1], ".tgz") - } - item.Chartfile = &chart.Metadata{Name: parts[0], Version: ver} - } - ni.Add(item.Chartfile, item.URL, "", item.Checksum) - } - return ni, nil -} diff --git a/pkg/repo/index_test.go b/pkg/repo/index_test.go index c830a339e..5dbd5e551 100644 --- a/pkg/repo/index_test.go +++ b/pkg/repo/index_test.go @@ -17,14 +17,19 @@ limitations under the License. package repo import ( + "bufio" + "bytes" "io/ioutil" "net/http" "os" + "path/filepath" + "sort" "strings" "testing" "helm.sh/helm/v3/pkg/cli" "helm.sh/helm/v3/pkg/getter" + "helm.sh/helm/v3/pkg/helmpath" "helm.sh/helm/v3/pkg/chart" ) @@ -178,6 +183,18 @@ func TestDownloadIndexFile(t *testing.T) { t.Fatalf("Index %q failed to parse: %s", testfile, err) } verifyLocalIndex(t, i) + + // Check that charts file is also created + idx = filepath.Join(r.CachePath, helmpath.CacheChartsFile(r.Config.Name)) + if _, err := os.Stat(idx); err != nil { + t.Fatalf("error finding created charts file: %#v", err) + } + + b, err = ioutil.ReadFile(idx) + if err != nil { + t.Fatalf("error reading charts file: %#v", err) + } + verifyLocalChartsFile(t, b, i) }) t.Run("should not decode the path in the repo url while downloading index", func(t *testing.T) { @@ -224,6 +241,18 @@ func TestDownloadIndexFile(t *testing.T) { t.Fatalf("Index %q failed to parse: %s", testfile, err) } verifyLocalIndex(t, i) + + // Check that charts file is also created + idx = filepath.Join(r.CachePath, helmpath.CacheChartsFile(r.Config.Name)) + if _, err := os.Stat(idx); err != nil { + t.Fatalf("error finding created charts file: %#v", err) + } + + b, err = ioutil.ReadFile(idx) + if err != nil { + t.Fatalf("error reading charts file: %#v", err) + } + verifyLocalChartsFile(t, b, i) }) } @@ -322,6 +351,24 @@ func verifyLocalIndex(t *testing.T, i *IndexFile) { } } +func verifyLocalChartsFile(t *testing.T, chartsContent []byte, indexContent *IndexFile) { + var expected, real []string + for chart := range indexContent.Entries { + expected = append(expected, chart) + } + sort.Strings(expected) + + scanner := bufio.NewScanner(bytes.NewReader(chartsContent)) + for scanner.Scan() { + real = append(real, scanner.Text()) + } + sort.Strings(real) + + if strings.Join(expected, " ") != strings.Join(real, " ") { + t.Errorf("Cached charts file content unexpected. Expected:\n%s\ngot:\n%s", expected, real) + } +} + func TestIndexDirectory(t *testing.T) { dir := "testdata/repository" index, err := IndexDirectory(dir, "http://localhost:8080") @@ -361,26 +408,6 @@ func TestIndexDirectory(t *testing.T) { } } -func TestLoadUnversionedIndex(t *testing.T) { - data, err := ioutil.ReadFile("testdata/unversioned-index.yaml") - if err != nil { - t.Fatal(err) - } - - ind, err := loadUnversionedIndex(data) - if err != nil { - t.Fatal(err) - } - - if l := len(ind.Entries); l != 2 { - t.Fatalf("Expected 2 entries, got %d", l) - } - - if l := len(ind.Entries["mysql"]); l != 3 { - t.Fatalf("Expected 3 mysql versions, got %d", l) - } -} - func TestIndexAdd(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") diff --git a/pkg/repo/testdata/unversioned-index.yaml b/pkg/repo/testdata/unversioned-index.yaml deleted file mode 100644 index 1d814c7ae..000000000 --- a/pkg/repo/testdata/unversioned-index.yaml +++ /dev/null @@ -1,60 +0,0 @@ -memcached-0.1.0: - name: memcached - url: https://mumoshu.github.io/charts/memcached-0.1.0.tgz - created: 2016-08-04 02:05:02.259205055 +0000 UTC - checksum: ce9b76576c4b4eb74286fa30a978c56d69e7a522 - chartfile: - name: memcached - home: http://https://hub.docker.com/_/memcached/ - sources: [] - version: 0.1.0 - description: A simple Memcached cluster - keywords: [] - maintainers: - - name: Matt Butcher - email: mbutcher@deis.com -mysql-0.2.0: - name: mysql - url: https://mumoshu.github.io/charts/mysql-0.2.0.tgz - created: 2016-08-04 00:42:47.517342022 +0000 UTC - checksum: aa5edd2904d639b0b6295f1c7cf4c0a8e4f77dd3 - chartfile: - name: mysql - home: https://www.mysql.com/ - sources: [] - version: 0.2.0 - description: Chart running MySQL. - keywords: [] - maintainers: - - name: Matt Fisher - email: mfisher@deis.com -mysql-0.2.1: - name: mysql - url: https://mumoshu.github.io/charts/mysql-0.2.1.tgz - created: 2016-08-04 02:40:29.717829534 +0000 UTC - checksum: 9d9f056171beefaaa04db75680319ca4edb6336a - chartfile: - name: mysql - home: https://www.mysql.com/ - sources: [] - version: 0.2.1 - description: Chart running MySQL. - keywords: [] - maintainers: - - name: Matt Fisher - email: mfisher@deis.com -mysql-0.2.2: - name: mysql - url: https://mumoshu.github.io/charts/mysql-0.2.2.tgz - created: 2016-08-04 02:40:29.71841952 +0000 UTC - checksum: 6d6810e76a5987943faf0040ec22990d9fb141c7 - chartfile: - name: mysql - home: https://www.mysql.com/ - sources: [] - version: 0.2.2 - description: Chart running MySQL. - keywords: [] - maintainers: - - name: Matt Fisher - email: mfisher@deis.com diff --git a/pkg/storage/driver/cfgmaps.go b/pkg/storage/driver/cfgmaps.go index 1b630f407..cc2e2416a 100644 --- a/pkg/storage/driver/cfgmaps.go +++ b/pkg/storage/driver/cfgmaps.go @@ -217,14 +217,14 @@ func (cfgmaps *ConfigMaps) Delete(key string) (rls *rspb.Release, err error) { // newConfigMapsObject constructs a kubernetes ConfigMap object // to store a release. Each configmap data entry is the base64 -// encoded string of a release's binary protobuf encoding. +// encoded gzipped string of a release. // // The following labels are used within each configmap: // // "modifiedAt" - timestamp indicating when this configmap was last modified. (set in Update) // "createdAt" - timestamp indicating when this configmap was created. (set in Create) // "version" - version of the release. -// "status" - status of the release (see proto/hapi/release.status.pb.go for variants) +// "status" - status of the release (see pkg/release/status.go for variants) // "owner" - owner of the configmap, currently "helm". // "name" - name of the release. // diff --git a/pkg/storage/driver/secrets.go b/pkg/storage/driver/secrets.go index dc55cf458..dcb2ecfcf 100644 --- a/pkg/storage/driver/secrets.go +++ b/pkg/storage/driver/secrets.go @@ -198,14 +198,14 @@ func (secrets *Secrets) Delete(key string) (rls *rspb.Release, err error) { // newSecretsObject constructs a kubernetes Secret object // to store a release. Each secret data entry is the base64 -// encoded string of a release's binary protobuf encoding. +// encoded gzipped string of a release. // // The following labels are used within each secret: // // "modifiedAt" - timestamp indicating when this secret was last modified. (set in Update) // "createdAt" - timestamp indicating when this secret was created. (set in Create) // "version" - version of the release. -// "status" - status of the release (see proto/hapi/release.status.pb.go for variants) +// "status" - status of the release (see pkg/release/status.go for variants) // "owner" - owner of the secret, currently "helm". // "name" - name of the release. // diff --git a/pkg/storage/driver/util.go b/pkg/storage/driver/util.go index 6cde74cd3..a87002ab4 100644 --- a/pkg/storage/driver/util.go +++ b/pkg/storage/driver/util.go @@ -31,7 +31,7 @@ var b64 = base64.StdEncoding var magicGzip = []byte{0x1f, 0x8b, 0x08} // encodeRelease encodes a release returning a base64 encoded -// gzipped binary protobuf encoding representation, or error. +// gzipped string representation, or error. func encodeRelease(rls *rspb.Release) (string, error) { b, err := json.Marshal(rls) if err != nil { @@ -50,10 +50,9 @@ func encodeRelease(rls *rspb.Release) (string, error) { return b64.EncodeToString(buf.Bytes()), nil } -// decodeRelease decodes the bytes in data into a release -// type. Data must contain a base64 encoded string of a -// valid protobuf encoding of a release, otherwise -// an error is returned. +// decodeRelease decodes the bytes of data into a release +// type. Data must contain a base64 encoded gzipped string of a +// valid release, otherwise an error is returned. func decodeRelease(data string) (*rspb.Release, error) { // base64 decode string b, err := b64.DecodeString(data) @@ -77,7 +76,7 @@ func decodeRelease(data string) (*rspb.Release, error) { } var rls rspb.Release - // unmarshal protobuf bytes + // unmarshal release object bytes if err := json.Unmarshal(b, &rls); err != nil { return nil, err } diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go index 58a7eb06f..89183355f 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 { @@ -112,9 +112,6 @@ func (s *Storage) ListDeployed() ([]*rspb.Release, error) { func (s *Storage) Deployed(name string) (*rspb.Release, error) { ls, err := s.DeployedAll(name) if err != nil { - if strings.Contains(err.Error(), "not found") { - return nil, errors.Errorf("%q has no deployed releases", name) - } return nil, err } @@ -122,7 +119,11 @@ func (s *Storage) Deployed(name string) (*rspb.Release, error) { return nil, errors.Errorf("%q has no deployed releases", name) } - return ls[0], err + // If executed concurrently, Helm's database gets corrupted + // and multiple releases are DEPLOYED. Take the latest. + relutil.Reverse(ls, relutil.SortByRevision) + + return ls[0], nil } // DeployedAll returns all deployed releases with the provided name, or diff --git a/pkg/storage/storage_test.go b/pkg/storage/storage_test.go index b8c333d24..ee9c68b80 100644 --- a/pkg/storage/storage_test.go +++ b/pkg/storage/storage_test.go @@ -205,6 +205,46 @@ func TestStorageDeployed(t *testing.T) { } } +func TestStorageDeployedWithCorruption(t *testing.T) { + storage := Init(driver.NewMemory()) + + const name = "angry-bird" + const vers = int(4) + + // setup storage with test releases + setup := func() { + // release records (notice odd order and corruption) + rls0 := ReleaseTestData{Name: name, Version: 1, Status: rspb.StatusSuperseded}.ToRelease() + rls1 := ReleaseTestData{Name: name, Version: 4, Status: rspb.StatusDeployed}.ToRelease() + rls2 := ReleaseTestData{Name: name, Version: 3, Status: rspb.StatusSuperseded}.ToRelease() + rls3 := ReleaseTestData{Name: name, Version: 2, Status: rspb.StatusDeployed}.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() + + rls, err := storage.Deployed(name) + if err != nil { + t.Fatalf("Failed to query for deployed release: %s\n", err) + } + + switch { + case rls == nil: + t.Fatalf("Release is nil") + case rls.Name != name: + t.Fatalf("Expected release name %q, actual %q\n", name, rls.Name) + case rls.Version != vers: + t.Fatalf("Expected release version %d, actual %d\n", vers, rls.Version) + case rls.Info.Status != rspb.StatusDeployed: + t.Fatalf("Expected release status 'DEPLOYED', actual %s\n", rls.Info.Status.String()) + } +} + func TestStorageHistory(t *testing.T) { storage := Init(driver.NewMemory())