Merge remote-tracking branch 'helm/master'

pull/7767/head
Liu Ming 5 years ago
commit 376a5ca550

@ -10,6 +10,7 @@
- [Microsoft](https://microsoft.com)
- [Qovery](https://www.qovery.com/)
- [Samsung SDS](https://www.samsungsds.com/)
- [Softonic](https://hello.softonic.com/)
- [Ville de Montreal](https://montreal.ca)
_This file is part of the CNCF official documentation for projects._

@ -52,7 +52,7 @@ func newCompletionCmd(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "completion SHELL",
Short: "Generate autocompletions script for the specified shell (bash or zsh)",
Short: "generate autocompletions script for the specified shell (bash or zsh)",
Long: completionDesc,
RunE: func(cmd *cobra.Command, args []string) error {
return runCompletion(out, cmd, args)

@ -71,7 +71,7 @@ func newCreateCmd(out io.Writer) *cobra.Command {
},
}
cmd.Flags().StringVarP(&o.starter, "starter", "p", "", "The name or absolute path to Helm starter scaffold")
cmd.Flags().StringVarP(&o.starter, "starter", "p", "", "the name or absolute path to Helm starter scaffold")
return cmd
}

@ -40,7 +40,7 @@ func newEnvCmd(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "env",
Short: "Helm client environment information",
Short: "helm client environment information",
Long: envHelp,
Args: require.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {

@ -32,7 +32,6 @@ import (
// Import to initialize client auth plugins.
_ "k8s.io/client-go/plugin/pkg/client/auth"
"helm.sh/helm/v3/internal/completion"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/cli"
"helm.sh/helm/v3/pkg/gates"
@ -73,16 +72,6 @@ func main() {
actionConfig := new(action.Configuration)
cmd := newRootCmd(actionConfig, os.Stdout, os.Args[1:])
if calledCmd, _, err := cmd.Find(os.Args[1:]); err == nil && calledCmd.Name() == completion.CompRequestCmd {
// If completion is being called, we have to check if the completion is for the "--kube-context"
// value; if it is, we cannot call the action.Init() method with an incomplete kube-context value
// or else it will fail immediately. So, we simply unset the invalid kube-context value.
if args := os.Args[1:]; len(args) > 2 && args[len(args)-2] == "--kube-context" {
// We are completing the kube-context value! Reset it as the current value is not valid.
settings.KubeContext = ""
}
}
helmDriver := os.Getenv("HELM_DRIVER")
if err := actionConfig.Init(settings.RESTClientGetter(), settings.Namespace(), helmDriver, debug); err != nil {
log.Fatal(err)

@ -97,10 +97,15 @@ func storageFixture() *storage.Storage {
}
func executeActionCommandC(store *storage.Storage, cmd string) (*cobra.Command, string, error) {
return executeActionCommandStdinC(store, nil, cmd)
}
func executeActionCommandStdinC(store *storage.Storage, in *os.File, cmd string) (*cobra.Command, string, error) {
args, err := shellwords.Parse(cmd)
if err != nil {
return nil, "", err
}
buf := new(bytes.Buffer)
actionConfig := &action.Configuration{
@ -111,15 +116,26 @@ func executeActionCommandC(store *storage.Storage, cmd string) (*cobra.Command,
}
root := newRootCmd(actionConfig, buf, args)
root.SetOutput(buf)
root.SetOut(buf)
root.SetErr(buf)
root.SetArgs(args)
oldStdin := os.Stdin
if in != nil {
root.SetIn(in)
os.Stdin = in
}
if mem, ok := store.Driver.(*driver.Memory); ok {
mem.SetNamespace(settings.Namespace())
}
c, err := root.ExecuteC()
return c, buf.String(), err
result := buf.String()
os.Stdin = oldStdin
return c, result, err
}
// cmdTestCase describes a test case that works with releases.

@ -148,7 +148,7 @@ func addInstallFlags(f *pflag.FlagSet, client *action.Install, valueOpts *values
f.BoolVar(&client.Devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored")
f.BoolVar(&client.DependencyUpdate, "dependency-update", false, "run helm dependency update before installing the chart")
f.BoolVar(&client.DisableOpenAPIValidation, "disable-openapi-validation", false, "if set, the installation process will not validate rendered templates against the Kubernetes OpenAPI Schema")
f.BoolVar(&client.Atomic, "atomic", false, "if set, installation process purges chart on fail. The --wait flag will be set automatically if --atomic is used")
f.BoolVar(&client.Atomic, "atomic", false, "if set, the installation process deletes the installation on failure. The --wait flag will be set automatically if --atomic is used")
f.BoolVar(&client.SkipCRDs, "skip-crds", false, "if set, no CRDs will be installed. By default, CRDs are installed if not already present")
f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent")
addValueOptionsFlags(f, valueOpts)
@ -210,10 +210,15 @@ func runInstall(args []string, client *action.Install, valueOpts *values.Options
Getters: p,
RepositoryConfig: settings.RepositoryConfig,
RepositoryCache: settings.RepositoryCache,
Debug: settings.Debug,
}
if err := man.Update(); err != nil {
return nil, err
}
// Reload the chart with the updated Chart.lock file.
if chartRequested, err = loader.Load(cp); err != nil {
return nil, errors.Wrap(err, "failed reloading chart after repo update")
}
} else {
return nil, err
}

@ -111,6 +111,12 @@ func TestInstall(t *testing.T) {
cmd: "install nodeps testdata/testcharts/chart-missing-deps",
wantError: true,
},
// Install chart with update-dependency
{
name: "install chart with missing dependencies",
cmd: "install --dependency-update updeps testdata/testcharts/chart-with-subchart-update",
golden: "output/chart-with-subchart-update.txt",
},
// Install, chart with bad dependencies in Chart.yaml in /charts
{
name: "install chart with bad dependencies in Chart.yaml",

@ -82,7 +82,7 @@ func newReleaseTestCmd(cfg *action.Configuration, out io.Writer) *cobra.Command
f := cmd.Flags()
f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)")
f.BoolVar(&outputLogs, "logs", false, "Dump the logs from test pods (this runs after all tests are complete, but before any cleanup)")
f.BoolVar(&outputLogs, "logs", false, "dump the logs from test pods (this runs after all tests are complete, but before any cleanup)")
return cmd
}

@ -97,13 +97,38 @@ func (r *repoListWriter) encodeByFormat(out io.Writer, format output.Format) err
return nil
}
// Returns all repos from repos, except those with names matching ignoredRepoNames
// Inspired by https://stackoverflow.com/a/28701031/893211
func filterRepos(repos []*repo.Entry, ignoredRepoNames []string) []*repo.Entry {
// if ignoredRepoNames is nil, just return repo
if ignoredRepoNames == nil {
return repos
}
filteredRepos := make([]*repo.Entry, 0)
ignored := make(map[string]bool, len(ignoredRepoNames))
for _, repoName := range ignoredRepoNames {
ignored[repoName] = true
}
for _, repo := range repos {
if _, removed := ignored[repo.Name]; !removed {
filteredRepos = append(filteredRepos, repo)
}
}
return filteredRepos
}
// Provide dynamic auto-completion for repo names
func compListRepos(prefix string) []string {
func compListRepos(prefix string, ignoredRepoNames []string) []string {
var rNames []string
f, err := repo.LoadFile(settings.RepositoryConfig)
if err == nil && len(f.Repositories) > 0 {
for _, repo := range f.Repositories {
filteredRepos := filterRepos(f.Repositories, ignoredRepoNames)
for _, repo := range filteredRepos {
if strings.HasPrefix(repo.Name, prefix) {
rNames = append(rNames, repo.Name)
}

@ -32,7 +32,7 @@ import (
)
type repoRemoveOptions struct {
name string
names []string
repoFile string
repoCache string
}
@ -41,24 +41,21 @@ func newRepoRemoveCmd(out io.Writer) *cobra.Command {
o := &repoRemoveOptions{}
cmd := &cobra.Command{
Use: "remove [NAME]",
Use: "remove [REPO1 [REPO2 ...]]",
Aliases: []string{"rm"},
Short: "remove a chart repository",
Args: require.ExactArgs(1),
Short: "remove one or more chart repositories",
Args: require.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
o.repoFile = settings.RepositoryConfig
o.repoCache = settings.RepositoryCache
o.name = args[0]
o.names = args
return o.run(out)
},
}
// Function providing dynamic auto-completion
completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) {
if len(args) != 0 {
return nil, completion.BashCompDirectiveNoFileComp
}
return compListRepos(toComplete), completion.BashCompDirectiveNoFileComp
return compListRepos(toComplete, args), completion.BashCompDirectiveNoFileComp
})
return cmd
@ -70,18 +67,20 @@ func (o *repoRemoveOptions) run(out io.Writer) error {
return errors.New("no repositories configured")
}
if !r.Remove(o.name) {
return errors.Errorf("no repo named %q found", o.name)
}
if err := r.WriteFile(o.repoFile, 0644); err != nil {
return err
}
for _, name := range o.names {
if !r.Remove(name) {
return errors.Errorf("no repo named %q found", name)
}
if err := r.WriteFile(o.repoFile, 0644); err != nil {
return err
}
if err := removeRepoCache(o.repoCache, o.name); err != nil {
return err
if err := removeRepoCache(o.repoCache, name); err != nil {
return err
}
fmt.Fprintf(out, "%q has been removed from your repositories\n", name)
}
fmt.Fprintf(out, "%q has been removed from your repositories\n", o.name)
return nil
}

@ -44,7 +44,7 @@ func TestRepoRemove(t *testing.T) {
b := bytes.NewBuffer(nil)
rmOpts := repoRemoveOptions{
name: testRepoName,
names: []string{testRepoName},
repoFile: repoFile,
repoCache: rootDir,
}
@ -62,14 +62,9 @@ func TestRepoRemove(t *testing.T) {
t.Error(err)
}
idx := filepath.Join(rootDir, helmpath.CacheIndexFile(testRepoName))
mf, _ := os.Create(idx)
mf.Close()
idx2 := filepath.Join(rootDir, helmpath.CacheChartsFile(testRepoName))
mf, _ = os.Create(idx2)
mf.Close()
cacheIndexFile, cacheChartsFile := createCacheFiles(rootDir, testRepoName)
// Reset the buffer before running repo remove
b.Reset()
if err := rmOpts.run(b); err != nil {
@ -79,13 +74,7 @@ func TestRepoRemove(t *testing.T) {
t.Errorf("Unexpected output: %s", b.String())
}
if _, err := os.Stat(idx); err == nil {
t.Errorf("Error cache index file was not removed for repository %s", testRepoName)
}
if _, err := os.Stat(idx2); err == nil {
t.Errorf("Error cache chart file was not removed for repository %s", testRepoName)
}
testCacheFiles(t, cacheIndexFile, cacheChartsFile, testRepoName)
f, err := repo.LoadFile(repoFile)
if err != nil {
@ -95,4 +84,79 @@ func TestRepoRemove(t *testing.T) {
if f.Has(testRepoName) {
t.Errorf("%s was not successfully removed from repositories list", testRepoName)
}
// Test removal of multiple repos in one go
var testRepoNames = []string{"foo", "bar", "baz"}
cacheFiles := make(map[string][]string, len(testRepoNames))
// Add test repos
for _, repoName := range testRepoNames {
o := &repoAddOptions{
name: repoName,
url: ts.URL(),
repoFile: repoFile,
}
if err := o.run(os.Stderr); err != nil {
t.Error(err)
}
cacheIndex, cacheChart := createCacheFiles(rootDir, repoName)
cacheFiles[repoName] = []string{cacheIndex, cacheChart}
}
// Create repo remove command
multiRmOpts := repoRemoveOptions{
names: testRepoNames,
repoFile: repoFile,
repoCache: rootDir,
}
// Reset the buffer before running repo remove
b.Reset()
// Run repo remove command
if err := multiRmOpts.run(b); err != nil {
t.Errorf("Error removing list of repos from repositories: %q", testRepoNames)
}
// Check that stuff were removed
if !strings.Contains(b.String(), "has been removed") {
t.Errorf("Unexpected output: %s", b.String())
}
for _, repoName := range testRepoNames {
f, err := repo.LoadFile(repoFile)
if err != nil {
t.Error(err)
}
if f.Has(repoName) {
t.Errorf("%s was not successfully removed from repositories list", repoName)
}
cacheIndex := cacheFiles[repoName][0]
cacheChart := cacheFiles[repoName][1]
testCacheFiles(t, cacheIndex, cacheChart, repoName)
}
}
func createCacheFiles(rootDir string, repoName string) (cacheIndexFile string, cacheChartsFile string) {
cacheIndexFile = filepath.Join(rootDir, helmpath.CacheIndexFile(repoName))
mf, _ := os.Create(cacheIndexFile)
mf.Close()
cacheChartsFile = filepath.Join(rootDir, helmpath.CacheChartsFile(repoName))
mf, _ = os.Create(cacheChartsFile)
mf.Close()
return cacheIndexFile, cacheChartsFile
}
func testCacheFiles(t *testing.T, cacheIndexFile string, cacheChartsFile string, repoName string) {
if _, err := os.Stat(cacheIndexFile); err == nil {
t.Errorf("Error cache index file was not removed for repository %s", repoName)
}
if _, err := os.Stat(cacheChartsFile); err == nil {
t.Errorf("Error cache chart file was not removed for repository %s", repoName)
}
}

@ -17,6 +17,7 @@ limitations under the License.
package main // import "helm.sh/helm/v3/cmd/helm"
import (
"context"
"fmt"
"io"
"strings"
@ -92,7 +93,7 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string
completion.CompDebugln(fmt.Sprintf("About to call kube client for namespaces with timeout of: %d", to))
nsNames := []string{}
if namespaces, err := client.CoreV1().Namespaces().List(metav1.ListOptions{TimeoutSeconds: &to}); err == nil {
if namespaces, err := client.CoreV1().Namespaces().List(context.Background(), metav1.ListOptions{TimeoutSeconds: &to}); err == nil {
for _, ns := range namespaces.Items {
if strings.HasPrefix(ns.Name, toComplete) {
nsNames = append(nsNames, ns.Name)

@ -298,7 +298,7 @@ func compListCharts(toComplete string, includeFiles bool) ([]string, completion.
var completions []string
// First check completions for repos
repos := compListRepos("")
repos := compListRepos("", nil)
for _, repo := range repos {
repoWithSlash := fmt.Sprintf("%s/", repo)
if strings.HasPrefix(toComplete, repoWithSlash) {

@ -0,0 +1,8 @@
NAME: updeps
LAST DEPLOYED: Fri Sep 2 22:04:05 1977
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
PARENT NOTES

@ -0,0 +1,8 @@
apiVersion: v2
description: Chart with subchart that needs to be fetched
name: chart-with-subchart-update
version: 0.0.1
dependencies:
- name: subchart-with-notes
version: 0.0.1
repository: file://../chart-with-subchart-notes/charts

@ -0,0 +1,4 @@
apiVersion: v2
description: Subchart with notes
name: subchart-with-notes
version: 0.0.1

@ -75,21 +75,8 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
client.Namespace = settings.Namespace()
if client.Version == "" && client.Devel {
debug("setting version to >0.0.0-0")
client.Version = ">0.0.0-0"
}
vals, err := valueOpts.MergeValues(getter.All(settings))
if err != nil {
return err
}
chartPath, err := client.ChartPathOptions.LocateChart(args[1], settings)
if err != nil {
return err
}
// Fixes #7002 - Support reading values from STDIN for `upgrade` command
// Must load values AFTER determining if we have to call install so that values loaded from stdin are are not read twice
if client.Install {
// If a release does not exist, install it.
histClient := action.NewHistory(cfg)
@ -124,6 +111,21 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
}
}
if client.Version == "" && client.Devel {
debug("setting version to >0.0.0-0")
client.Version = ">0.0.0-0"
}
chartPath, err := client.ChartPathOptions.LocateChart(args[1], settings)
if err != nil {
return err
}
vals, err := valueOpts.MergeValues(getter.All(settings))
if err != nil {
return err
}
// Check chart dependencies to make sure all are present in /charts
ch, err := loader.Load(chartPath)
if err != nil {

@ -19,6 +19,7 @@ package main
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
@ -256,6 +257,69 @@ func TestUpgradeWithValuesFile(t *testing.T) {
}
func TestUpgradeWithValuesFromStdin(t *testing.T) {
releaseName := "funny-bunny-v5"
relMock, ch, chartPath := prepareMockRelease(releaseName, t)
defer resetEnv()()
store := storageFixture()
store.Create(relMock(releaseName, 3, ch))
in, err := os.Open("testdata/testcharts/upgradetest/values.yaml")
if err != nil {
t.Errorf("unexpected error, got '%v'", err)
}
cmd := fmt.Sprintf("upgrade %s --values - '%s'", releaseName, chartPath)
_, _, err = executeActionCommandStdinC(store, in, cmd)
if err != nil {
t.Errorf("unexpected error, got '%v'", err)
}
updatedRel, err := store.Get(releaseName, 4)
if err != nil {
t.Errorf("unexpected error, got '%v'", err)
}
if !strings.Contains(updatedRel.Manifest, "drink: beer") {
t.Errorf("The value is not set correctly. manifest: %s", updatedRel.Manifest)
}
}
func TestUpgradeInstallWithValuesFromStdin(t *testing.T) {
releaseName := "funny-bunny-v6"
_, _, chartPath := prepareMockRelease(releaseName, t)
defer resetEnv()()
store := storageFixture()
in, err := os.Open("testdata/testcharts/upgradetest/values.yaml")
if err != nil {
t.Errorf("unexpected error, got '%v'", err)
}
cmd := fmt.Sprintf("upgrade %s -f - --install '%s'", releaseName, chartPath)
_, _, err = executeActionCommandStdinC(store, in, cmd)
if err != nil {
t.Errorf("unexpected error, got '%v'", err)
}
updatedRel, err := store.Get(releaseName, 1)
if err != nil {
t.Errorf("unexpected error, got '%v'", err)
}
if !strings.Contains(updatedRel.Manifest, "drink: beer") {
t.Errorf("The value is not set correctly. manifest: %s", updatedRel.Manifest)
}
}
func prepareMockRelease(releaseName string, t *testing.T) (func(n string, v int, ch *chart.Chart) *release.Release, *chart.Chart, string) {
tmpChart := ensure.TempDir(t)
configmapData, err := ioutil.ReadFile("testdata/testcharts/upgradetest/templates/configmap.yaml")

@ -39,6 +39,14 @@ version.BuildInfo{Version:"v2.0.0", GitCommit:"ff52399e51bb880526e9cd0ed8386f643
- GitCommit is the SHA for the commit that this version was built from.
- GitTreeState is "clean" if there are no local code changes when this binary was
built, and "dirty" if the binary was built from locally modified code.
When using the --template flag the following properties are available to use in
the template:
- .Version contains the semantic version of Helm
- .GitCommit is the git commit
- .GitTreeState is the state of the git tree when Helm was built
- .GoVersion contains the version of Go that Helm was compiled with
`
type versionOptions struct {

@ -24,19 +24,19 @@ require (
github.com/opencontainers/image-spec v1.0.1
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.4.2
github.com/spf13/cobra v0.0.5
github.com/spf13/cobra v1.0.0
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.4.0
github.com/xeipuuv/gojsonschema v1.1.0
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d
k8s.io/api v0.17.3
k8s.io/apiextensions-apiserver v0.17.3
k8s.io/apimachinery v0.17.3
k8s.io/cli-runtime v0.17.3
k8s.io/client-go v0.17.3
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975
k8s.io/api v0.18.0
k8s.io/apiextensions-apiserver v0.18.0
k8s.io/apimachinery v0.18.0
k8s.io/cli-runtime v0.18.0
k8s.io/client-go v0.18.0
k8s.io/klog v1.0.0
k8s.io/kubectl v0.17.3
sigs.k8s.io/yaml v1.1.0
k8s.io/kubectl v0.18.0
sigs.k8s.io/yaml v1.2.0
)
replace (

@ -39,6 +39,7 @@ github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tT
github.com/Microsoft/hcsshim v0.8.7 h1:ptnOoufxGSzauVTsdE+wMYnCWA301PdoN4xg5oRdZpg=
github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
@ -78,6 +79,7 @@ github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0Bsq
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o=
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
@ -94,6 +96,7 @@ github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv
github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
@ -103,8 +106,11 @@ github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg=
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
@ -119,6 +125,7 @@ github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8l
github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
github.com/docker/cli v0.0.0-20200130152716-5d0cf8839492 h1:FwssHbCDJD025h+BchanCwE1Q8fyMgqDr2mOQAWOLGw=
github.com/docker/cli v0.0.0-20200130152716-5d0cf8839492/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
@ -144,6 +151,7 @@ github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:Htrtb
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e h1:p1yVGRW3nmb85p1Sh1ZJSDm4A4iKLS5QNbvUHMgGu/M=
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk=
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
@ -169,6 +177,7 @@ github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
@ -232,6 +241,8 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekf
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903 h1:LbsanbbD6LieFkXbj9YNNBupiGHJgFeLpO0j0Fza1h8=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -252,6 +263,8 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@ -260,6 +273,8 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d h1:7XGaL1e6bYS1yIonGp9761ExpPPV1ui0SAC59Yube9k=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/gnostic v0.1.0 h1:rVsPeBmXbYv4If/cumu1AzZPwV58q433hvONV1UEZoI=
github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/391+sT5o=
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
@ -273,8 +288,10 @@ github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY=
github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
@ -360,11 +377,13 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
@ -397,6 +416,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
@ -404,20 +424,30 @@ github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8=
github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
@ -426,6 +456,7 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
@ -434,6 +465,8 @@ github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKv
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
@ -442,6 +475,7 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
@ -453,6 +487,8 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
@ -473,6 +509,7 @@ github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMzt
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY=
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
@ -482,6 +519,7 @@ go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@ -496,6 +534,8 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d h1:9FCpayM9Egr1baVnV1SX0H87m+XB0B8S0hAMi99X/3U=
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo=
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -518,6 +558,7 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -556,6 +597,8 @@ golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3 h1:7TYNF4UdlohbFwpNH04CoPMp1cHUZgO1Ebq5r2hIjfo=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7 h1:HmbHVPwrPEKPGLAcHSrMe6+hqSUlvZU0rab6x5EXfGU=
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -604,8 +647,10 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
@ -639,41 +684,69 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
k8s.io/api v0.17.3 h1:XAm3PZp3wnEdzekNkcmj/9Y1zdmQYJ1I4GKSBBZ8aG0=
k8s.io/api v0.17.3/go.mod h1:YZ0OTkuw7ipbe305fMpIdf3GLXZKRigjtZaV5gzC2J0=
k8s.io/api v0.18.0 h1:lwYk8Vt7rsVTwjRU6pzEsa9YNhThbmbocQlKvNBB4EQ=
k8s.io/api v0.18.0/go.mod h1:q2HRQkfDzHMBZL9l/y9rH63PkQl4vae0xRT+8prbrK8=
k8s.io/apiextensions-apiserver v0.17.3 h1:WDZWkPcbgvchEdDd7ysL21GGPx3UKZQLDZXEkevT6n4=
k8s.io/apiextensions-apiserver v0.17.3/go.mod h1:CJbCyMfkKftAd/X/V6OTHYhVn7zXnDdnkUjS1h0GTeY=
k8s.io/apiextensions-apiserver v0.18.0 h1:HN4/P8vpGZFvB5SOMuPPH2Wt9Y/ryX+KRvIyAkchu1Q=
k8s.io/apiextensions-apiserver v0.18.0/go.mod h1:18Cwn1Xws4xnWQNC00FLq1E350b9lUF+aOdIWDOZxgo=
k8s.io/apimachinery v0.17.3 h1:f+uZV6rm4/tHE7xXgLyToprg6xWairaClGVkm2t8omg=
k8s.io/apimachinery v0.17.3/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g=
k8s.io/apimachinery v0.18.0 h1:fuPfYpk3cs1Okp/515pAf0dNhL66+8zk8RLbSX+EgAE=
k8s.io/apimachinery v0.18.0/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA=
k8s.io/apiserver v0.17.3/go.mod h1:iJtsPpu1ZpEnHaNawpSV0nYTGBhhX2dUlnn7/QS7QiY=
k8s.io/apiserver v0.18.0/go.mod h1:3S2O6FeBBd6XTo0njUrLxiqk8GNy6wWOftjhJcXYnjw=
k8s.io/cli-runtime v0.17.3 h1:0ZlDdJgJBKsu77trRUynNiWsRuAvAVPBNaQfnt/1qtc=
k8s.io/cli-runtime v0.17.3/go.mod h1:X7idckYphH4SZflgNpOOViSxetiMj6xI0viMAjM81TA=
k8s.io/cli-runtime v0.18.0 h1:jG8XpSqQ5TrV0N+EZ3PFz6+gqlCk71dkggWCCq9Mq34=
k8s.io/cli-runtime v0.18.0/go.mod h1:1eXfmBsIJosjn9LjEBUd2WVPoPAY9XGTqTFcPMIBsUQ=
k8s.io/client-go v0.17.3 h1:deUna1Ksx05XeESH6XGCyONNFfiQmDdqeqUvicvP6nU=
k8s.io/client-go v0.17.3/go.mod h1:cLXlTMtWHkuK4tD360KpWz2gG2KtdWEr/OT02i3emRQ=
k8s.io/client-go v0.18.0 h1:yqKw4cTUQraZK3fcVCMeSa+lqKwcjZ5wtcOIPnxQno4=
k8s.io/client-go v0.18.0/go.mod h1:uQSYDYs4WhVZ9i6AIoEZuwUggLVEF64HOD37boKAtF8=
k8s.io/code-generator v0.17.3/go.mod h1:l8BLVwASXQZTo2xamW5mQNFCe1XPiAesVq7Y1t7PiQQ=
k8s.io/code-generator v0.18.0/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc=
k8s.io/component-base v0.17.3 h1:hQzTSshY14aLSR6WGIYvmw+w+u6V4d+iDR2iDGMrlUg=
k8s.io/component-base v0.17.3/go.mod h1:GeQf4BrgelWm64PXkIXiPh/XS0hnO42d9gx9BtbZRp8=
k8s.io/component-base v0.18.0 h1:I+lP0fNfsEdTDpHaL61bCAqTZLoiWjEEP304Mo5ZQgE=
k8s.io/component-base v0.18.0/go.mod h1:u3BCg0z1uskkzrnAKFzulmYaEpZF7XC9Pf/uFyb1v2c=
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLyy7rfbeuf1PYyBf973pgyU=
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c h1:/KUFqjjqAcY4Us6luF5RDNZ16KJtb49HfR3ZHB9qYXM=
k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
k8s.io/kubectl v0.17.3 h1:9HHYj07kuFkM+sMJMOyQX29CKWq4lvKAG1UIPxNPMQ4=
k8s.io/kubectl v0.17.3/go.mod h1:NUn4IBY7f7yCMwSop2HCXlw/MVYP4HJBiUmOR3n9w28=
k8s.io/kubectl v0.18.0 h1:hu52Ndq/d099YW+3sS3VARxFz61Wheiq8K9S7oa82Dk=
k8s.io/kubectl v0.18.0/go.mod h1:LOkWx9Z5DXMEg5KtOjHhRiC1fqJPLyCr3KtQgEolCkU=
k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
k8s.io/metrics v0.17.3/go.mod h1:HEJGy1fhHOjHggW9rMDBJBD3YuGroH3Y1pnIRw9FFaI=
k8s.io/metrics v0.18.0/go.mod h1:8aYTW18koXqjLVKL7Ds05RPMX9ipJZI3mywYvBOxXd4=
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f h1:GiPwtSzdP43eI1hpPCbROQCCIgCuiMMNF8YUVLF3vJo=
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89 h1:d4vVOjXm687F1iLSP2q3lyPPuyvTUt3aVoBpi2DqRsU=
k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0=
sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18=
sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
sigs.k8s.io/structured-merge-diff/v3 v3.0.0 h1:dOmIZBMfhcHS09XZkMyUgkq5trg3/jRyJYFZUiaOp8E=
sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI=

@ -17,6 +17,7 @@ limitations under the License.
package util
import (
"context"
"sort"
apps "k8s.io/api/apps/v1"
@ -116,7 +117,7 @@ func GetNewReplicaSet(deployment *apps.Deployment, c appsclient.AppsV1Interface)
// RsListFromClient returns an rsListFunc that wraps the given client.
func RsListFromClient(c appsclient.AppsV1Interface) RsListFunc {
return func(namespace string, options metav1.ListOptions) ([]*apps.ReplicaSet, error) {
rsList, err := c.ReplicaSets(namespace).List(options)
rsList, err := c.ReplicaSets(namespace).List(context.Background(), options)
if err != nil {
return nil, err
}

@ -221,23 +221,23 @@ func (c *Configuration) recordRelease(r *release.Release) {
}
// Init initializes the action configuration
func (c *Configuration) Init(getter genericclioptions.RESTClientGetter, namespace string, helmDriver string, log DebugLog) error {
func (c *Configuration) Init(getter genericclioptions.RESTClientGetter, namespace, helmDriver string, log DebugLog) error {
kc := kube.New(getter)
kc.Log = log
clientset, err := kc.Factory.KubernetesClientSet()
if err != nil {
return err
lazyClient := &lazyClient{
namespace: namespace,
clientFn: kc.Factory.KubernetesClientSet,
}
var store *storage.Storage
switch helmDriver {
case "secret", "secrets", "":
d := driver.NewSecrets(clientset.CoreV1().Secrets(namespace))
d := driver.NewSecrets(newSecretClient(lazyClient))
d.Log = log
store = storage.Init(d)
case "configmap", "configmaps":
d := driver.NewConfigMaps(clientset.CoreV1().ConfigMaps(namespace))
d := driver.NewConfigMaps(newConfigMapClient(lazyClient))
d.Log = log
store = storage.Init(d)
case "memory":

@ -0,0 +1,182 @@
/*
Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package action
import (
"context"
"sync"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/kubernetes"
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
)
// lazyClient is a workaround to deal with Kubernetes having an unstable client API.
// In Kubernetes v1.18 the defaults where removed which broke creating a
// client without an explicit configuration. ಠ_ಠ
type lazyClient struct {
// client caches an initialized kubernetes client
initClient sync.Once
client kubernetes.Interface
clientErr error
// clientFn loads a kubernetes client
clientFn func() (*kubernetes.Clientset, error)
// namespace passed to each client request
namespace string
}
func (s *lazyClient) init() error {
s.initClient.Do(func() {
s.client, s.clientErr = s.clientFn()
})
return s.clientErr
}
// secretClient implements a corev1.SecretsInterface
type secretClient struct{ *lazyClient }
var _ corev1.SecretInterface = (*secretClient)(nil)
func newSecretClient(lc *lazyClient) *secretClient {
return &secretClient{lazyClient: lc}
}
func (s *secretClient) Create(ctx context.Context, secret *v1.Secret, opts metav1.CreateOptions) (*v1.Secret, error) {
if err := s.init(); err != nil {
return nil, err
}
return s.client.CoreV1().Secrets(s.namespace).Create(ctx, secret, opts)
}
func (s *secretClient) Update(ctx context.Context, secret *v1.Secret, opts metav1.UpdateOptions) (*v1.Secret, error) {
if err := s.init(); err != nil {
return nil, err
}
return s.client.CoreV1().Secrets(s.namespace).Update(ctx, secret, opts)
}
func (s *secretClient) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error {
if err := s.init(); err != nil {
return err
}
return s.client.CoreV1().Secrets(s.namespace).Delete(ctx, name, opts)
}
func (s *secretClient) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error {
if err := s.init(); err != nil {
return err
}
return s.client.CoreV1().Secrets(s.namespace).DeleteCollection(ctx, opts, listOpts)
}
func (s *secretClient) Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.Secret, error) {
if err := s.init(); err != nil {
return nil, err
}
return s.client.CoreV1().Secrets(s.namespace).Get(ctx, name, opts)
}
func (s *secretClient) List(ctx context.Context, opts metav1.ListOptions) (*v1.SecretList, error) {
if err := s.init(); err != nil {
return nil, err
}
return s.client.CoreV1().Secrets(s.namespace).List(ctx, opts)
}
func (s *secretClient) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
if err := s.init(); err != nil {
return nil, err
}
return s.client.CoreV1().Secrets(s.namespace).Watch(ctx, opts)
}
func (s *secretClient) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (*v1.Secret, error) {
if err := s.init(); err != nil {
return nil, err
}
return s.client.CoreV1().Secrets(s.namespace).Patch(ctx, name, pt, data, opts, subresources...)
}
// configMapClient implements a corev1.ConfigMapInterface
type configMapClient struct{ *lazyClient }
var _ corev1.ConfigMapInterface = (*configMapClient)(nil)
func newConfigMapClient(lc *lazyClient) *configMapClient {
return &configMapClient{lazyClient: lc}
}
func (c *configMapClient) Create(ctx context.Context, configMap *v1.ConfigMap, opts metav1.CreateOptions) (*v1.ConfigMap, error) {
if err := c.init(); err != nil {
return nil, err
}
return c.client.CoreV1().ConfigMaps(c.namespace).Create(ctx, configMap, opts)
}
func (c *configMapClient) Update(ctx context.Context, configMap *v1.ConfigMap, opts metav1.UpdateOptions) (*v1.ConfigMap, error) {
if err := c.init(); err != nil {
return nil, err
}
return c.client.CoreV1().ConfigMaps(c.namespace).Update(ctx, configMap, opts)
}
func (c *configMapClient) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error {
if err := c.init(); err != nil {
return err
}
return c.client.CoreV1().ConfigMaps(c.namespace).Delete(ctx, name, opts)
}
func (c *configMapClient) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error {
if err := c.init(); err != nil {
return err
}
return c.client.CoreV1().ConfigMaps(c.namespace).DeleteCollection(ctx, opts, listOpts)
}
func (c *configMapClient) Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.ConfigMap, error) {
if err := c.init(); err != nil {
return nil, err
}
return c.client.CoreV1().ConfigMaps(c.namespace).Get(ctx, name, opts)
}
func (c *configMapClient) List(ctx context.Context, opts metav1.ListOptions) (*v1.ConfigMapList, error) {
if err := c.init(); err != nil {
return nil, err
}
return c.client.CoreV1().ConfigMaps(c.namespace).List(ctx, opts)
}
func (c *configMapClient) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
if err := c.init(); err != nil {
return nil, err
}
return c.client.CoreV1().ConfigMaps(c.namespace).Watch(ctx, opts)
}
func (c *configMapClient) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (*v1.ConfigMap, error) {
if err := c.init(); err != nil {
return nil, err
}
return c.client.CoreV1().ConfigMaps(c.namespace).Patch(ctx, name, pt, data, opts, subresources...)
}

@ -17,6 +17,7 @@ limitations under the License.
package action
import (
"context"
"fmt"
"io"
"time"
@ -81,7 +82,7 @@ func (r *ReleaseTesting) GetPodLogs(out io.Writer, rel *release.Release) error {
for _, e := range h.Events {
if e == release.HookTest {
req := client.CoreV1().Pods(r.Namespace).GetLogs(h.Name, &v1.PodLogOptions{})
logReader, err := req.Stream()
logReader, err := req.Stream(context.Background())
if err != nil {
return errors.Wrapf(err, "unable to get pod logs for %s", h.Name)
}

@ -213,11 +213,8 @@ func (r *Rollback) performRollback(currentRelease, targetRelease *release.Releas
targetRelease.Info.Status = release.StatusDeployed
deployed, err := r.cfg.Releases.DeployedAll(currentRelease.Name)
if err != nil {
if strings.Contains(err.Error(), "has no deployed releases") {
r.cfg.Log(err.Error())
return targetRelease, nil
}
if err != nil && !strings.Contains(err.Error(), "has no deployed releases") {
return nil, err
}
// Supersede all previous deployments, see issue #2941.

@ -18,6 +18,7 @@ package action
import (
"bytes"
"context"
"fmt"
"strings"
"time"
@ -42,27 +43,57 @@ type Upgrade struct {
ChartPathOptions
Install bool
Devel bool
// Install is a purely informative flag that indicates whether this upgrade was done in "install" mode.
//
// Applications may use this to determine whether this Upgrade operation was done as part of a
// pure upgrade (Upgrade.Install == false) or as part of an install-or-upgrade operation
// (Upgrade.Install == true).
//
// Setting this to `true` will NOT cause `Upgrade` to perform an install if the release does not exist.
// That process must be handled by creating an Install action directly. See cmd/upgrade.go for an
// example of how this flag is used.
Install bool
// Devel indicates that the operation is done in devel mode.
Devel bool
// Namespace is the namespace in which this operation should be performed.
Namespace string
// SkipCRDs skip installing CRDs when install flag is enabled during upgrade
SkipCRDs bool
Timeout time.Duration
Wait bool
// SkipCRDs skips installing CRDs when install flag is enabled during upgrade
SkipCRDs bool
// Timeout is the timeout for this operation
Timeout time.Duration
// Wait determines whether the wait operation should be performed after the upgrade is requested.
Wait bool
// DisableHooks disables hook processing if set to true.
DisableHooks bool
DryRun bool
Force bool
ResetValues bool
ReuseValues bool
// DryRun controls whether the operation is prepared, but not executed.
// If `true`, the upgrade is prepared but not performed.
DryRun bool
// Force will, if set to `true`, ignore certain warnings and perform the upgrade anyway.
//
// This should be used with caution.
Force bool
// ResetValues will reset the values to the chart's built-ins rather than merging with existing.
ResetValues bool
// ReuseValues will re-use the user's last supplied values.
ReuseValues bool
// Recreate will (if true) recreate pods after a rollback.
Recreate bool
// MaxHistory limits the maximum number of revisions saved per release
MaxHistory int
Atomic bool
CleanupOnFail bool
SubNotes bool
Description string
PostRenderer postrender.PostRenderer
MaxHistory int
// Atomic, if true, will roll back on failure.
Atomic bool
// CleanupOnFail will, if true, cause the upgrade to delete newly-created resources on a failed update.
CleanupOnFail bool
// SubNotes determines whether sub-notes are rendered in the chart.
SubNotes bool
// Description is the description of this operation
Description string
// PostRender is an optional post-renderer
//
// If this is non-nil, then after templates are rendered, they will be sent to the
// post renderer before sending to the Kuberntes API server.
PostRenderer postrender.PostRenderer
// DisableOpenAPIValidation controls whether OpenAPI validation is enforced.
DisableOpenAPIValidation bool
}
@ -202,6 +233,13 @@ func (u *Upgrade) prepareUpgrade(name string, chart *chart.Chart, vals map[strin
func (u *Upgrade) performUpgrade(originalRelease, upgradedRelease *release.Release) (*release.Release, error) {
current, err := u.cfg.KubeClient.Build(bytes.NewBufferString(originalRelease.Manifest), false)
if err != nil {
// Checking for removed Kubernetes API error so can provide a more informative error message to the user
// Ref: https://github.com/helm/helm/issues/7219
if strings.Contains(err.Error(), "unable to recognize \"\": no matches for kind") {
return upgradedRelease, errors.Wrap(err, "current release manifest contains removed kubernetes api(s) for this "+
"kubernetes version and it is therefore unable to build the kubernetes "+
"objects for performing the diff. error from kubernetes")
}
return upgradedRelease, errors.Wrap(err, "unable to build kubernetes objects from current release manifest")
}
target, err := u.cfg.KubeClient.Build(bytes.NewBufferString(upgradedRelease.Manifest), !u.DisableOpenAPIValidation)
@ -428,7 +466,7 @@ func recreate(cfg *Configuration, resources kube.ResourceList) error {
return errors.Wrapf(err, "unable to recreate pods for object %s/%s because an error occurred", res.Namespace, res.Name)
}
pods, err := client.CoreV1().Pods(res.Namespace).List(metav1.ListOptions{
pods, err := client.CoreV1().Pods(res.Namespace).List(context.Background(), metav1.ListOptions{
LabelSelector: selector.String(),
})
if err != nil {
@ -438,7 +476,7 @@ func recreate(cfg *Configuration, resources kube.ResourceList) error {
// Restart pods
for _, pod := range pods.Items {
// Delete each pod for get them restarted with changed spec.
if err := client.CoreV1().Pods(pod.Namespace).Delete(pod.Name, metav1.NewPreconditionDeleteOptions(string(pod.UID))); err != nil {
if err := client.CoreV1().Pods(pod.Namespace).Delete(context.Background(), pod.Name, *metav1.NewPreconditionDeleteOptions(string(pod.UID))); err != nil {
return errors.Wrapf(err, "unable to recreate pods for object %s/%s because an error occurred", res.Namespace, res.Name)
}
}

@ -96,3 +96,66 @@ func TestMetadata(t *testing.T) {
is.Equal("1.0.0", chrt.AppVersion())
is.Equal(nil, chrt.Validate())
}
func TestIsRoot(t *testing.T) {
chrt1 := Chart{
parent: &Chart{
Metadata: &Metadata{
Name: "foo",
},
},
}
chrt2 := Chart{
Metadata: &Metadata{
Name: "foo",
},
}
is := assert.New(t)
is.Equal(false, chrt1.IsRoot())
is.Equal(true, chrt2.IsRoot())
}
func TestChartPath(t *testing.T) {
chrt1 := Chart{
parent: &Chart{
Metadata: &Metadata{
Name: "foo",
},
},
}
chrt2 := Chart{
Metadata: &Metadata{
Name: "foo",
},
}
is := assert.New(t)
is.Equal("foo.", chrt1.ChartPath())
is.Equal("foo", chrt2.ChartPath())
}
func TestChartFullPath(t *testing.T) {
chrt1 := Chart{
parent: &Chart{
Metadata: &Metadata{
Name: "foo",
},
},
}
chrt2 := Chart{
Metadata: &Metadata{
Name: "foo",
},
}
is := assert.New(t)
is.Equal("foo/charts/", chrt1.ChartFullPath())
is.Equal("foo", chrt2.ChartFullPath())
}

@ -0,0 +1,59 @@
/*
Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package chart
import (
"testing"
)
func TestValidate(t *testing.T) {
tests := []struct {
md *Metadata
err error
}{
{
nil,
ValidationError("chart.metadata is required"),
},
{
&Metadata{Name: "test", Version: "1.0"},
ValidationError("chart.metadata.apiVersion is required"),
},
{
&Metadata{APIVersion: "v2", Version: "1.0"},
ValidationError("chart.metadata.name is required"),
},
{
&Metadata{Name: "test", APIVersion: "v2"},
ValidationError("chart.metadata.version is required"),
},
{
&Metadata{Name: "test", APIVersion: "v2", Version: "1.0", Type: "test"},
ValidationError("chart.metadata.type must be application or library"),
},
{
&Metadata{Name: "test", APIVersion: "v2", Version: "1.0", Type: "application"},
nil,
},
}
for _, tt := range tests {
result := tt.md.Validate()
if result != tt.err {
t.Errorf("expected %s, got %s", tt.err, result)
}
}
}

@ -214,29 +214,29 @@ metadata:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.tls }}
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
{{- range .paths }}
- path: {{ . }}
backend:
serviceName: {{ $fullName }}
servicePort: {{ $svcPort }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
`
const defaultDeployment = `apiVersion: apps/v1
@ -261,10 +261,10 @@ spec:
labels:
{{- include "<CHARTNAME>.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- end }}
serviceAccountName: {{ include "<CHARTNAME>.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
@ -292,14 +292,14 @@ spec:
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- end }}
`
const defaultService = `apiVersion: v1
@ -330,7 +330,7 @@ metadata:
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end -}}
{{- end }}
`
const defaultHorizontalPodAutoscaler = `{{- if .Values.autoscaling.enabled }}
@ -391,8 +391,8 @@ const defaultHelpers = `{{/* vim: set filetype=mustache: */}}
Expand the name of the chart.
*/}}
{{- define "<CHARTNAME>.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
@ -400,24 +400,24 @@ We truncate at 63 chars because some Kubernetes name fields are limited to this
If release name contains chart name it will be used as a full name.
*/}}
{{- define "<CHARTNAME>.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "<CHARTNAME>.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
@ -429,7 +429,7 @@ helm.sh/chart: {{ include "<CHARTNAME>.chart" . }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end -}}
{{- end }}
{{/*
Selector labels
@ -437,18 +437,18 @@ Selector labels
{{- define "<CHARTNAME>.selectorLabels" -}}
app.kubernetes.io/name: {{ include "<CHARTNAME>.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end -}}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "<CHARTNAME>.serviceAccountName" -}}
{{- if .Values.serviceAccount.create -}}
{{ default (include "<CHARTNAME>.fullname" .) .Values.serviceAccount.name }}
{{- else -}}
{{ default "default" .Values.serviceAccount.name }}
{{- end -}}
{{- end -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "<CHARTNAME>.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
`
const defaultTestConnection = `apiVersion: v1

@ -239,6 +239,7 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error {
fmt.Fprintf(m.Out, "Saving %d charts\n", len(deps))
var saveError error
churls := make(map[string]struct{})
for _, dep := range deps {
// No repository means the chart is in charts directory
if dep.Repository == "" {
@ -278,8 +279,6 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error {
continue
}
fmt.Fprintf(m.Out, "Downloading %s from repo %s\n", dep.Name, dep.Repository)
// Any failure to resolve/download a chart should fail:
// https://github.com/helm/helm/issues/1439
churl, username, password, err := m.findChartURL(dep.Name, dep.Version, dep.Repository, repos)
@ -288,6 +287,13 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error {
break
}
if _, ok := churls[churl]; ok {
fmt.Fprintf(m.Out, "Already downloaded %s from repo %s\n", dep.Name, dep.Repository)
continue
}
fmt.Fprintf(m.Out, "Downloading %s from repo %s\n", dep.Name, dep.Repository)
dl := ChartDownloader{
Out: m.Out,
Verify: m.Verify,
@ -304,6 +310,8 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error {
saveError = errors.Wrapf(err, "could not download %s", churl)
break
}
churls[churl] = struct{}{}
}
if saveError == nil {

@ -17,6 +17,7 @@ limitations under the License.
package engine
import (
"context"
"log"
"strings"
@ -47,7 +48,7 @@ func NewLookupFunction(config *rest.Config) lookupFunc {
}
if name != "" {
// this will return a single object
obj, err := client.Get(name, metav1.GetOptions{})
obj, err := client.Get(context.Background(), name, metav1.GetOptions{})
if err != nil {
if apierrors.IsNotFound(err) {
// Just return an empty interface when the object was not found.
@ -59,7 +60,7 @@ func NewLookupFunction(config *rest.Config) lookupFunc {
return obj.UnstructuredContent(), nil
}
//this will return a list
obj, err := client.List(metav1.ListOptions{})
obj, err := client.List(context.Background(), metav1.ListOptions{})
if err != nil {
if apierrors.IsNotFound(err) {
// Just return an empty interface when the object was not found.

@ -88,10 +88,17 @@ var nopLogger = func(_ string, _ ...interface{}) {}
// IsReachable tests connectivity to the cluster
func (c *Client) IsReachable() error {
client, _ := c.Factory.KubernetesClientSet()
_, err := client.ServerVersion()
client, err := c.Factory.KubernetesClientSet()
if err == genericclioptions.ErrEmptyConfig {
// re-replace kubernetes ErrEmptyConfig error with a friendy error
// moar workarounds for Kubernetes API breaking.
return errors.New("Kubernetes cluster unreachable")
}
if err != nil {
return fmt.Errorf("Kubernetes cluster unreachable: %s", err.Error())
return errors.Wrap(err, "Kubernetes cluster unreachable")
}
if _, err := client.ServerVersion(); err != nil {
return errors.Wrap(err, "Kubernetes cluster unreachable")
}
return nil
}
@ -344,7 +351,7 @@ func batchPerform(infos ResourceList, fn func(*resource.Info) error, errs chan<-
}
func createResource(info *resource.Info) error {
obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object, nil)
obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object)
if err != nil {
return err
}
@ -568,9 +575,12 @@ func scrubValidationError(err error) error {
// WaitAndGetCompletedPodPhase waits up to a timeout until a pod enters a completed phase
// and returns said phase (PodSucceeded or PodFailed qualify).
func (c *Client) WaitAndGetCompletedPodPhase(name string, timeout time.Duration) (v1.PodPhase, error) {
client, _ := c.Factory.KubernetesClientSet()
client, err := c.Factory.KubernetesClientSet()
if err != nil {
return v1.PodUnknown, err
}
to := int64(timeout)
watcher, err := client.CoreV1().Pods(c.namespace()).Watch(metav1.ListOptions{
watcher, err := client.CoreV1().Pods(c.namespace()).Watch(context.Background(), metav1.ListOptions{
FieldSelector: fmt.Sprintf("metadata.name=%s", name),
TimeoutSeconds: &to,
})

@ -17,6 +17,7 @@ limitations under the License.
package kube // import "helm.sh/helm/v3/pkg/kube"
import (
"context"
"fmt"
"time"
@ -62,12 +63,12 @@ func (w *waiter) waitForResources(created ResourceList) error {
)
switch value := AsVersioned(v).(type) {
case *corev1.Pod:
pod, err := w.c.CoreV1().Pods(v.Namespace).Get(v.Name, metav1.GetOptions{})
pod, err := w.c.CoreV1().Pods(v.Namespace).Get(context.Background(), v.Name, metav1.GetOptions{})
if err != nil || !w.isPodReady(pod) {
return false, err
}
case *appsv1.Deployment, *appsv1beta1.Deployment, *appsv1beta2.Deployment, *extensionsv1beta1.Deployment:
currentDeployment, err := w.c.AppsV1().Deployments(v.Namespace).Get(v.Name, metav1.GetOptions{})
currentDeployment, err := w.c.AppsV1().Deployments(v.Namespace).Get(context.Background(), v.Name, metav1.GetOptions{})
if err != nil {
return false, err
}
@ -84,7 +85,7 @@ func (w *waiter) waitForResources(created ResourceList) error {
return false, nil
}
case *corev1.PersistentVolumeClaim:
claim, err := w.c.CoreV1().PersistentVolumeClaims(v.Namespace).Get(v.Name, metav1.GetOptions{})
claim, err := w.c.CoreV1().PersistentVolumeClaims(v.Namespace).Get(context.Background(), v.Name, metav1.GetOptions{})
if err != nil {
return false, err
}
@ -92,7 +93,7 @@ func (w *waiter) waitForResources(created ResourceList) error {
return false, nil
}
case *corev1.Service:
svc, err := w.c.CoreV1().Services(v.Namespace).Get(v.Name, metav1.GetOptions{})
svc, err := w.c.CoreV1().Services(v.Namespace).Get(context.Background(), v.Name, metav1.GetOptions{})
if err != nil {
return false, err
}
@ -100,7 +101,7 @@ func (w *waiter) waitForResources(created ResourceList) error {
return false, nil
}
case *extensionsv1beta1.DaemonSet, *appsv1.DaemonSet, *appsv1beta2.DaemonSet:
ds, err := w.c.AppsV1().DaemonSets(v.Namespace).Get(v.Name, metav1.GetOptions{})
ds, err := w.c.AppsV1().DaemonSets(v.Namespace).Get(context.Background(), v.Name, metav1.GetOptions{})
if err != nil {
return false, err
}
@ -130,7 +131,7 @@ func (w *waiter) waitForResources(created ResourceList) error {
return false, nil
}
case *appsv1.StatefulSet, *appsv1beta1.StatefulSet, *appsv1beta2.StatefulSet:
sts, err := w.c.AppsV1().StatefulSets(v.Namespace).Get(v.Name, metav1.GetOptions{})
sts, err := w.c.AppsV1().StatefulSets(v.Namespace).Get(context.Background(), v.Name, metav1.GetOptions{})
if err != nil {
return false, err
}
@ -337,7 +338,7 @@ func (w *waiter) statefulSetReady(sts *appsv1.StatefulSet) bool {
}
func getPods(client kubernetes.Interface, namespace, selector string) ([]corev1.Pod, error) {
list, err := client.CoreV1().Pods(namespace).List(metav1.ListOptions{
list, err := client.CoreV1().Pods(namespace).List(context.Background(), metav1.ListOptions{
LabelSelector: selector,
})
return list.Items, err

@ -17,9 +17,12 @@ limitations under the License.
package lint
import (
"io/ioutil"
"os"
"strings"
"testing"
"helm.sh/helm/v3/pkg/chartutil"
"helm.sh/helm/v3/pkg/lint/support"
)
@ -104,3 +107,30 @@ func TestGoodChart(t *testing.T) {
t.Errorf("All failed but shouldn't have: %#v", m)
}
}
// TestHelmCreateChart tests that a `helm create` always passes a `helm lint` test.
//
// See https://github.com/helm/helm/issues/7923
func TestHelmCreateChart(t *testing.T) {
dir, err := ioutil.TempDir("", "-helm-test")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
createdChart, err := chartutil.Create("testhelmcreatepasseslint", dir)
if err != nil {
t.Error(err)
// Fatal is bad because of the defer.
return
}
// Note: we test with strict=true here, even though others have
// strict = false.
m := All(createdChart, values, namespace, true).Messages
if ll := len(m); ll != 1 {
t.Errorf("All should have had exactly 1 error. Got %d", ll)
} else if msg := m[0].Err.Error(); !strings.Contains(msg, "icon is recommended") {
t.Errorf("Unexpected lint error: %s", msg)
}
}

@ -17,6 +17,7 @@ limitations under the License.
package driver // import "helm.sh/helm/v3/pkg/storage/driver"
import (
"context"
"strconv"
"strings"
"time"
@ -62,7 +63,7 @@ func (cfgmaps *ConfigMaps) Name() string {
// or error if not found.
func (cfgmaps *ConfigMaps) Get(key string) (*rspb.Release, error) {
// fetch the configmap holding the release named by key
obj, err := cfgmaps.impl.Get(key, metav1.GetOptions{})
obj, err := cfgmaps.impl.Get(context.Background(), key, metav1.GetOptions{})
if err != nil {
if apierrors.IsNotFound(err) {
return nil, ErrReleaseNotFound
@ -88,7 +89,7 @@ func (cfgmaps *ConfigMaps) List(filter func(*rspb.Release) bool) ([]*rspb.Releas
lsel := kblabels.Set{"owner": "helm"}.AsSelector()
opts := metav1.ListOptions{LabelSelector: lsel.String()}
list, err := cfgmaps.impl.List(opts)
list, err := cfgmaps.impl.List(context.Background(), opts)
if err != nil {
cfgmaps.Log("list: failed to list: %s", err)
return nil, err
@ -124,7 +125,7 @@ func (cfgmaps *ConfigMaps) Query(labels map[string]string) ([]*rspb.Release, err
opts := metav1.ListOptions{LabelSelector: ls.AsSelector().String()}
list, err := cfgmaps.impl.List(opts)
list, err := cfgmaps.impl.List(context.Background(), opts)
if err != nil {
cfgmaps.Log("query: failed to query with labels: %s", err)
return nil, err
@ -162,7 +163,7 @@ func (cfgmaps *ConfigMaps) Create(key string, rls *rspb.Release) error {
return err
}
// push the configmap object out into the kubiverse
if _, err := cfgmaps.impl.Create(obj); err != nil {
if _, err := cfgmaps.impl.Create(context.Background(), obj, metav1.CreateOptions{}); err != nil {
if apierrors.IsAlreadyExists(err) {
return ErrReleaseExists
}
@ -189,7 +190,7 @@ func (cfgmaps *ConfigMaps) Update(key string, rls *rspb.Release) error {
return err
}
// push the configmap object out into the kubiverse
_, err = cfgmaps.impl.Update(obj)
_, err = cfgmaps.impl.Update(context.Background(), obj, metav1.UpdateOptions{})
if err != nil {
cfgmaps.Log("update: failed to update: %s", err)
return err
@ -204,7 +205,7 @@ func (cfgmaps *ConfigMaps) Delete(key string) (rls *rspb.Release, err error) {
return nil, err
}
// delete the release
if err = cfgmaps.impl.Delete(key, &metav1.DeleteOptions{}); err != nil {
if err = cfgmaps.impl.Delete(context.Background(), key, metav1.DeleteOptions{}); err != nil {
return rls, err
}
return rls, nil

@ -130,6 +130,30 @@ func TestConfigMapList(t *testing.T) {
}
}
func TestConfigMapQuery(t *testing.T) {
cfgmaps := newTestFixtureCfgMaps(t, []*rspb.Release{
releaseStub("key-1", 1, "default", rspb.StatusUninstalled),
releaseStub("key-2", 1, "default", rspb.StatusUninstalled),
releaseStub("key-3", 1, "default", rspb.StatusDeployed),
releaseStub("key-4", 1, "default", rspb.StatusDeployed),
releaseStub("key-5", 1, "default", rspb.StatusSuperseded),
releaseStub("key-6", 1, "default", rspb.StatusSuperseded),
}...)
rls, err := cfgmaps.Query(map[string]string{"status": "deployed"})
if err != nil {
t.Errorf("Failed to query: %s", err)
}
if len(rls) != 2 {
t.Errorf("Expected 2 results, got %d", len(rls))
}
_, err = cfgmaps.Query(map[string]string{"name": "notExist"})
if err != ErrReleaseNotFound {
t.Errorf("Expected {%v}, got {%v}", ErrReleaseNotFound, err)
}
}
func TestConfigMapCreate(t *testing.T) {
cfgmaps := newTestFixtureCfgMaps(t)

@ -141,6 +141,11 @@ func (mem *Memory) Query(keyvals map[string]string) ([]*rspb.Release, error) {
break
}
}
if len(ls) == 0 {
return nil, ErrReleaseNotFound
}
return ls, nil
}

@ -17,12 +17,14 @@ limitations under the License.
package driver // import "helm.sh/helm/v3/pkg/storage/driver"
import (
"context"
"fmt"
"testing"
v1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kblabels "k8s.io/apimachinery/pkg/labels"
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
rspb "helm.sh/helm/v3/pkg/release"
@ -102,7 +104,7 @@ func (mock *MockConfigMapsInterface) Init(t *testing.T, releases ...*rspb.Releas
}
// Get returns the ConfigMap by name.
func (mock *MockConfigMapsInterface) Get(name string, options metav1.GetOptions) (*v1.ConfigMap, error) {
func (mock *MockConfigMapsInterface) Get(_ context.Context, name string, _ metav1.GetOptions) (*v1.ConfigMap, error) {
object, ok := mock.objects[name]
if !ok {
return nil, apierrors.NewNotFound(v1.Resource("tests"), name)
@ -111,16 +113,24 @@ func (mock *MockConfigMapsInterface) Get(name string, options metav1.GetOptions)
}
// List returns the a of ConfigMaps.
func (mock *MockConfigMapsInterface) List(opts metav1.ListOptions) (*v1.ConfigMapList, error) {
func (mock *MockConfigMapsInterface) List(_ context.Context, opts metav1.ListOptions) (*v1.ConfigMapList, error) {
var list v1.ConfigMapList
labelSelector, err := kblabels.Parse(opts.LabelSelector)
if err != nil {
return nil, err
}
for _, cfgmap := range mock.objects {
list.Items = append(list.Items, *cfgmap)
if labelSelector.Matches(kblabels.Set(cfgmap.ObjectMeta.Labels)) {
list.Items = append(list.Items, *cfgmap)
}
}
return &list, nil
}
// Create creates a new ConfigMap.
func (mock *MockConfigMapsInterface) Create(cfgmap *v1.ConfigMap) (*v1.ConfigMap, error) {
func (mock *MockConfigMapsInterface) Create(_ context.Context, cfgmap *v1.ConfigMap, _ metav1.CreateOptions) (*v1.ConfigMap, error) {
name := cfgmap.ObjectMeta.Name
if object, ok := mock.objects[name]; ok {
return object, apierrors.NewAlreadyExists(v1.Resource("tests"), name)
@ -130,7 +140,7 @@ func (mock *MockConfigMapsInterface) Create(cfgmap *v1.ConfigMap) (*v1.ConfigMap
}
// Update updates a ConfigMap.
func (mock *MockConfigMapsInterface) Update(cfgmap *v1.ConfigMap) (*v1.ConfigMap, error) {
func (mock *MockConfigMapsInterface) Update(_ context.Context, cfgmap *v1.ConfigMap, _ metav1.UpdateOptions) (*v1.ConfigMap, error) {
name := cfgmap.ObjectMeta.Name
if _, ok := mock.objects[name]; !ok {
return nil, apierrors.NewNotFound(v1.Resource("tests"), name)
@ -140,7 +150,7 @@ func (mock *MockConfigMapsInterface) Update(cfgmap *v1.ConfigMap) (*v1.ConfigMap
}
// Delete deletes a ConfigMap by name.
func (mock *MockConfigMapsInterface) Delete(name string, opts *metav1.DeleteOptions) error {
func (mock *MockConfigMapsInterface) Delete(_ context.Context, name string, _ metav1.DeleteOptions) error {
if _, ok := mock.objects[name]; !ok {
return apierrors.NewNotFound(v1.Resource("tests"), name)
}
@ -180,7 +190,7 @@ func (mock *MockSecretsInterface) Init(t *testing.T, releases ...*rspb.Release)
}
// Get returns the Secret by name.
func (mock *MockSecretsInterface) Get(name string, options metav1.GetOptions) (*v1.Secret, error) {
func (mock *MockSecretsInterface) Get(_ context.Context, name string, _ metav1.GetOptions) (*v1.Secret, error) {
object, ok := mock.objects[name]
if !ok {
return nil, apierrors.NewNotFound(v1.Resource("tests"), name)
@ -189,16 +199,24 @@ func (mock *MockSecretsInterface) Get(name string, options metav1.GetOptions) (*
}
// List returns the a of Secret.
func (mock *MockSecretsInterface) List(opts metav1.ListOptions) (*v1.SecretList, error) {
func (mock *MockSecretsInterface) List(_ context.Context, opts metav1.ListOptions) (*v1.SecretList, error) {
var list v1.SecretList
labelSelector, err := kblabels.Parse(opts.LabelSelector)
if err != nil {
return nil, err
}
for _, secret := range mock.objects {
list.Items = append(list.Items, *secret)
if labelSelector.Matches(kblabels.Set(secret.ObjectMeta.Labels)) {
list.Items = append(list.Items, *secret)
}
}
return &list, nil
}
// Create creates a new Secret.
func (mock *MockSecretsInterface) Create(secret *v1.Secret) (*v1.Secret, error) {
func (mock *MockSecretsInterface) Create(_ context.Context, secret *v1.Secret, _ metav1.CreateOptions) (*v1.Secret, error) {
name := secret.ObjectMeta.Name
if object, ok := mock.objects[name]; ok {
return object, apierrors.NewAlreadyExists(v1.Resource("tests"), name)
@ -208,7 +226,7 @@ func (mock *MockSecretsInterface) Create(secret *v1.Secret) (*v1.Secret, error)
}
// Update updates a Secret.
func (mock *MockSecretsInterface) Update(secret *v1.Secret) (*v1.Secret, error) {
func (mock *MockSecretsInterface) Update(_ context.Context, secret *v1.Secret, _ metav1.UpdateOptions) (*v1.Secret, error) {
name := secret.ObjectMeta.Name
if _, ok := mock.objects[name]; !ok {
return nil, apierrors.NewNotFound(v1.Resource("tests"), name)
@ -218,7 +236,7 @@ func (mock *MockSecretsInterface) Update(secret *v1.Secret) (*v1.Secret, error)
}
// Delete deletes a Secret by name.
func (mock *MockSecretsInterface) Delete(name string, opts *metav1.DeleteOptions) error {
func (mock *MockSecretsInterface) Delete(_ context.Context, name string, _ metav1.DeleteOptions) error {
if _, ok := mock.objects[name]; !ok {
return apierrors.NewNotFound(v1.Resource("tests"), name)
}

@ -17,6 +17,7 @@ limitations under the License.
package driver // import "helm.sh/helm/v3/pkg/storage/driver"
import (
"context"
"strconv"
"strings"
"time"
@ -62,7 +63,7 @@ func (secrets *Secrets) Name() string {
// or error if not found.
func (secrets *Secrets) Get(key string) (*rspb.Release, error) {
// fetch the secret holding the release named by key
obj, err := secrets.impl.Get(key, metav1.GetOptions{})
obj, err := secrets.impl.Get(context.Background(), key, metav1.GetOptions{})
if err != nil {
if apierrors.IsNotFound(err) {
return nil, ErrReleaseNotFound
@ -81,7 +82,7 @@ func (secrets *Secrets) List(filter func(*rspb.Release) bool) ([]*rspb.Release,
lsel := kblabels.Set{"owner": "helm"}.AsSelector()
opts := metav1.ListOptions{LabelSelector: lsel.String()}
list, err := secrets.impl.List(opts)
list, err := secrets.impl.List(context.Background(), opts)
if err != nil {
return nil, errors.Wrap(err, "list: failed to list")
}
@ -116,7 +117,7 @@ func (secrets *Secrets) Query(labels map[string]string) ([]*rspb.Release, error)
opts := metav1.ListOptions{LabelSelector: ls.AsSelector().String()}
list, err := secrets.impl.List(opts)
list, err := secrets.impl.List(context.Background(), opts)
if err != nil {
return nil, errors.Wrap(err, "query: failed to query with labels")
}
@ -152,7 +153,7 @@ func (secrets *Secrets) Create(key string, rls *rspb.Release) error {
return errors.Wrapf(err, "create: failed to encode release %q", rls.Name)
}
// push the secret object out into the kubiverse
if _, err := secrets.impl.Create(obj); err != nil {
if _, err := secrets.impl.Create(context.Background(), obj, metav1.CreateOptions{}); err != nil {
if apierrors.IsAlreadyExists(err) {
return ErrReleaseExists
}
@ -177,7 +178,7 @@ func (secrets *Secrets) Update(key string, rls *rspb.Release) error {
return errors.Wrapf(err, "update: failed to encode release %q", rls.Name)
}
// push the secret object out into the kubiverse
_, err = secrets.impl.Update(obj)
_, err = secrets.impl.Update(context.Background(), obj, metav1.UpdateOptions{})
return errors.Wrap(err, "update: failed to update")
}
@ -188,7 +189,7 @@ func (secrets *Secrets) Delete(key string) (rls *rspb.Release, err error) {
return nil, err
}
// delete the release
err = secrets.impl.Delete(key, &metav1.DeleteOptions{})
err = secrets.impl.Delete(context.Background(), key, metav1.DeleteOptions{})
return rls, err
}

@ -130,6 +130,30 @@ func TestSecretList(t *testing.T) {
}
}
func TestSecretQuery(t *testing.T) {
secrets := newTestFixtureSecrets(t, []*rspb.Release{
releaseStub("key-1", 1, "default", rspb.StatusUninstalled),
releaseStub("key-2", 1, "default", rspb.StatusUninstalled),
releaseStub("key-3", 1, "default", rspb.StatusDeployed),
releaseStub("key-4", 1, "default", rspb.StatusDeployed),
releaseStub("key-5", 1, "default", rspb.StatusSuperseded),
releaseStub("key-6", 1, "default", rspb.StatusSuperseded),
}...)
rls, err := secrets.Query(map[string]string{"status": "deployed"})
if err != nil {
t.Fatalf("Failed to query: %s", err)
}
if len(rls) != 2 {
t.Fatalf("Expected 2 results, actual %d", len(rls))
}
_, err = secrets.Query(map[string]string{"name": "notExist"})
if err != ErrReleaseNotFound {
t.Errorf("Expected {%v}, got {%v}", ErrReleaseNotFound, err)
}
}
func TestSecretCreate(t *testing.T) {
secrets := newTestFixtureSecrets(t)

@ -169,21 +169,37 @@ func (s *Storage) removeLeastRecent(name string, max int) error {
if len(h) <= max {
return nil
}
overage := len(h) - max
// We want oldest to newest
relutil.SortByRevision(h)
lastDeployed, err := s.Deployed(name)
if err != nil {
return err
}
var toDelete []*rspb.Release
for _, rel := range h {
// once we have enough releases to delete to reach the max, stop
if len(h)-len(toDelete) == max {
break
}
if lastDeployed != nil {
if rel.Version != lastDeployed.Version {
toDelete = append(toDelete, rel)
}
} else {
toDelete = append(toDelete, rel)
}
}
// Delete as many as possible. In the case of API throughput limitations,
// multiple invocations of this function will eventually delete them all.
toDelete := h[0:overage]
errs := []error{}
for _, rel := range toDelete {
key := makeKey(name, rel.Version)
_, innerErr := s.Delete(name, rel.Version)
if innerErr != nil {
s.Log("error pruning %s from release history: %s", key, innerErr)
errs = append(errs, innerErr)
err = s.deleteReleaseVersion(name, rel.Version)
if err != nil {
errs = append(errs, err)
}
}
@ -198,6 +214,16 @@ func (s *Storage) removeLeastRecent(name string, max int) error {
}
}
func (s *Storage) deleteReleaseVersion(name string, version int) error {
key := makeKey(name, version)
_, err := s.Delete(name, version)
if err != nil {
s.Log("error pruning %s from release history: %s", key, err)
return err
}
return nil
}
// Last fetches the last revision of the named release.
func (s *Storage) Last(name string) (*rspb.Release, error) {
s.Log("getting last revision of %q", name)

@ -333,6 +333,57 @@ func TestStorageRemoveLeastRecent(t *testing.T) {
}
}
func TestStorageDontDeleteDeployed(t *testing.T) {
storage := Init(driver.NewMemory())
storage.Log = t.Logf
storage.MaxHistory = 3
const name = "angry-bird"
// setup storage with test releases
setup := func() {
// release records
rls0 := ReleaseTestData{Name: name, Version: 1, Status: rspb.StatusSuperseded}.ToRelease()
rls1 := ReleaseTestData{Name: name, Version: 2, Status: rspb.StatusDeployed}.ToRelease()
rls2 := ReleaseTestData{Name: name, Version: 3, Status: rspb.StatusFailed}.ToRelease()
rls3 := ReleaseTestData{Name: name, Version: 4, Status: rspb.StatusFailed}.ToRelease()
// create the release records in the storage
assertErrNil(t.Fatal, storage.Create(rls0), "Storing release 'angry-bird' (v1)")
assertErrNil(t.Fatal, storage.Create(rls1), "Storing release 'angry-bird' (v2)")
assertErrNil(t.Fatal, storage.Create(rls2), "Storing release 'angry-bird' (v3)")
assertErrNil(t.Fatal, storage.Create(rls3), "Storing release 'angry-bird' (v4)")
}
setup()
rls5 := ReleaseTestData{Name: name, Version: 5, Status: rspb.StatusFailed}.ToRelease()
assertErrNil(t.Fatal, storage.Create(rls5), "Storing release 'angry-bird' (v5)")
// On inserting the 5th record, we expect a total of 3 releases, but we expect version 2
// (the only deployed release), to still exist
hist, err := storage.History(name)
if err != nil {
t.Fatal(err)
} else if len(hist) != storage.MaxHistory {
for _, item := range hist {
t.Logf("%s %v", item.Name, item.Version)
}
t.Fatalf("expected %d items in history, got %d", storage.MaxHistory, len(hist))
}
expectedVersions := map[int]bool{
2: true,
4: true,
5: true,
}
for _, item := range hist {
if !expectedVersions[item.Version] {
t.Errorf("Release version %d, found when not expected", item.Version)
}
}
}
func TestStorageLast(t *testing.T) {
storage := Init(driver.NewMemory())

@ -17,8 +17,7 @@
# The install script is based off of the MIT-licensed script from glide,
# the package manager for Go: https://github.com/Masterminds/glide.sh/blob/master/get
PROJECT_NAME="helm"
: ${BINARY_NAME:="helm"}
: ${USE_SUDO:="true"}
: ${HELM_INSTALL_DIR:="/usr/local/bin"}
@ -92,8 +91,8 @@ checkDesiredVersion() {
# checkHelmInstalledVersion checks which version of helm is installed and
# if it needs to be changed.
checkHelmInstalledVersion() {
if [[ -f "${HELM_INSTALL_DIR}/${PROJECT_NAME}" ]]; then
local version=$("${HELM_INSTALL_DIR}/${PROJECT_NAME}" version --template="{{ .Version }}")
if [[ -f "${HELM_INSTALL_DIR}/${BINARY_NAME}" ]]; then
local version=$("${HELM_INSTALL_DIR}/${BINARY_NAME}" version --template="{{ .Version }}")
if [[ "$version" == "$TAG" ]]; then
echo "Helm ${version} is already ${DESIRED_VERSION:-latest}"
return 0
@ -131,7 +130,7 @@ downloadFile() {
# installFile verifies the SHA256 for the file, then unpacks and
# installs it.
installFile() {
HELM_TMP="$HELM_TMP_ROOT/$PROJECT_NAME"
HELM_TMP="$HELM_TMP_ROOT/$BINARY_NAME"
local sum=$(openssl sha1 -sha256 ${HELM_TMP_FILE} | awk '{print $2}')
local expected_sum=$(cat ${HELM_SUM_FILE})
if [ "$sum" != "$expected_sum" ]; then
@ -141,10 +140,10 @@ installFile() {
mkdir -p "$HELM_TMP"
tar xf "$HELM_TMP_FILE" -C "$HELM_TMP"
HELM_TMP_BIN="$HELM_TMP/$OS-$ARCH/$PROJECT_NAME"
echo "Preparing to install $PROJECT_NAME into ${HELM_INSTALL_DIR}"
runAsRoot cp "$HELM_TMP_BIN" "$HELM_INSTALL_DIR"
echo "$PROJECT_NAME installed into $HELM_INSTALL_DIR/$PROJECT_NAME"
HELM_TMP_BIN="$HELM_TMP/$OS-$ARCH/helm"
echo "Preparing to install $BINARY_NAME into ${HELM_INSTALL_DIR}"
runAsRoot cp "$HELM_TMP_BIN" "$HELM_INSTALL_DIR/$BINARY_NAME"
echo "$BINARY_NAME installed into $HELM_INSTALL_DIR/$BINARY_NAME"
}
# fail_trap is executed if an error occurs.
@ -152,10 +151,10 @@ fail_trap() {
result=$?
if [ "$result" != "0" ]; then
if [[ -n "$INPUT_ARGUMENTS" ]]; then
echo "Failed to install $PROJECT_NAME with the arguments provided: $INPUT_ARGUMENTS"
echo "Failed to install $BINARY_NAME with the arguments provided: $INPUT_ARGUMENTS"
help
else
echo "Failed to install $PROJECT_NAME"
echo "Failed to install $BINARY_NAME"
fi
echo -e "\tFor support, go to https://github.com/helm/helm."
fi
@ -166,9 +165,9 @@ fail_trap() {
# testVersion tests the installed client to make sure it is working.
testVersion() {
set +e
HELM="$(which $PROJECT_NAME)"
HELM="$(which $BINARY_NAME)"
if [ "$?" = "1" ]; then
echo "$PROJECT_NAME not found. Is $HELM_INSTALL_DIR on your "'$PATH?'
echo "$BINARY_NAME not found. Is $HELM_INSTALL_DIR on your "'$PATH?'
exit 1
fi
set -e

Loading…
Cancel
Save