From 8f3c0a160cb9e5aa15a2c7a2f618704eea5de743 Mon Sep 17 00:00:00 2001 From: Calle Pettersson Date: Tue, 2 Jun 2020 13:43:53 +0200 Subject: [PATCH 01/45] Report what cause finding chart to fail Signed-off-by: Calle Pettersson --- pkg/downloader/manager.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/downloader/manager.go b/pkg/downloader/manager.go index 00198de0c..28a656e55 100644 --- a/pkg/downloader/manager.go +++ b/pkg/downloader/manager.go @@ -571,7 +571,7 @@ func (m *Manager) findChartURL(name, version, repoURL string, repos map[string]* if err == nil { return } - err = errors.Errorf("chart %s not found in %s", name, repoURL) + err = errors.Errorf("chart %s not found in %s: %s", name, repoURL, err) return } From 52295490fd7a22c05058ad6eab794ccd4fdf3193 Mon Sep 17 00:00:00 2001 From: yxxhero Date: Tue, 21 Jul 2020 13:51:27 +0800 Subject: [PATCH 02/45] fix insecure-skip-tls-verify flag does't work on helm install, Keep FindChartInRepoURL and FindChartInAuthRepoURL functions signatures intact. Signed-off-by: yxxhero --- pkg/action/install.go | 4 ++-- pkg/action/pull.go | 2 +- pkg/repo/chartrepo.go | 22 +++++++++++++++------- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/pkg/action/install.go b/pkg/action/install.go index 48a3aeeca..383dd2c86 100644 --- a/pkg/action/install.go +++ b/pkg/action/install.go @@ -654,8 +654,8 @@ func (c *ChartPathOptions) LocateChart(name string, settings *cli.EnvSettings) ( dl.Verify = downloader.VerifyAlways } if c.RepoURL != "" { - chartURL, err := repo.FindChartInAuthRepoURL(c.RepoURL, c.Username, c.Password, name, version, - c.CertFile, c.KeyFile, c.CaFile, getter.All(settings)) + chartURL, err := repo.FindChartInAuthAndTLSRepoURL(c.RepoURL, c.Username, c.Password, name, version, + c.CertFile, c.KeyFile, c.CaFile, c.InsecureSkipTLSverify, getter.All(settings)) if err != nil { return "", err } diff --git a/pkg/action/pull.go b/pkg/action/pull.go index a46e98bae..220ca11b2 100644 --- a/pkg/action/pull.go +++ b/pkg/action/pull.go @@ -89,7 +89,7 @@ func (p *Pull) Run(chartRef string) (string, error) { } if p.RepoURL != "" { - chartURL, err := repo.FindChartInAuthRepoURL(p.RepoURL, p.Username, p.Password, chartRef, p.Version, p.CertFile, p.KeyFile, p.CaFile, getter.All(p.Settings)) + chartURL, err := repo.FindChartInAuthAndTLSRepoURL(p.RepoURL, p.Username, p.Password, chartRef, p.Version, p.CertFile, p.KeyFile, p.CaFile, p.InsecureSkipTLSverify, getter.All(p.Settings)) if err != nil { return out.String(), err } diff --git a/pkg/repo/chartrepo.go b/pkg/repo/chartrepo.go index c2c366a1e..266986a95 100644 --- a/pkg/repo/chartrepo.go +++ b/pkg/repo/chartrepo.go @@ -205,6 +205,13 @@ func FindChartInRepoURL(repoURL, chartName, chartVersion, certFile, keyFile, caF // without adding repo to repositories, like FindChartInRepoURL, // but it also receives credentials for the chart repository. func FindChartInAuthRepoURL(repoURL, username, password, chartName, chartVersion, certFile, keyFile, caFile string, getters getter.Providers) (string, error) { + return FindChartInAuthAndTLSRepoURL(repoURL, username, password, chartName, chartVersion, certFile, keyFile, caFile, false, getters) +} + +// FindChartInAuthRepoURL finds chart in chart repository pointed by repoURL +// without adding repo to repositories, like FindChartInRepoURL, +// but it also receives credentials and TLS verify flag for the chart repository. +func FindChartInAuthAndTLSRepoURL(repoURL, username, password, chartName, chartVersion, certFile, keyFile, caFile string, insecureSkipTLSverify bool, getters getter.Providers) (string, error) { // Download and write the index file to a temporary location buf := make([]byte, 20) @@ -212,13 +219,14 @@ func FindChartInAuthRepoURL(repoURL, username, password, chartName, chartVersion name := strings.ReplaceAll(base64.StdEncoding.EncodeToString(buf), "/", "-") c := Entry{ - URL: repoURL, - Username: username, - Password: password, - CertFile: certFile, - KeyFile: keyFile, - CAFile: caFile, - Name: name, + URL: repoURL, + Username: username, + Password: password, + CertFile: certFile, + KeyFile: keyFile, + CAFile: caFile, + Name: name, + InsecureSkipTLSverify: insecureSkipTLSverify, } r, err := NewChartRepository(&c, getters) if err != nil { From 0ecc500c451f423bbcfb393a61953170c479fc51 Mon Sep 17 00:00:00 2001 From: yxxhero Date: Thu, 23 Jul 2020 08:06:39 +0800 Subject: [PATCH 03/45] add unit tests for FindChartInAuthAndTLSRepoURL. Signed-off-by: yxxhero --- pkg/repo/chartrepo_test.go | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/pkg/repo/chartrepo_test.go b/pkg/repo/chartrepo_test.go index f50d6a2b6..5317bcbc0 100644 --- a/pkg/repo/chartrepo_test.go +++ b/pkg/repo/chartrepo_test.go @@ -276,6 +276,44 @@ func startLocalServerForTests(handler http.Handler) (*httptest.Server, error) { return httptest.NewServer(handler), nil } +// startLocalTLSServerForTests Start the local helm server with TLS +func startLocalTLSServerForTests(handler http.Handler) (*httptest.Server, error) { + if handler == nil { + fileBytes, err := ioutil.ReadFile("testdata/local-index.yaml") + if err != nil { + return nil, err + } + handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write(fileBytes) + }) + } + + return httptest.NewTLSServer(handler), nil +} + +func TestFindChartInAuthAndTLSRepoURL(t *testing.T) { + srv, err := startLocalTLSServerForTests(nil) + if err != nil { + t.Fatal(err) + } + defer srv.Close() + + chartURL, err := FindChartInAuthAndTLSRepoURL(srv.URL, "", "", "nginx", "", "", "", "", true, getter.All(&cli.EnvSettings{})) + if err != nil { + t.Fatalf("%v", err) + } + if chartURL != "https://kubernetes-charts.storage.googleapis.com/nginx-0.2.0.tgz" { + t.Errorf("%s is not the valid URL", chartURL) + } + + // If the insecureSkipTLsverify is false, it will return an error that contains "x509: certificate signed by unknown authority". + _, err = FindChartInAuthAndTLSRepoURL(srv.URL, "", "", "nginx", "0.1.0", "", "", "", false, getter.All(&cli.EnvSettings{})) + + if !strings.Contains(err.Error(), "x509: certificate signed by unknown authority") { + t.Errorf("Expected TLS error for function FindChartInAuthAndTLSRepoURL not found, but got a different error (%v)", err) + } +} + func TestFindChartInRepoURL(t *testing.T) { srv, err := startLocalServerForTests(nil) if err != nil { From 0674d93609bfb82fe49eaf72b0ae84f348f4ee57 Mon Sep 17 00:00:00 2001 From: yxxhero Date: Wed, 5 Aug 2020 11:47:42 +0800 Subject: [PATCH 04/45] add helm v4 todo comments for FindChartInAuthAndTLSRepoURL. Signed-off-by: yxxhero --- pkg/repo/chartrepo.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/repo/chartrepo.go b/pkg/repo/chartrepo.go index 266986a95..edb86eaeb 100644 --- a/pkg/repo/chartrepo.go +++ b/pkg/repo/chartrepo.go @@ -211,6 +211,7 @@ func FindChartInAuthRepoURL(repoURL, username, password, chartName, chartVersion // FindChartInAuthRepoURL finds chart in chart repository pointed by repoURL // without adding repo to repositories, like FindChartInRepoURL, // but it also receives credentials and TLS verify flag for the chart repository. +// TODO Helm 4, FindChartInAuthAndTLSRepoURL should be integrated into FindChartInAuthRepoURL. func FindChartInAuthAndTLSRepoURL(repoURL, username, password, chartName, chartVersion, certFile, keyFile, caFile string, insecureSkipTLSverify bool, getters getter.Providers) (string, error) { // Download and write the index file to a temporary location From 6898ad14576463ea1df857bb17f2f0ee47653756 Mon Sep 17 00:00:00 2001 From: Josh Dolitsky <393494+jdolitsky@users.noreply.github.com> Date: Tue, 8 Sep 2020 08:48:22 -0500 Subject: [PATCH 05/45] Add GPG signature verification to install script (#7944) * Add GPG signature verification to install script The script fetches the KEYS file from GitHub, as well as the .asc files on the release and verifies the release artifacts are signed by a valid key. Added new boolean config options in the install script which allow for fine-grained control over verification and output: - DEBUG: sets -x in the bash script (default: false) - VERIFY_CHECKSUM: verifies checksum (default: true) - VERIFY_SIGNATURE: verifies signature (default: true) Also reduced check for curl/wget to only one time. Resolves #7943. Resolves #7838. Signed-off-by: Josh Dolitsky <393494+jdolitsky@users.noreply.github.com> * disable signature verification by default Signed-off-by: Josh Dolitsky <393494+jdolitsky@users.noreply.github.com> * remove repeated line Signed-off-by: Josh Dolitsky <393494+jdolitsky@users.noreply.github.com> * fix typo Signed-off-by: Josh Dolitsky <393494+jdolitsky@users.noreply.github.com> * do not auto-import GPG keys Signed-off-by: Josh Dolitsky <393494+jdolitsky@users.noreply.github.com> * silence errors about missing commands Signed-off-by: Josh Dolitsky <393494+jdolitsky@users.noreply.github.com> * use a temporary gpg keyring Signed-off-by: Josh Dolitsky <393494+jdolitsky@users.noreply.github.com> * Fix wget commands for VERIFY_SIGNATURES=true Signed-off-by: jdolitsky <393494+jdolitsky@users.noreply.github.com> --- scripts/get-helm-3 | 128 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 110 insertions(+), 18 deletions(-) diff --git a/scripts/get-helm-3 b/scripts/get-helm-3 index f2495e444..08d0e14ca 100755 --- a/scripts/get-helm-3 +++ b/scripts/get-helm-3 @@ -19,8 +19,16 @@ : ${BINARY_NAME:="helm"} : ${USE_SUDO:="true"} +: ${DEBUG:="false"} +: ${VERIFY_CHECKSUM:="true"} +: ${VERIFY_SIGNATURES:="false"} : ${HELM_INSTALL_DIR:="/usr/local/bin"} +HAS_CURL="$(type "curl" &> /dev/null && echo true || echo false)" +HAS_WGET="$(type "wget" &> /dev/null && echo true || echo false)" +HAS_OPENSSL="$(type "openssl" &> /dev/null && echo true || echo false)" +HAS_GPG="$(type "gpg" &> /dev/null && echo true || echo false)" + # initArch discovers the architecture for this system. initArch() { ARCH=$(uname -m) @@ -58,7 +66,7 @@ runAsRoot() { } # verifySupported checks that the os/arch combination is supported for -# binary builds. +# binary builds, as well whether or not necessary tools are present. verifySupported() { local supported="darwin-amd64\nlinux-386\nlinux-amd64\nlinux-arm\nlinux-arm64\nlinux-ppc64le\nlinux-s390x\nwindows-amd64" if ! echo "${supported}" | grep -q "${OS}-${ARCH}"; then @@ -67,10 +75,29 @@ verifySupported() { exit 1 fi - if ! type "curl" > /dev/null && ! type "wget" > /dev/null; then + if [ "${HAS_CURL}" != "true" ] && [ "${HAS_WGET}" != "true" ]; then echo "Either curl or wget is required" exit 1 fi + + if [ "${VERIFY_CHECKSUM}" == "true" ] && [ "${HAS_OPENSSL}" != "true" ]; then + echo "In order to verify checksum, openssl must first be installed." + echo "Please install openssl or set VERIFY_CHECKSUM=false in your environment." + exit 1 + fi + + if [ "${VERIFY_SIGNATURES}" == "true" ]; then + if [ "${HAS_GPG}" != "true" ]; then + echo "In order to verify signatures, gpg must first be installed." + echo "Please install gpg or set VERIFY_SIGNATURES=false in your environment." + exit 1 + fi + if [ "${OS}" != "linux" ]; then + echo "Signature verification is currently only supported on Linux." + echo "Please set VERIFY_SIGNATURES=false or verify the signatures manually." + exit 1 + fi + fi } # checkDesiredVersion checks if the desired version is available. @@ -78,9 +105,9 @@ checkDesiredVersion() { if [ "x$DESIRED_VERSION" == "x" ]; then # Get tag from release URL local latest_release_url="https://github.com/helm/helm/releases" - if type "curl" > /dev/null; then + if [ "${HAS_CURL}" == "true" ]; then TAG=$(curl -Ls $latest_release_url | grep 'href="/helm/helm/releases/tag/v3.[0-9]*.[0-9]*\"' | grep -v no-underline | head -n 1 | cut -d '"' -f 2 | awk '{n=split($NF,a,"/");print a[n]}' | awk 'a !~ $0{print}; {a=$0}') - elif type "wget" > /dev/null; then + elif [ "${HAS_WGET}" == "true" ]; then TAG=$(wget $latest_release_url -O - 2>&1 | grep 'href="/helm/helm/releases/tag/v3.[0-9]*.[0-9]*\"' | grep -v no-underline | head -n 1 | cut -d '"' -f 2 | awk '{n=split($NF,a,"/");print a[n]}' | awk 'a !~ $0{print}; {a=$0}') fi else @@ -115,35 +142,94 @@ downloadFile() { HELM_TMP_FILE="$HELM_TMP_ROOT/$HELM_DIST" HELM_SUM_FILE="$HELM_TMP_ROOT/$HELM_DIST.sha256" echo "Downloading $DOWNLOAD_URL" - if type "curl" > /dev/null; then + if [ "${HAS_CURL}" == "true" ]; then curl -SsL "$CHECKSUM_URL" -o "$HELM_SUM_FILE" - elif type "wget" > /dev/null; then - wget -q -O "$HELM_SUM_FILE" "$CHECKSUM_URL" - fi - if type "curl" > /dev/null; then curl -SsL "$DOWNLOAD_URL" -o "$HELM_TMP_FILE" - elif type "wget" > /dev/null; then + elif [ "${HAS_WGET}" == "true" ]; then + wget -q -O "$HELM_SUM_FILE" "$CHECKSUM_URL" wget -q -O "$HELM_TMP_FILE" "$DOWNLOAD_URL" fi } -# installFile verifies the SHA256 for the file, then unpacks and -# installs it. +# verifyFile verifies the SHA256 checksum of the binary package +# and the GPG signatures for both the package and checksum file +# (depending on settings in environment). +verifyFile() { + if [ "${VERIFY_CHECKSUM}" == "true" ]; then + verifyChecksum + fi + if [ "${VERIFY_SIGNATURES}" == "true" ]; then + verifySignatures + fi +} + +# installFile installs the Helm binary. installFile() { HELM_TMP="$HELM_TMP_ROOT/$BINARY_NAME" + mkdir -p "$HELM_TMP" + tar xf "$HELM_TMP_FILE" -C "$HELM_TMP" + HELM_TMP_BIN="$HELM_TMP/$OS-$ARCH/helm" + echo "Preparing to install $BINARY_NAME into ${HELM_INSTALL_DIR}" + runAsRoot cp "$HELM_TMP_BIN" "$HELM_INSTALL_DIR/$BINARY_NAME" + echo "$BINARY_NAME installed into $HELM_INSTALL_DIR/$BINARY_NAME" +} + +# verifyChecksum verifies the SHA256 checksum of the binary package. +verifyChecksum() { + printf "Verifying checksum... " local sum=$(openssl sha1 -sha256 ${HELM_TMP_FILE} | awk '{print $2}') local expected_sum=$(cat ${HELM_SUM_FILE}) if [ "$sum" != "$expected_sum" ]; then echo "SHA sum of ${HELM_TMP_FILE} does not match. Aborting." exit 1 fi + echo "Done." +} - mkdir -p "$HELM_TMP" - tar xf "$HELM_TMP_FILE" -C "$HELM_TMP" - HELM_TMP_BIN="$HELM_TMP/$OS-$ARCH/helm" - echo "Preparing to install $BINARY_NAME into ${HELM_INSTALL_DIR}" - runAsRoot cp "$HELM_TMP_BIN" "$HELM_INSTALL_DIR/$BINARY_NAME" - echo "$BINARY_NAME installed into $HELM_INSTALL_DIR/$BINARY_NAME" +# verifySignatures obtains the latest KEYS file from GitHub master branch +# as well as the signature .asc files from the specific GitHub release, +# then verifies that the release artifacts were signed by a maintainer's key. +verifySignatures() { + printf "Verifying signatures... " + local keys_filename="KEYS" + local github_keys_url="https://raw.githubusercontent.com/helm/helm/master/${keys_filename}" + if [ "${HAS_CURL}" == "true" ]; then + curl -SsL "${github_keys_url}" -o "${HELM_TMP_ROOT}/${keys_filename}" + elif [ "${HAS_WGET}" == "true" ]; then + wget -q -O "${HELM_TMP_ROOT}/${keys_filename}" "${github_keys_url}" + fi + local gpg_keyring="${HELM_TMP_ROOT}/keyring.gpg" + local gpg_homedir="${HELM_TMP_ROOT}/gnupg" + mkdir -p -m 0700 "${gpg_homedir}" + local gpg_stderr_device="/dev/null" + if [ "${DEBUG}" == "true" ]; then + gpg_stderr_device="/dev/stderr" + fi + gpg --batch --quiet --homedir="${gpg_homedir}" --import "${HELM_TMP_ROOT}/${keys_filename}" 2> "${gpg_stderr_device}" + gpg --batch --no-default-keyring --keyring "${gpg_homedir}/pubring.kbx" --export > "${gpg_keyring}" + local github_release_url="https://github.com/helm/helm/releases/download/${TAG}" + if [ "${HAS_CURL}" == "true" ]; then + curl -SsL "${github_release_url}/helm-${TAG}-${OS}-${ARCH}.tar.gz.sha256.asc" -o "${HELM_TMP_ROOT}/helm-${TAG}-${OS}-${ARCH}.tar.gz.sha256.asc" + curl -SsL "${github_release_url}/helm-${TAG}-${OS}-${ARCH}.tar.gz.asc" -o "${HELM_TMP_ROOT}/helm-${TAG}-${OS}-${ARCH}.tar.gz.asc" + elif [ "${HAS_WGET}" == "true" ]; then + wget -q -O "${HELM_TMP_ROOT}/helm-${TAG}-${OS}-${ARCH}.tar.gz.sha256.asc" "${github_release_url}/helm-${TAG}-${OS}-${ARCH}.tar.gz.sha256.asc" + wget -q -O "${HELM_TMP_ROOT}/helm-${TAG}-${OS}-${ARCH}.tar.gz.asc" "${github_release_url}/helm-${TAG}-${OS}-${ARCH}.tar.gz.asc" + fi + local error_text="If you think this might be a potential security issue," + error_text="${error_text}\nplease see here: https://github.com/helm/community/blob/master/SECURITY.md" + local num_goodlines_sha=$(gpg --verify --keyring="${gpg_keyring}" --status-fd=1 "${HELM_TMP_ROOT}/helm-${TAG}-${OS}-${ARCH}.tar.gz.sha256.asc" 2> "${gpg_stderr_device}" | grep -c -E '^\[GNUPG:\] (GOODSIG|VALIDSIG)') + if [[ ${num_goodlines_sha} -lt 2 ]]; then + echo "Unable to verify the signature of helm-${TAG}-${OS}-${ARCH}.tar.gz.sha256!" + echo -e "${error_text}" + exit 1 + fi + local num_goodlines_tar=$(gpg --verify --keyring="${gpg_keyring}" --status-fd=1 "${HELM_TMP_ROOT}/helm-${TAG}-${OS}-${ARCH}.tar.gz.asc" 2> "${gpg_stderr_device}" | grep -c -E '^\[GNUPG:\] (GOODSIG|VALIDSIG)') + if [[ ${num_goodlines_tar} -lt 2 ]]; then + echo "Unable to verify the signature of helm-${TAG}-${OS}-${ARCH}.tar.gz!" + echo -e "${error_text}" + exit 1 + fi + echo "Done." } # fail_trap is executed if an error occurs. @@ -195,6 +281,11 @@ cleanup() { trap "fail_trap" EXIT set -e +# Set debug if desired +if [ "${DEBUG}" == "true" ]; then + set -x +fi + # Parsing input arguments (if any) export INPUT_ARGUMENTS="${@}" set -u @@ -229,6 +320,7 @@ verifySupported checkDesiredVersion if ! checkHelmInstalledVersion; then downloadFile + verifyFile installFile fi testVersion From ba4c8029c2faa452496dd743fa41b55e20c9614c Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Wed, 9 Sep 2020 13:22:41 +0800 Subject: [PATCH 06/45] Use T.cleanup() to cleanup helm-action-test T.Cleanup() is introduced since go-1.14 Signed-off-by: Li Zhijian --- pkg/action/action_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/action/action_test.go b/pkg/action/action_test.go index 0cbdb162b..c05b4403d 100644 --- a/pkg/action/action_test.go +++ b/pkg/action/action_test.go @@ -20,6 +20,7 @@ import ( "flag" "io/ioutil" "net/http" + "os" "path/filepath" "testing" @@ -56,6 +57,8 @@ func actionConfigFixture(t *testing.T) *Configuration { t.Fatal(err) } + t.Cleanup(func() { os.RemoveAll(tdir) }) + cache, err := registry.NewCache( registry.CacheOptDebug(true), registry.CacheOptRoot(filepath.Join(tdir, registry.CacheRootDir)), From 6f780bb7502cab2b1eddcdb3a127a15a5b2579f9 Mon Sep 17 00:00:00 2001 From: leigh capili Date: Wed, 9 Sep 2020 14:44:40 -0600 Subject: [PATCH 07/45] Document all env vars for CLI help Signed-off-by: leigh capili --- cmd/helm/root.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cmd/helm/root.go b/cmd/helm/root.go index 904f11a21..0135048a6 100644 --- a/cmd/helm/root.go +++ b/cmd/helm/root.go @@ -48,11 +48,20 @@ Environment variables: | $HELM_CACHE_HOME | set an alternative location for storing cached files. | | $HELM_CONFIG_HOME | set an alternative location for storing Helm configuration. | | $HELM_DATA_HOME | set an alternative location for storing Helm data. | +| $HELM_DEBUG | indicate whether or not Helm is running in Debug mode | | $HELM_DRIVER | set the backend storage driver. Values are: configmap, secret, memory, postgres | | $HELM_DRIVER_SQL_CONNECTION_STRING | set the connection string the SQL storage driver should use. | | $HELM_MAX_HISTORY | set the maximum number of helm release history. | +| $HELM_NAMESPACE | set the namespace used for the helm operations. | | $HELM_NO_PLUGINS | disable plugins. Set HELM_NO_PLUGINS=1 to disable plugins. | +| $HELM_PLUGINS | set the path to the plugins directory | +| $HELM_REGISTRY_CONFIG | set the path to the registry config file. | +| $HELM_REPOSITORY_CACHE | set the path to the repository cache directory | +| $HELM_REPOSITORY_CONFIG | set the path to the repositories file. | | $KUBECONFIG | set an alternative Kubernetes configuration file (default "~/.kube/config") | +| $HELM_KUBEAPISERVER | set the Kubernetes API Server Endpoint for authentication | +| $HELM_KUBECONTEXT | set the name of the kubeconfig context. | +| $HELM_KUBETOKEN | set the Bearer KubeToken used for authentication. | Helm stores cache, configuration, and data based on the following configuration order: From 9429af8b39c6888a41ffa2945d7c73676afb577e Mon Sep 17 00:00:00 2001 From: leigh capili Date: Sat, 5 Sep 2020 21:01:00 -0600 Subject: [PATCH 08/45] Support impersonation via flags similar to kubectl --as="user" Signed-off-by: leigh capili --- cmd/helm/load_plugins.go | 2 +- cmd/helm/plugin_test.go | 6 ++++++ cmd/helm/root.go | 2 ++ cmd/helm/testdata/output/env-comp.txt | 2 ++ pkg/cli/environment.go | 31 ++++++++++++++++++++++----- pkg/cli/environment_test.go | 25 ++++++++++++++++----- 6 files changed, 57 insertions(+), 11 deletions(-) diff --git a/cmd/helm/load_plugins.go b/cmd/helm/load_plugins.go index a6e0c4eae..e4aac6c0f 100644 --- a/cmd/helm/load_plugins.go +++ b/cmd/helm/load_plugins.go @@ -154,7 +154,7 @@ func callPluginExecutable(pluginName string, main string, argv []string, out io. func manuallyProcessArgs(args []string) ([]string, []string) { known := []string{} unknown := []string{} - kvargs := []string{"--kube-context", "--namespace", "-n", "--kubeconfig", "--kube-apiserver", "--kube-token", "--registry-config", "--repository-cache", "--repository-config"} + kvargs := []string{"--kube-context", "--namespace", "-n", "--kubeconfig", "--kube-apiserver", "--kube-token", "--kube-as-user", "--kube-as-group", "--registry-config", "--repository-cache", "--repository-config"} knownArg := func(a string) bool { for _, pre := range kvargs { if strings.HasPrefix(a, pre+"=") { diff --git a/cmd/helm/plugin_test.go b/cmd/helm/plugin_test.go index cf21d8460..0bf867f2a 100644 --- a/cmd/helm/plugin_test.go +++ b/cmd/helm/plugin_test.go @@ -37,6 +37,9 @@ func TestManuallyProcessArgs(t *testing.T) { "--kubeconfig", "/home/foo", "--kube-context=test1", "--kube-context", "test1", + "--kube-as-user", "pikachu", + "--kube-as-group", "teatime", + "--kube-as-group", "admins", "-n=test2", "-n", "test2", "--namespace=test2", @@ -51,6 +54,9 @@ func TestManuallyProcessArgs(t *testing.T) { "--kubeconfig", "/home/foo", "--kube-context=test1", "--kube-context", "test1", + "--kube-as-user", "pikachu", + "--kube-as-group", "teatime", + "--kube-as-group", "admins", "-n=test2", "-n", "test2", "--namespace=test2", diff --git a/cmd/helm/root.go b/cmd/helm/root.go index 0135048a6..82c87bd7d 100644 --- a/cmd/helm/root.go +++ b/cmd/helm/root.go @@ -60,6 +60,8 @@ Environment variables: | $HELM_REPOSITORY_CONFIG | set the path to the repositories file. | | $KUBECONFIG | set an alternative Kubernetes configuration file (default "~/.kube/config") | | $HELM_KUBEAPISERVER | set the Kubernetes API Server Endpoint for authentication | +| $HELM_KUBEASGROUPS | set the Username to impersonate for the operation. | +| $HELM_KUBEASUSER | set the Groups to use for impoersonation using a comma-separated list. | | $HELM_KUBECONTEXT | set the name of the kubeconfig context. | | $HELM_KUBETOKEN | set the Bearer KubeToken used for authentication. | diff --git a/cmd/helm/testdata/output/env-comp.txt b/cmd/helm/testdata/output/env-comp.txt index c4b46ae6b..3739d8bc1 100644 --- a/cmd/helm/testdata/output/env-comp.txt +++ b/cmd/helm/testdata/output/env-comp.txt @@ -4,6 +4,8 @@ HELM_CONFIG_HOME HELM_DATA_HOME HELM_DEBUG HELM_KUBEAPISERVER +HELM_KUBEASGROUPS +HELM_KUBEASUSER HELM_KUBECONTEXT HELM_KUBETOKEN HELM_MAX_HISTORY diff --git a/pkg/cli/environment.go b/pkg/cli/environment.go index a9994f03d..4f3abc08b 100644 --- a/pkg/cli/environment.go +++ b/pkg/cli/environment.go @@ -26,6 +26,7 @@ import ( "fmt" "os" "strconv" + "strings" "github.com/spf13/pflag" "k8s.io/cli-runtime/pkg/genericclioptions" @@ -47,6 +48,10 @@ type EnvSettings struct { KubeContext string // Bearer KubeToken used for authentication KubeToken string + // Username to impersonate for the operation + KubeAsUser string + // Groups to impersonate for the operation, multiple groups parsed from a comma delimited list + KubeAsGroups []string // Kubernetes API Server Endpoint for authentication KubeAPIServer string // Debug indicates whether or not Helm is running in Debug mode. @@ -69,6 +74,8 @@ func New() *EnvSettings { MaxHistory: envIntOr("HELM_MAX_HISTORY", defaultMaxHistory), KubeContext: os.Getenv("HELM_KUBECONTEXT"), KubeToken: os.Getenv("HELM_KUBETOKEN"), + KubeAsUser: os.Getenv("HELM_KUBEASUSER"), + KubeAsGroups: envCSV("HELM_KUBEASGROUPS"), KubeAPIServer: os.Getenv("HELM_KUBEAPISERVER"), PluginsDirectory: envOr("HELM_PLUGINS", helmpath.DataPath("plugins")), RegistryConfig: envOr("HELM_REGISTRY_CONFIG", helmpath.ConfigPath("registry.json")), @@ -79,11 +86,13 @@ func New() *EnvSettings { // bind to kubernetes config flags env.config = &genericclioptions.ConfigFlags{ - Namespace: &env.namespace, - Context: &env.KubeContext, - BearerToken: &env.KubeToken, - APIServer: &env.KubeAPIServer, - KubeConfig: &env.KubeConfig, + Namespace: &env.namespace, + Context: &env.KubeContext, + BearerToken: &env.KubeToken, + APIServer: &env.KubeAPIServer, + KubeConfig: &env.KubeConfig, + Impersonate: &env.KubeAsUser, + ImpersonateGroup: &env.KubeAsGroups, } return env } @@ -94,6 +103,8 @@ func (s *EnvSettings) AddFlags(fs *pflag.FlagSet) { fs.StringVar(&s.KubeConfig, "kubeconfig", "", "path to the kubeconfig file") fs.StringVar(&s.KubeContext, "kube-context", s.KubeContext, "name of the kubeconfig context to use") fs.StringVar(&s.KubeToken, "kube-token", s.KubeToken, "bearer token used for authentication") + fs.StringVar(&s.KubeAsUser, "kube-as-user", s.KubeAsUser, "Username to impersonate for the operation") + fs.StringArrayVar(&s.KubeAsGroups, "kube-as-group", s.KubeAsGroups, "Group to impersonate for the operation, this flag can be repeated to specify multiple groups.") fs.StringVar(&s.KubeAPIServer, "kube-apiserver", s.KubeAPIServer, "the address and the port for the Kubernetes API server") fs.BoolVar(&s.Debug, "debug", s.Debug, "enable verbose output") fs.StringVar(&s.RegistryConfig, "registry-config", s.RegistryConfig, "path to the registry config file") @@ -120,6 +131,14 @@ func envIntOr(name string, def int) int { return ret } +func envCSV(name string) (ls []string) { + trimmed := strings.Trim(os.Getenv(name), ", ") + if trimmed != "" { + ls = strings.Split(trimmed, ",") + } + return +} + func (s *EnvSettings) EnvVars() map[string]string { envvars := map[string]string{ "HELM_BIN": os.Args[0], @@ -137,6 +156,8 @@ func (s *EnvSettings) EnvVars() map[string]string { // broken, these are populated from helm flags and not kubeconfig. "HELM_KUBECONTEXT": s.KubeContext, "HELM_KUBETOKEN": s.KubeToken, + "HELM_KUBEASUSER": s.KubeAsUser, + "HELM_KUBEASGROUPS": strings.Join(s.KubeAsGroups, ","), "HELM_KUBEAPISERVER": s.KubeAPIServer, } if s.KubeConfig != "" { diff --git a/pkg/cli/environment_test.go b/pkg/cli/environment_test.go index 3234a133b..ffdbce68b 100644 --- a/pkg/cli/environment_test.go +++ b/pkg/cli/environment_test.go @@ -18,6 +18,7 @@ package cli import ( "os" + "reflect" "strings" "testing" @@ -36,6 +37,8 @@ func TestEnvSettings(t *testing.T) { ns, kcontext string debug bool maxhistory int + kAsUser string + kAsGroups []string }{ { name: "defaults", @@ -44,25 +47,31 @@ func TestEnvSettings(t *testing.T) { }, { name: "with flags set", - args: "--debug --namespace=myns", + args: "--debug --namespace=myns --kube-as-user=poro --kube-as-group=admins --kube-as-group=teatime --kube-as-group=snackeaters", ns: "myns", debug: true, maxhistory: defaultMaxHistory, + kAsUser: "poro", + kAsGroups: []string{"admins", "teatime", "snackeaters"}, }, { name: "with envvars set", - envvars: map[string]string{"HELM_DEBUG": "1", "HELM_NAMESPACE": "yourns", "HELM_MAX_HISTORY": "5"}, + envvars: map[string]string{"HELM_DEBUG": "1", "HELM_NAMESPACE": "yourns", "HELM_KUBEASUSER": "pikachu", "HELM_KUBEASGROUPS": ",,,operators,snackeaters,partyanimals", "HELM_MAX_HISTORY": "5"}, ns: "yourns", maxhistory: 5, debug: true, + kAsUser: "pikachu", + kAsGroups: []string{"operators", "snackeaters", "partyanimals"}, }, { name: "with flags and envvars set", - args: "--debug --namespace=myns", - envvars: map[string]string{"HELM_DEBUG": "1", "HELM_NAMESPACE": "yourns"}, + args: "--debug --namespace=myns --kube-as-user=poro --kube-as-group=admins --kube-as-group=teatime --kube-as-group=snackeaters", + envvars: map[string]string{"HELM_DEBUG": "1", "HELM_NAMESPACE": "yourns", "HELM_KUBEASUSER": "pikachu", "HELM_KUBEASGROUPS": ",,,operators,snackeaters,partyanimals", "HELM_MAX_HISTORY": "5"}, ns: "myns", debug: true, - maxhistory: defaultMaxHistory, + maxhistory: 5, + kAsUser: "poro", + kAsGroups: []string{"admins", "teatime", "snackeaters"}, }, } @@ -92,6 +101,12 @@ func TestEnvSettings(t *testing.T) { if settings.MaxHistory != tt.maxhistory { t.Errorf("expected maxHistory %d, got %d", tt.maxhistory, settings.MaxHistory) } + if tt.kAsUser != settings.KubeAsUser { + t.Errorf("expected kAsUser %q, got %q", tt.kAsUser, settings.KubeAsUser) + } + if !reflect.DeepEqual(tt.kAsGroups, settings.KubeAsGroups) { + t.Errorf("expected kAsGroups %+v, got %+v", len(tt.kAsGroups), len(settings.KubeAsGroups)) + } }) } } From 35c5268d9dd98238319578c469072e80e4aeb1e7 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Wed, 9 Sep 2020 13:38:57 +0800 Subject: [PATCH 09/45] Use T.Cleanup() to cleanup temp dir helm-repotest For backward compatibility, as suggested by @bacongobbler, we introduce a new API NewTempServerWithCleanup Signed-off-by: Li Zhijian --- cmd/helm/dependency_build_test.go | 2 +- cmd/helm/dependency_update_test.go | 4 ++-- cmd/helm/pull_test.go | 2 +- cmd/helm/repo_add_test.go | 6 +++--- cmd/helm/repo_remove_test.go | 2 +- cmd/helm/repo_update_test.go | 2 +- cmd/helm/show_test.go | 2 +- pkg/downloader/chart_downloader_test.go | 6 +++--- pkg/downloader/manager_test.go | 4 ++-- pkg/repo/repotest/server.go | 14 ++++++++++++++ pkg/repo/repotest/server_test.go | 2 +- 11 files changed, 30 insertions(+), 16 deletions(-) diff --git a/cmd/helm/dependency_build_test.go b/cmd/helm/dependency_build_test.go index eeca12fa6..d6dfdabcb 100644 --- a/cmd/helm/dependency_build_test.go +++ b/cmd/helm/dependency_build_test.go @@ -28,7 +28,7 @@ import ( ) func TestDependencyBuildCmd(t *testing.T) { - srv, err := repotest.NewTempServer("testdata/testcharts/*.tgz") + srv, err := repotest.NewTempServerWithCleanup(t, "testdata/testcharts/*.tgz") defer srv.Stop() if err != nil { t.Fatal(err) diff --git a/cmd/helm/dependency_update_test.go b/cmd/helm/dependency_update_test.go index 1f9d55867..bf27c7b6c 100644 --- a/cmd/helm/dependency_update_test.go +++ b/cmd/helm/dependency_update_test.go @@ -33,7 +33,7 @@ import ( ) func TestDependencyUpdateCmd(t *testing.T) { - srv, err := repotest.NewTempServer("testdata/testcharts/*.tgz") + srv, err := repotest.NewTempServerWithCleanup(t, "testdata/testcharts/*.tgz") if err != nil { t.Fatal(err) } @@ -121,7 +121,7 @@ func TestDependencyUpdateCmd_DontDeleteOldChartsOnError(t *testing.T) { defer resetEnv()() defer ensure.HelmHome(t)() - srv, err := repotest.NewTempServer("testdata/testcharts/*.tgz") + srv, err := repotest.NewTempServerWithCleanup(t, "testdata/testcharts/*.tgz") if err != nil { t.Fatal(err) } diff --git a/cmd/helm/pull_test.go b/cmd/helm/pull_test.go index 3f769a1bc..1d439e873 100644 --- a/cmd/helm/pull_test.go +++ b/cmd/helm/pull_test.go @@ -26,7 +26,7 @@ import ( ) func TestPullCmd(t *testing.T) { - srv, err := repotest.NewTempServer("testdata/testcharts/*.tgz*") + srv, err := repotest.NewTempServerWithCleanup(t, "testdata/testcharts/*.tgz*") if err != nil { t.Fatal(err) } diff --git a/cmd/helm/repo_add_test.go b/cmd/helm/repo_add_test.go index 9ef64390b..19281f3aa 100644 --- a/cmd/helm/repo_add_test.go +++ b/cmd/helm/repo_add_test.go @@ -34,7 +34,7 @@ import ( ) func TestRepoAddCmd(t *testing.T) { - srv, err := repotest.NewTempServer("testdata/testserver/*.*") + srv, err := repotest.NewTempServerWithCleanup(t, "testdata/testserver/*.*") if err != nil { t.Fatal(err) } @@ -53,7 +53,7 @@ func TestRepoAddCmd(t *testing.T) { } func TestRepoAdd(t *testing.T) { - ts, err := repotest.NewTempServer("testdata/testserver/*.*") + ts, err := repotest.NewTempServerWithCleanup(t, "testdata/testserver/*.*") if err != nil { t.Fatal(err) } @@ -118,7 +118,7 @@ func TestRepoAddConcurrentDirNotExist(t *testing.T) { } func repoAddConcurrent(t *testing.T, testName, repoFile string) { - ts, err := repotest.NewTempServer("testdata/testserver/*.*") + ts, err := repotest.NewTempServerWithCleanup(t, "testdata/testserver/*.*") if err != nil { t.Fatal(err) } diff --git a/cmd/helm/repo_remove_test.go b/cmd/helm/repo_remove_test.go index 0ea1d63d2..cb5c6e9ab 100644 --- a/cmd/helm/repo_remove_test.go +++ b/cmd/helm/repo_remove_test.go @@ -30,7 +30,7 @@ import ( ) func TestRepoRemove(t *testing.T) { - ts, err := repotest.NewTempServer("testdata/testserver/*.*") + ts, err := repotest.NewTempServerWithCleanup(t, "testdata/testserver/*.*") if err != nil { t.Fatal(err) } diff --git a/cmd/helm/repo_update_test.go b/cmd/helm/repo_update_test.go index e5e4eb337..4b16a1ea7 100644 --- a/cmd/helm/repo_update_test.go +++ b/cmd/helm/repo_update_test.go @@ -75,7 +75,7 @@ func TestUpdateCharts(t *testing.T) { defer resetEnv()() defer ensure.HelmHome(t)() - ts, err := repotest.NewTempServer("testdata/testserver/*.*") + ts, err := repotest.NewTempServerWithCleanup(t, "testdata/testserver/*.*") if err != nil { t.Fatal(err) } diff --git a/cmd/helm/show_test.go b/cmd/helm/show_test.go index 2734faf5e..ac5294d3c 100644 --- a/cmd/helm/show_test.go +++ b/cmd/helm/show_test.go @@ -26,7 +26,7 @@ import ( ) func TestShowPreReleaseChart(t *testing.T) { - srv, err := repotest.NewTempServer("testdata/testcharts/*.tgz*") + srv, err := repotest.NewTempServerWithCleanup(t, "testdata/testcharts/*.tgz*") if err != nil { t.Fatal(err) } diff --git a/pkg/downloader/chart_downloader_test.go b/pkg/downloader/chart_downloader_test.go index abfb007ff..b456143a1 100644 --- a/pkg/downloader/chart_downloader_test.go +++ b/pkg/downloader/chart_downloader_test.go @@ -172,7 +172,7 @@ func TestIsTar(t *testing.T) { func TestDownloadTo(t *testing.T) { // Set up a fake repo with basic auth enabled - srv, err := repotest.NewTempServer("testdata/*.tgz*") + srv, err := repotest.NewTempServerWithCleanup(t, "testdata/*.tgz*") srv.Stop() if err != nil { t.Fatal(err) @@ -229,7 +229,7 @@ func TestDownloadTo(t *testing.T) { func TestDownloadTo_TLS(t *testing.T) { // Set up mock server w/ tls enabled - srv, err := repotest.NewTempServer("testdata/*.tgz*") + srv, err := repotest.NewTempServerWithCleanup(t, "testdata/*.tgz*") srv.Stop() if err != nil { t.Fatal(err) @@ -285,7 +285,7 @@ func TestDownloadTo_VerifyLater(t *testing.T) { dest := ensure.TempDir(t) // Set up a fake repo - srv, err := repotest.NewTempServer("testdata/*.tgz*") + srv, err := repotest.NewTempServerWithCleanup(t, "testdata/*.tgz*") if err != nil { t.Fatal(err) } diff --git a/pkg/downloader/manager_test.go b/pkg/downloader/manager_test.go index e60cf7624..9532cca7c 100644 --- a/pkg/downloader/manager_test.go +++ b/pkg/downloader/manager_test.go @@ -183,7 +183,7 @@ func TestGetRepoNames(t *testing.T) { func TestUpdateBeforeBuild(t *testing.T) { // Set up a fake repo - srv, err := repotest.NewTempServer("testdata/*.tgz*") + srv, err := repotest.NewTempServerWithCleanup(t, "testdata/*.tgz*") if err != nil { t.Fatal(err) } @@ -257,7 +257,7 @@ func TestUpdateBeforeBuild(t *testing.T) { // If each of these main fields (name, version, repository) is not supplied by dep param, default value will be used. func checkBuildWithOptionalFields(t *testing.T, chartName string, dep chart.Dependency) { // Set up a fake repo - srv, err := repotest.NewTempServer("testdata/*.tgz*") + srv, err := repotest.NewTempServerWithCleanup(t, "testdata/*.tgz*") if err != nil { t.Fatal(err) } diff --git a/pkg/repo/repotest/server.go b/pkg/repo/repotest/server.go index b18bce49c..9032b1e30 100644 --- a/pkg/repo/repotest/server.go +++ b/pkg/repo/repotest/server.go @@ -21,6 +21,7 @@ import ( "net/http/httptest" "os" "path/filepath" + "testing" "helm.sh/helm/v3/internal/tlsutil" @@ -29,6 +30,19 @@ import ( "helm.sh/helm/v3/pkg/repo" ) +// NewTempServerWithCleanup creates a server inside of a temp dir. +// +// If the passed in string is not "", it will be treated as a shell glob, and files +// will be copied from that path to the server's docroot. +// +// The caller is responsible for stopping the server. +// The temp dir will be removed by testing package automatically when test finished. +func NewTempServerWithCleanup(t *testing.T, glob string) (*Server, error) { + srv, err := NewTempServer(glob) + t.Cleanup(func() { os.RemoveAll(srv.docroot) }) + return srv, err +} + // NewTempServer creates a server inside of a temp dir. // // If the passed in string is not "", it will be treated as a shell glob, and files diff --git a/pkg/repo/repotest/server_test.go b/pkg/repo/repotest/server_test.go index ee62791af..6d71071da 100644 --- a/pkg/repo/repotest/server_test.go +++ b/pkg/repo/repotest/server_test.go @@ -99,7 +99,7 @@ func TestServer(t *testing.T) { func TestNewTempServer(t *testing.T) { defer ensure.HelmHome(t)() - srv, err := NewTempServer("testdata/examplechart-0.1.0.tgz") + srv, err := NewTempServerWithCleanup(t, "testdata/examplechart-0.1.0.tgz") if err != nil { t.Fatal(err) } From cccc2867ea8242de55e32910b1d6c2f252ed1af5 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Thu, 10 Sep 2020 09:31:03 +0800 Subject: [PATCH 10/45] mark NewTempServer as Deprecated Please use NewTempServerWithCleanup instead Signed-off-by: Li Zhijian --- pkg/repo/repotest/server.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/repo/repotest/server.go b/pkg/repo/repotest/server.go index 9032b1e30..270c8958a 100644 --- a/pkg/repo/repotest/server.go +++ b/pkg/repo/repotest/server.go @@ -50,6 +50,8 @@ func NewTempServerWithCleanup(t *testing.T, glob string) (*Server, error) { // // The caller is responsible for destroying the temp directory as well as stopping // the server. +// +// Deprecated: use NewTempServerWithCleanup func NewTempServer(glob string) (*Server, error) { tdir, err := ioutil.TempDir("", "helm-repotest-") if err != nil { From d9ad9153c8ddd910bdd3bb0d0cb6b0f693189c54 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Wed, 9 Sep 2020 13:45:37 +0800 Subject: [PATCH 11/45] Use RemoveAll to remove a non-empty directory Signed-off-by: Li Zhijian --- pkg/chart/loader/load_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/chart/loader/load_test.go b/pkg/chart/loader/load_test.go index 40b86dec2..16a94d4eb 100644 --- a/pkg/chart/loader/load_test.go +++ b/pkg/chart/loader/load_test.go @@ -310,7 +310,7 @@ func TestLoadInvalidArchive(t *testing.T) { if err != nil { t.Fatal(err) } - defer os.Remove(tmpdir) + defer os.RemoveAll(tmpdir) writeTar := func(filename, internalPath string, body []byte) { dest, err := os.Create(filename) From 4258e8664e6b8dfd1b9c3b8ca2115930b296c41c Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Wed, 9 Sep 2020 14:32:41 +0800 Subject: [PATCH 12/45] Use T.cleanup() to cleanup cmdtest_temp file Signed-off-by: Li Zhijian --- pkg/kube/client_test.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/pkg/kube/client_test.go b/pkg/kube/client_test.go index 568afa094..de5358aee 100644 --- a/pkg/kube/client_test.go +++ b/pkg/kube/client_test.go @@ -91,9 +91,12 @@ func newResponse(code int, obj runtime.Object) (*http.Response, error) { return &http.Response{StatusCode: code, Header: header, Body: body}, nil } -func newTestClient() *Client { +func newTestClient(t *testing.T) *Client { + testFactory := cmdtesting.NewTestFactory() + t.Cleanup(testFactory.Cleanup) + return &Client{ - Factory: cmdtesting.NewTestFactory().WithNamespace("default"), + Factory: testFactory.WithNamespace("default"), Log: nopLogger, } } @@ -107,7 +110,7 @@ func TestUpdate(t *testing.T) { var actions []string - c := newTestClient() + c := newTestClient(t) c.Factory.(*cmdtesting.TestFactory).UnstructuredClient = &fake.RESTClient{ NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { @@ -232,7 +235,7 @@ func TestBuild(t *testing.T) { }, } - c := newTestClient() + c := newTestClient(t) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Test for an invalid manifest @@ -279,7 +282,7 @@ func TestPerform(t *testing.T) { return nil } - c := newTestClient() + c := newTestClient(t) infos, err := c.Build(tt.reader, false) if err != nil && err.Error() != tt.errMessage { t.Errorf("Error while building manifests: %v", err) From 319240841575d97bbac0cc274c18fdb162b72919 Mon Sep 17 00:00:00 2001 From: Paul Brousseau Date: Sun, 13 Sep 2020 21:22:18 -0700 Subject: [PATCH 13/45] Fixing typo in engine comments Signed-off-by: Paul Brousseau --- pkg/engine/engine.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index 5aa0ed8ec..155d50a38 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -40,7 +40,7 @@ type Engine struct { Strict bool // In LintMode, some 'required' template values may be missing, so don't fail LintMode bool - // the rest config to connect to te kubernetes api + // the rest config to connect to the kubernetes api config *rest.Config } From 8b2cf17648889270ac5c5985f5bb9ef5e43d12d6 Mon Sep 17 00:00:00 2001 From: Ma Xinjian Date: Wed, 2 Sep 2020 13:43:35 +0800 Subject: [PATCH 14/45] Add support to install helm install the binary that was compiled by make build Signed-off-by: Ma Xinjian --- Makefile | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Makefile b/Makefile index 97f99fd86..85041f7f4 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,5 @@ BINDIR := $(CURDIR)/bin +INSTALL_PATH ?= /usr/local/bin DIST_DIRS := find * -type d -exec TARGETS := darwin/amd64 linux/amd64 linux/386 linux/arm linux/arm64 linux/ppc64le linux/s390x windows/amd64 TARGET_OBJS ?= darwin-amd64.tar.gz darwin-amd64.tar.gz.sha256 darwin-amd64.tar.gz.sha256sum linux-amd64.tar.gz linux-amd64.tar.gz.sha256 linux-amd64.tar.gz.sha256sum linux-386.tar.gz linux-386.tar.gz.sha256 linux-386.tar.gz.sha256sum linux-arm.tar.gz linux-arm.tar.gz.sha256 linux-arm.tar.gz.sha256sum linux-arm64.tar.gz linux-arm64.tar.gz.sha256 linux-arm64.tar.gz.sha256sum linux-ppc64le.tar.gz linux-ppc64le.tar.gz.sha256 linux-ppc64le.tar.gz.sha256sum linux-s390x.tar.gz linux-s390x.tar.gz.sha256 linux-s390x.tar.gz.sha256sum windows-amd64.zip windows-amd64.zip.sha256 windows-amd64.zip.sha256sum @@ -62,6 +63,13 @@ build: $(BINDIR)/$(BINNAME) $(BINDIR)/$(BINNAME): $(SRC) GO111MODULE=on go build $(GOFLAGS) -tags '$(TAGS)' -ldflags '$(LDFLAGS)' -o '$(BINDIR)'/$(BINNAME) ./cmd/helm +# ------------------------------------------------------------------------------ +# install + +.PHONY: install +install: build + @install "$(BINDIR)/$(BINNAME)" "$(INSTALL_PATH)/$(BINNAME)" + # ------------------------------------------------------------------------------ # test From f917c169d001a96fbdfd7943c441fb09509b9f7f Mon Sep 17 00:00:00 2001 From: Morten Linderud Date: Tue, 1 Sep 2020 12:08:41 +0200 Subject: [PATCH 15/45] Makefile: Fix LDFLAGS overriding When distributions build software it's desirable to have the ability to define own linker flags, or Go flags. As `-ldflags` defined in `go build` overrides `-ldflags` defined in the env variable `GOFLAGS`, there is a distinct need to be able to replace the default values with new ones or append to them. Fixes #8645 Signed-off-by: Morten Linderud --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 97f99fd86..0014efa5f 100644 --- a/Makefile +++ b/Makefile @@ -49,6 +49,7 @@ endif LDFLAGS += -X helm.sh/helm/v3/internal/version.metadata=${VERSION_METADATA} LDFLAGS += -X helm.sh/helm/v3/internal/version.gitCommit=${GIT_COMMIT} LDFLAGS += -X helm.sh/helm/v3/internal/version.gitTreeState=${GIT_DIRTY} +LDFLAGS += $(EXT_LDFLAGS) .PHONY: all all: build From 459dcd7f728b38ec44c72d79192ee93d6964d53d Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Mon, 14 Sep 2020 07:36:41 -0400 Subject: [PATCH 16/45] fix(comp): Disable file comp for output formats It does not make sense to suggest files to the user as output formats. Signed-off-by: Marc Khouzam --- cmd/helm/flags.go | 2 +- cmd/helm/testdata/output/output-comp.txt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/helm/flags.go b/cmd/helm/flags.go index 544fb7608..d1329c279 100644 --- a/cmd/helm/flags.go +++ b/cmd/helm/flags.go @@ -69,7 +69,7 @@ func bindOutputFlag(cmd *cobra.Command, varRef *output.Format) { formatNames = append(formatNames, format) } } - return formatNames, cobra.ShellCompDirectiveDefault + return formatNames, cobra.ShellCompDirectiveNoFileComp }) if err != nil { diff --git a/cmd/helm/testdata/output/output-comp.txt b/cmd/helm/testdata/output/output-comp.txt index be574756b..e7799a56b 100644 --- a/cmd/helm/testdata/output/output-comp.txt +++ b/cmd/helm/testdata/output/output-comp.txt @@ -1,5 +1,5 @@ table json yaml -:0 -Completion ended with directive: ShellCompDirectiveDefault +:4 +Completion ended with directive: ShellCompDirectiveNoFileComp From d1c8561be6e6b08bbf425dc79631149100a1e0db Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Wed, 16 Sep 2020 09:37:37 +0800 Subject: [PATCH 17/45] fix incorrect wildcard expand Previously, when there is no *.{gz,zip} files under _dist, the wildcard will be expanded to 2 strings '_dist/*.gz' and '_dist/*.zip'(see below). helm$ ls _dist helm$ make checksum for f in _dist/*.{gz,zip} ; do \ shasum -a 256 "${f}" | sed 's/_dist\///' > "${f}.sha256sum" ; \ shasum -a 256 "${f}" | awk '{print $1}' > "${f}.sha256" ; \ done shasum: _dist/*.gz: shasum: _dist/*.gz: shasum: _dist/*.zip: shasum: _dist/*.zip: helm$ ls _dist '*.gz.sha256' '*.gz.sha256sum' '*.zip.sha256' '*.zip.sha256sum' Signed-off-by: Li Zhijian --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index a9eac8033..79fc31976 100644 --- a/Makefile +++ b/Makefile @@ -165,7 +165,7 @@ fetch-dist: .PHONY: sign sign: - for f in _dist/*.{gz,zip,sha256,sha256sum} ; do \ + for f in $$(ls _dist/*.{gz,zip,sha256,sha256sum} 2>/dev/null) ; do \ gpg --armor --detach-sign $${f} ; \ done @@ -178,7 +178,7 @@ sign: # removed in Helm v4. .PHONY: checksum checksum: - for f in _dist/*.{gz,zip} ; do \ + for f in $$(ls _dist/*.{gz,zip} 2>/dev/null) ; do \ shasum -a 256 "$${f}" | sed 's/_dist\///' > "$${f}.sha256sum" ; \ shasum -a 256 "$${f}" | awk '{print $$1}' > "$${f}.sha256" ; \ done From 82398667dfe208407be9fe499ac96240aa8ce54b Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Tue, 8 Sep 2020 17:15:01 -0600 Subject: [PATCH 18/45] fix: check mode bits on kubeconfig file Signed-off-by: Matt Butcher --- cmd/helm/root.go | 3 ++ cmd/helm/root_unix.go | 58 ++++++++++++++++++++++++++++++++++++ cmd/helm/root_unix_test.go | 61 ++++++++++++++++++++++++++++++++++++++ cmd/helm/root_windows.go | 24 +++++++++++++++ 4 files changed, 146 insertions(+) create mode 100644 cmd/helm/root_unix.go create mode 100644 cmd/helm/root_unix_test.go create mode 100644 cmd/helm/root_windows.go diff --git a/cmd/helm/root.go b/cmd/helm/root.go index 82c87bd7d..91542bb7e 100644 --- a/cmd/helm/root.go +++ b/cmd/helm/root.go @@ -204,5 +204,8 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string // Find and add plugins loadPlugins(cmd, out) + // Check permissions on critical files + checkPerms(out) + return cmd, nil } diff --git a/cmd/helm/root_unix.go b/cmd/helm/root_unix.go new file mode 100644 index 000000000..210842b35 --- /dev/null +++ b/cmd/helm/root_unix.go @@ -0,0 +1,58 @@ +/* +Copyright The Helm Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "fmt" + "io" + "os" + "os/user" + "path/filepath" +) + +func checkPerms(out io.Writer) { + // This function MUST NOT FAIL, as it is just a check for a common permissions problem. + // If for some reason the function hits a stopping condition, it may panic. But only if + // we can be sure that it is panicing because Helm cannot proceed. + + kc := settings.KubeConfig + if kc == "" { + kc = os.Getenv("KUBECONFIG") + } + if kc == "" { + u, err := user.Current() + if err != nil { + // No idea where to find KubeConfig, so return silently. Many helm commands + // can proceed happily without a KUBECONFIG, so this is not a fatal error. + return + } + kc = filepath.Join(u.HomeDir, ".kube", "config") + } + fi, err := os.Stat(kc) + if err != nil { + // DO NOT error if no KubeConfig is found. Not all commands require one. + return + } + + perm := fi.Mode().Perm() + if perm&0040 > 0 { + fmt.Fprintf(out, "WARNING: Kubernetes configuration file is group-readable. This is insecure. Location: %s\n", kc) + } + if perm&0004 > 0 { + fmt.Fprintf(out, "WARNING: Kubernetes configuration file is world-readable. This is insecure. Location: %s\n", kc) + } +} diff --git a/cmd/helm/root_unix_test.go b/cmd/helm/root_unix_test.go new file mode 100644 index 000000000..73f18ec28 --- /dev/null +++ b/cmd/helm/root_unix_test.go @@ -0,0 +1,61 @@ +/* +Copyright The Helm Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "bytes" + "io/ioutil" + "os" + "path/filepath" + "strings" + "testing" +) + +func TestCheckPerms(t *testing.T) { + tdir, err := ioutil.TempDir("", "helmtest") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tdir) + tfile := filepath.Join(tdir, "testconfig") + fh, err := os.OpenFile(tfile, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0440) + if err != nil { + t.Errorf("Failed to create temp file: %s", err) + } + + tconfig := settings.KubeConfig + settings.KubeConfig = tfile + defer func() { settings.KubeConfig = tconfig }() + + var b bytes.Buffer + checkPerms(&b) + expectPrefix := "WARNING: Kubernetes configuration file is group-readable. This is insecure. Location:" + if !strings.HasPrefix(b.String(), expectPrefix) { + t.Errorf("Expected to get a warning for group perms. Got %q", b.String()) + } + + if err := fh.Chmod(0404); err != nil { + t.Errorf("Could not change mode on file: %s", err) + } + b.Reset() + checkPerms(&b) + expectPrefix = "WARNING: Kubernetes configuration file is world-readable. This is insecure. Location:" + if !strings.HasPrefix(b.String(), expectPrefix) { + t.Errorf("Expected to get a warning for world perms. Got %q", b.String()) + } + +} diff --git a/cmd/helm/root_windows.go b/cmd/helm/root_windows.go new file mode 100644 index 000000000..243780d40 --- /dev/null +++ b/cmd/helm/root_windows.go @@ -0,0 +1,24 @@ +/* +Copyright The Helm Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import "io" + +func checkPerms(out io.Writer) { + // Not yet implemented on Windows. If you know how to do a comprehensive perms + // check on Windows, contributions welcomed! +} From c4ef82be13a0a3b6b42ce92bdd0357f4f6ac9e62 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Wed, 9 Sep 2020 16:33:18 -0600 Subject: [PATCH 19/45] validate the name passed in during helm create Signed-off-by: Matt Butcher --- pkg/chartutil/create.go | 27 +++++++++++++++++++++++++++ pkg/chartutil/create_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/pkg/chartutil/create.go b/pkg/chartutil/create.go index 6e382b961..d4b65e9b8 100644 --- a/pkg/chartutil/create.go +++ b/pkg/chartutil/create.go @@ -21,6 +21,7 @@ import ( "io/ioutil" "os" "path/filepath" + "regexp" "strings" "github.com/pkg/errors" @@ -30,6 +31,12 @@ import ( "helm.sh/helm/v3/pkg/chart/loader" ) +// chartName is a regular expression for testing the supplied name of a chart. +// This regular expression is probably stricter than it needs to be. We can relax it +// somewhat. Newline characters, as well as $, quotes, +, parens, and % are known to be +// problematic. +var chartName = regexp.MustCompile("^[a-zA-Z0-9._-]+$") + const ( // ChartfileName is the default Chart file name. ChartfileName = "Chart.yaml" @@ -63,6 +70,10 @@ const ( TestConnectionName = TemplatesTestsDir + sep + "test-connection.yaml" ) +// maxChartNameLength is lower than the limits we know of with certain file systems, +// and with certain Kubernetes fields. +const maxChartNameLength = 250 + const sep = string(filepath.Separator) const defaultChartfile = `apiVersion: v2 @@ -522,6 +533,12 @@ func CreateFrom(chartfile *chart.Metadata, dest, src string) error { // error. In such a case, this will attempt to clean up by removing the // new chart directory. func Create(name, dir string) (string, error) { + + // Sanity-check the name of a chart so user doesn't create one that causes problems. + if err := validateChartName(name); err != nil { + return "", err + } + path, err := filepath.Abs(dir) if err != nil { return path, err @@ -627,3 +644,13 @@ func writeFile(name string, content []byte) error { } return ioutil.WriteFile(name, content, 0644) } + +func validateChartName(name string) error { + if name == "" || len(name) > maxChartNameLength { + return fmt.Errorf("chart name must be between 1 and %d characters", maxChartNameLength) + } + if !chartName.MatchString(name) { + return fmt.Errorf("chart name must match the regular expression %q", chartName.String()) + } + return nil +} diff --git a/pkg/chartutil/create_test.go b/pkg/chartutil/create_test.go index a11c45140..f68ebbd63 100644 --- a/pkg/chartutil/create_test.go +++ b/pkg/chartutil/create_test.go @@ -117,3 +117,30 @@ func TestCreateFrom(t *testing.T) { } } } + +func TestValidateChartName(t *testing.T) { + for name, shouldPass := range map[string]bool{ + "": false, + "abcdefghijklmnopqrstuvwxyz-_.": true, + "ABCDEFGHIJKLMNOPQRSTUVWXYZ-_.": true, + "$hello": false, + "Hellô": false, + "he%%o": false, + "he\nllo": false, + + "abcdefghijklmnopqrstuvwxyz-_." + + "abcdefghijklmnopqrstuvwxyz-_." + + "abcdefghijklmnopqrstuvwxyz-_." + + "abcdefghijklmnopqrstuvwxyz-_." + + "abcdefghijklmnopqrstuvwxyz-_." + + "abcdefghijklmnopqrstuvwxyz-_." + + "abcdefghijklmnopqrstuvwxyz-_." + + "abcdefghijklmnopqrstuvwxyz-_." + + "abcdefghijklmnopqrstuvwxyz-_." + + "ABCDEFGHIJKLMNOPQRSTUVWXYZ-_.": false, + } { + if err := validateChartName(name); (err != nil) == shouldPass { + t.Errorf("test for %q failed", name) + } + } +} From ed5fba5142fa5a2366df143616e9161ff866a53d Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Wed, 9 Sep 2020 13:30:30 -0600 Subject: [PATCH 20/45] refactor the release name validation to be consistent across Helm Signed-off-by: Matt Butcher --- pkg/action/action.go | 7 +- pkg/action/action_test.go | 37 ----------- pkg/action/history.go | 3 +- pkg/action/release_testing.go | 3 +- pkg/action/rollback.go | 3 +- pkg/action/uninstall.go | 3 +- pkg/action/upgrade.go | 15 +---- pkg/chartutil/validate_name.go | 99 +++++++++++++++++++++++++++++ pkg/chartutil/validate_name_test.go | 91 ++++++++++++++++++++++++++ pkg/lint/rules/template.go | 14 +--- 10 files changed, 206 insertions(+), 69 deletions(-) create mode 100644 pkg/chartutil/validate_name.go create mode 100644 pkg/chartutil/validate_name_test.go diff --git a/pkg/action/action.go b/pkg/action/action.go index 071db709b..79bb4f638 100644 --- a/pkg/action/action.go +++ b/pkg/action/action.go @@ -58,14 +58,15 @@ var ( errMissingRelease = errors.New("no release provided") // errInvalidRevision indicates that an invalid release revision number was provided. errInvalidRevision = errors.New("invalid release revision") - // errInvalidName indicates that an invalid release name was provided - errInvalidName = errors.New("invalid release name, must match regex ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])+$ and the length must not longer than 53") // errPending indicates that another instance of Helm is already applying an operation on a release. errPending = errors.New("another operation (install/upgrade/rollback) is in progress") ) // ValidName is a regular expression for resource names. // +// DEPRECATED: This will be removed in Helm 4, and is no longer used here. See +// pkg/chartutil.ValidateName for the replacement. +// // According to the Kubernetes help text, the regular expression it uses is: // // [a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)* @@ -294,7 +295,7 @@ func (c *Configuration) Now() time.Time { } func (c *Configuration) releaseContent(name string, version int) (*release.Release, error) { - if err := validateReleaseName(name); err != nil { + if err := chartutil.ValidateReleaseName(name); err != nil { return nil, errors.Errorf("releaseContent: Release name is invalid: %s", name) } diff --git a/pkg/action/action_test.go b/pkg/action/action_test.go index c05b4403d..fedf260fb 100644 --- a/pkg/action/action_test.go +++ b/pkg/action/action_test.go @@ -319,40 +319,3 @@ func TestGetVersionSet(t *testing.T) { t.Error("Non-existent version is reported found.") } } - -// TestValidName is a regression test for ValidName -// -// Kubernetes has strict naming conventions for resource names. This test represents -// those conventions. -// -// See https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names -// -// NOTE: At the time of this writing, the docs above say that names cannot begin with -// digits. However, `kubectl`'s regular expression explicit allows this, and -// Kubernetes (at least as of 1.18) also accepts resources whose names begin with digits. -func TestValidName(t *testing.T) { - names := map[string]bool{ - "": false, - "foo": true, - "foo.bar1234baz.seventyone": true, - "FOO": false, - "123baz": true, - "foo.BAR.baz": false, - "one-two": true, - "-two": false, - "one_two": false, - "a..b": false, - "%^&#$%*@^*@&#^": false, - "example:com": false, - "example%%com": false, - } - for input, expectPass := range names { - if ValidName.MatchString(input) != expectPass { - st := "fail" - if expectPass { - st = "succeed" - } - t.Errorf("Expected %q to %s", input, st) - } - } -} diff --git a/pkg/action/history.go b/pkg/action/history.go index a592745e9..f4043609c 100644 --- a/pkg/action/history.go +++ b/pkg/action/history.go @@ -19,6 +19,7 @@ package action import ( "github.com/pkg/errors" + "helm.sh/helm/v3/pkg/chartutil" "helm.sh/helm/v3/pkg/release" ) @@ -45,7 +46,7 @@ func (h *History) Run(name string) ([]*release.Release, error) { return nil, err } - if err := validateReleaseName(name); err != nil { + if err := chartutil.ValidateReleaseName(name); err != nil { return nil, errors.Errorf("release name is invalid: %s", name) } diff --git a/pkg/action/release_testing.go b/pkg/action/release_testing.go index 795c3c747..2f6f5cfce 100644 --- a/pkg/action/release_testing.go +++ b/pkg/action/release_testing.go @@ -25,6 +25,7 @@ import ( "github.com/pkg/errors" v1 "k8s.io/api/core/v1" + "helm.sh/helm/v3/pkg/chartutil" "helm.sh/helm/v3/pkg/release" ) @@ -51,7 +52,7 @@ func (r *ReleaseTesting) Run(name string) (*release.Release, error) { return nil, err } - if err := validateReleaseName(name); err != nil { + if err := chartutil.ValidateReleaseName(name); err != nil { return nil, errors.Errorf("releaseTest: Release name is invalid: %s", name) } diff --git a/pkg/action/rollback.go b/pkg/action/rollback.go index 8773b6271..542acefae 100644 --- a/pkg/action/rollback.go +++ b/pkg/action/rollback.go @@ -24,6 +24,7 @@ import ( "github.com/pkg/errors" + "helm.sh/helm/v3/pkg/chartutil" "helm.sh/helm/v3/pkg/release" helmtime "helm.sh/helm/v3/pkg/time" ) @@ -90,7 +91,7 @@ func (r *Rollback) Run(name string) error { // prepareRollback finds the previous release and prepares a new release object with // the previous release's configuration func (r *Rollback) prepareRollback(name string) (*release.Release, *release.Release, error) { - if err := validateReleaseName(name); err != nil { + if err := chartutil.ValidateReleaseName(name); err != nil { return nil, nil, errors.Errorf("prepareRollback: Release name is invalid: %s", name) } diff --git a/pkg/action/uninstall.go b/pkg/action/uninstall.go index a51a283d6..c466c6ee2 100644 --- a/pkg/action/uninstall.go +++ b/pkg/action/uninstall.go @@ -22,6 +22,7 @@ import ( "github.com/pkg/errors" + "helm.sh/helm/v3/pkg/chartutil" "helm.sh/helm/v3/pkg/release" "helm.sh/helm/v3/pkg/releaseutil" helmtime "helm.sh/helm/v3/pkg/time" @@ -62,7 +63,7 @@ func (u *Uninstall) Run(name string) (*release.UninstallReleaseResponse, error) return &release.UninstallReleaseResponse{Release: r}, nil } - if err := validateReleaseName(name); err != nil { + if err := chartutil.ValidateReleaseName(name); err != nil { return nil, errors.Errorf("uninstall: Release name is invalid: %s", name) } diff --git a/pkg/action/upgrade.go b/pkg/action/upgrade.go index b707e7e69..c439af79d 100644 --- a/pkg/action/upgrade.go +++ b/pkg/action/upgrade.go @@ -115,7 +115,7 @@ func (u *Upgrade) Run(name string, chart *chart.Chart, vals map[string]interface // the user doesn't have to specify both u.Wait = u.Wait || u.Atomic - if err := validateReleaseName(name); err != nil { + if err := chartutil.ValidateReleaseName(name); err != nil { return nil, errors.Errorf("release name is invalid: %s", name) } u.cfg.Log("preparing upgrade for %s", name) @@ -142,19 +142,6 @@ func (u *Upgrade) Run(name string, chart *chart.Chart, vals map[string]interface return res, nil } -func validateReleaseName(releaseName string) error { - if releaseName == "" { - return errMissingRelease - } - - // Check length first, since that is a less expensive operation. - if len(releaseName) > releaseNameMaxLen || !ValidName.MatchString(releaseName) { - return errInvalidName - } - - return nil -} - // prepareUpgrade builds an upgraded release for an upgrade operation. func (u *Upgrade) prepareUpgrade(name string, chart *chart.Chart, vals map[string]interface{}) (*release.Release, *release.Release, error) { if chart == nil { diff --git a/pkg/chartutil/validate_name.go b/pkg/chartutil/validate_name.go new file mode 100644 index 000000000..22132c80e --- /dev/null +++ b/pkg/chartutil/validate_name.go @@ -0,0 +1,99 @@ +/* +Copyright The Helm Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package chartutil + +import ( + "regexp" + + "github.com/pkg/errors" +) + +// validName is a regular expression for resource names. +// +// According to the Kubernetes help text, the regular expression it uses is: +// +// [a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)* +// +// This follows the above regular expression (but requires a full string match, not partial). +// +// The Kubernetes documentation is here, though it is not entirely correct: +// https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names +var validName = regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`) + +var ( + // errMissingName indicates that a release (name) was not provided. + errMissingName = errors.New("no name provided") + + // errInvalidName indicates that an invalid release name was provided + errInvalidName = errors.New("invalid release name, must match regex ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])+$ and the length must not longer than 53") + + // errInvalidKubernetesName indicates that the name does not meet the Kubernetes + // restrictions on metadata names. + errInvalidKubernetesName = errors.New("invalid metadata name, must match regex ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])+$ and the length must not longer than 253") +) + +const ( + // maxNameLen is the maximum length Helm allows for a release name + maxReleaseNameLen = 53 + // maxMetadataNameLen is the maximum length Kubernetes allows for any name. + maxMetadataNameLen = 253 +) + +// ValidateReleaseName performs checks for an entry for a Helm release name +// +// For Helm to allow a name, it must be below a certain character count (53) and also match +// a reguar expression. +// +// According to the Kubernetes help text, the regular expression it uses is: +// +// [a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)* +// +// This follows the above regular expression (but requires a full string match, not partial). +// +// The Kubernetes documentation is here, though it is not entirely correct: +// https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names +func ValidateReleaseName(name string) error { + // This case is preserved for backwards compatibility + if name == "" { + return errMissingName + + } + if len(name) > maxReleaseNameLen || !validName.MatchString(name) { + return errInvalidName + } + return nil +} + +// ValidateMetadataName validates the name field of a Kubernetes metadata object. +// +// Empty strings, strings longer than 253 chars, or strings that don't match the regexp +// will fail. +// +// According to the Kubernetes help text, the regular expression it uses is: +// +// [a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)* +// +// This follows the above regular expression (but requires a full string match, not partial). +// +// The Kubernetes documentation is here, though it is not entirely correct: +// https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names +func ValidateMetadataName(name string) error { + if name == "" || len(name) > maxMetadataNameLen || !validName.MatchString(name) { + return errInvalidKubernetesName + } + return nil +} diff --git a/pkg/chartutil/validate_name_test.go b/pkg/chartutil/validate_name_test.go new file mode 100644 index 000000000..5f0792f94 --- /dev/null +++ b/pkg/chartutil/validate_name_test.go @@ -0,0 +1,91 @@ +/* +Copyright The Helm Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package chartutil + +import "testing" + +// TestValidateName is a regression test for ValidateName +// +// Kubernetes has strict naming conventions for resource names. This test represents +// those conventions. +// +// See https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names +// +// NOTE: At the time of this writing, the docs above say that names cannot begin with +// digits. However, `kubectl`'s regular expression explicit allows this, and +// Kubernetes (at least as of 1.18) also accepts resources whose names begin with digits. +func TestValidateReleaseName(t *testing.T) { + names := map[string]bool{ + "": false, + "foo": true, + "foo.bar1234baz.seventyone": true, + "FOO": false, + "123baz": true, + "foo.BAR.baz": false, + "one-two": true, + "-two": false, + "one_two": false, + "a..b": false, + "%^&#$%*@^*@&#^": false, + "example:com": false, + "example%%com": false, + "a1111111111111111111111111111111111111111111111111111111111z": false, + } + for input, expectPass := range names { + if err := ValidateReleaseName(input); (err == nil) != expectPass { + st := "fail" + if expectPass { + st = "succeed" + } + t.Errorf("Expected %q to %s", input, st) + } + } +} + +func TestValidateMetadataName(t *testing.T) { + names := map[string]bool{ + "": false, + "foo": true, + "foo.bar1234baz.seventyone": true, + "FOO": false, + "123baz": true, + "foo.BAR.baz": false, + "one-two": true, + "-two": false, + "one_two": false, + "a..b": false, + "%^&#$%*@^*@&#^": false, + "example:com": false, + "example%%com": false, + "a1111111111111111111111111111111111111111111111111111111111z": true, + "a1111111111111111111111111111111111111111111111111111111111z" + + "a1111111111111111111111111111111111111111111111111111111111z" + + "a1111111111111111111111111111111111111111111111111111111111z" + + "a1111111111111111111111111111111111111111111111111111111111z" + + "a1111111111111111111111111111111111111111111111111111111111z" + + "a1111111111111111111111111111111111111111111111111111111111z": false, + } + for input, expectPass := range names { + if err := ValidateMetadataName(input); (err == nil) != expectPass { + st := "fail" + if expectPass { + st = "succeed" + } + t.Errorf("Expected %q to %s", input, st) + } + } +} diff --git a/pkg/lint/rules/template.go b/pkg/lint/rules/template.go index 73d645264..5de0819c4 100644 --- a/pkg/lint/rules/template.go +++ b/pkg/lint/rules/template.go @@ -40,14 +40,6 @@ var ( releaseTimeSearch = regexp.MustCompile(`\.Release\.Time`) ) -// validName is a regular expression for names. -// -// This is different than action.ValidName. It conforms to the regular expression -// `kubectl` says it uses, plus it disallows empty names. -// -// For details, see https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names -var validName = regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`) - // Templates lints the templates in the Linter. func Templates(linter *support.Linter, values map[string]interface{}, namespace string, strict bool) { fpath := "templates/" @@ -199,10 +191,10 @@ func validateMetadataName(obj *K8sYamlStruct) error { } // This will return an error if the characters do not abide by the standard OR if the // name is left empty. - if validName.MatchString(obj.Metadata.Name) { - return nil + if err := chartutil.ValidateMetadataName(obj.Metadata.Name); err != nil { + return errors.Wrapf(err, "object name does not conform to Kubernetes naming requirements: %q", obj.Metadata.Name) } - return fmt.Errorf("object name does not conform to Kubernetes naming requirements: %q", obj.Metadata.Name) + return nil } func validateNoCRDHooks(manifest []byte) error { From 106f1fb45c93fe862ac86d9b774e2de8b1dd314c Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Wed, 9 Sep 2020 16:47:59 -0600 Subject: [PATCH 21/45] fixed bug that caused helm create to not overwrite modified files Signed-off-by: Matt Butcher --- cmd/helm/create.go | 1 + pkg/chartutil/create.go | 11 ++++++++-- pkg/chartutil/create_test.go | 39 ++++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/cmd/helm/create.go b/cmd/helm/create.go index 21a7e026c..fe5cc540a 100644 --- a/cmd/helm/create.go +++ b/cmd/helm/create.go @@ -107,6 +107,7 @@ func (o *createOptions) run(out io.Writer) error { return chartutil.CreateFrom(cfile, filepath.Dir(o.name), lstarter) } + chartutil.Stderr = out _, err := chartutil.Create(chartname, filepath.Dir(o.name)) return err } diff --git a/pkg/chartutil/create.go b/pkg/chartutil/create.go index 6e382b961..4ae5c7f3c 100644 --- a/pkg/chartutil/create.go +++ b/pkg/chartutil/create.go @@ -18,6 +18,7 @@ package chartutil import ( "fmt" + "io" "io/ioutil" "os" "path/filepath" @@ -468,6 +469,12 @@ spec: restartPolicy: Never ` +// Stderr is an io.Writer to which error messages can be written +// +// In Helm 4, this will be replaced. It is needed in Helm 3 to preserve API backward +// compatibility. +var Stderr io.Writer = os.Stderr + // CreateFrom creates a new chart, but scaffolds it from the src chart. func CreateFrom(chartfile *chart.Metadata, dest, src string) error { schart, err := loader.Load(src) @@ -601,8 +608,8 @@ func Create(name, dir string) (string, error) { for _, file := range files { if _, err := os.Stat(file.path); err == nil { - // File exists and is okay. Skip it. - continue + // There is no handle to a preferred output stream here. + fmt.Fprintf(Stderr, "WARNING: File %q already exists. Overwriting.\n", file.path) } if err := writeFile(file.path, file.content); err != nil { return cdir, err diff --git a/pkg/chartutil/create_test.go b/pkg/chartutil/create_test.go index a11c45140..49dcde633 100644 --- a/pkg/chartutil/create_test.go +++ b/pkg/chartutil/create_test.go @@ -117,3 +117,42 @@ func TestCreateFrom(t *testing.T) { } } } + +// TestCreate_Overwrite is a regression test for making sure that files are overwritten. +func TestCreate_Overwrite(t *testing.T) { + tdir, err := ioutil.TempDir("", "helm-") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tdir) + + var errlog bytes.Buffer + + if _, err := Create("foo", tdir); err != nil { + t.Fatal(err) + } + + dir := filepath.Join(tdir, "foo") + + tplname := filepath.Join(dir, "templates/hpa.yaml") + writeFile(tplname, []byte("FOO")) + + // Now re-run the create + Stderr = &errlog + if _, err := Create("foo", tdir); err != nil { + t.Fatal(err) + } + + data, err := ioutil.ReadFile(tplname) + if err != nil { + t.Fatal(err) + } + + if string(data) == "FOO" { + t.Fatal("File that should have been modified was not.") + } + + if errlog.Len() == 0 { + t.Errorf("Expected warnings about overwriting files.") + } +} From 40b78002873d525a31c5dec75c8607be67327360 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Fri, 11 Sep 2020 16:23:34 -0600 Subject: [PATCH 22/45] handle case where dependency name collisions break dependency resolution Signed-off-by: Matt Butcher --- pkg/action/dependency.go | 87 ++++++++++++++++++++-------- pkg/action/dependency_test.go | 103 ++++++++++++++++++++++++++++++++++ 2 files changed, 167 insertions(+), 23 deletions(-) diff --git a/pkg/action/dependency.go b/pkg/action/dependency.go index 4a4b8ebad..4c80d0159 100644 --- a/pkg/action/dependency.go +++ b/pkg/action/dependency.go @@ -21,6 +21,7 @@ import ( "io" "os" "path/filepath" + "strings" "github.com/Masterminds/semver/v3" "github.com/gosuri/uitable" @@ -61,6 +62,7 @@ func (d *Dependency) List(chartpath string, out io.Writer) error { return nil } +// dependecyStatus returns a string describing the status of a dependency viz a viz the parent chart. func (d *Dependency) dependencyStatus(chartpath string, dep *chart.Dependency, parent *chart.Chart) string { filename := fmt.Sprintf("%s-%s.tgz", dep.Name, "*") @@ -75,35 +77,40 @@ func (d *Dependency) dependencyStatus(chartpath string, dep *chart.Dependency, p case err != nil: return "bad pattern" case len(archives) > 1: - return "too many matches" - case len(archives) == 1: - archive := archives[0] - if _, err := os.Stat(archive); err == nil { - c, err := loader.Load(archive) - if err != nil { - return "corrupt" + // See if the second part is a SemVer + found := []string{} + for _, arc := range archives { + // we need to trip the prefix dirs and the extension off. + filename = strings.TrimSuffix(filepath.Base(arc), ".tgz") + maybeVersion := strings.TrimPrefix(filename, fmt.Sprintf("%s-", dep.Name)) + + if _, err := semver.StrictNewVersion(maybeVersion); err == nil { + // If the version parsed without an error, it is possibly a valid + // version. + found = append(found, arc) } - if c.Name() != dep.Name { - return "misnamed" + } + + if l := len(found); l == 1 { + // If we get here, we do the same thing as in len(archives) == 1. + if r := statArchiveForStatus(found[0], dep); r != "" { + return r } - if c.Metadata.Version != dep.Version { - constraint, err := semver.NewConstraint(dep.Version) - if err != nil { - return "invalid version" - } + // Fall through and look for directories + } else if l > 1 { + return "too many matches" + } - v, err := semver.NewVersion(c.Metadata.Version) - if err != nil { - return "invalid version" - } + // The sanest thing to do here is to fall through and see if we have any directory + // matches. - if !constraint.Check(v) { - return "wrong version" - } - } - return "ok" + case len(archives) == 1: + archive := archives[0] + if r := statArchiveForStatus(archive, dep); r != "" { + return r } + } // End unnecessary code. @@ -137,6 +144,40 @@ func (d *Dependency) dependencyStatus(chartpath string, dep *chart.Dependency, p return "unpacked" } +// stat an archive and return a message if the stat is successful +// +// This is a refactor of the code originally in dependencyStatus. It is here to +// support legacy behavior, and should be removed in Helm 4. +func statArchiveForStatus(archive string, dep *chart.Dependency) string { + if _, err := os.Stat(archive); err == nil { + c, err := loader.Load(archive) + if err != nil { + return "corrupt" + } + if c.Name() != dep.Name { + return "misnamed" + } + + if c.Metadata.Version != dep.Version { + constraint, err := semver.NewConstraint(dep.Version) + if err != nil { + return "invalid version" + } + + v, err := semver.NewVersion(c.Metadata.Version) + if err != nil { + return "invalid version" + } + + if !constraint.Check(v) { + return "wrong version" + } + } + return "ok" + } + return "" +} + // printDependencies prints all of the dependencies in the yaml file. func (d *Dependency) printDependencies(chartpath string, out io.Writer, c *chart.Chart) { table := uitable.New() diff --git a/pkg/action/dependency_test.go b/pkg/action/dependency_test.go index 4f3cb69a5..b5032a377 100644 --- a/pkg/action/dependency_test.go +++ b/pkg/action/dependency_test.go @@ -18,9 +18,16 @@ package action import ( "bytes" + "io/ioutil" + "os" + "path/filepath" "testing" + "github.com/stretchr/testify/assert" + "helm.sh/helm/v3/internal/test" + "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/chartutil" ) func TestList(t *testing.T) { @@ -56,3 +63,99 @@ func TestList(t *testing.T) { test.AssertGoldenBytes(t, buf.Bytes(), tcase.golden) } } + +// TestDependencyStatus_Dashes is a regression test to make sure that dashes in +// chart names do not cause resolution problems. +func TestDependencyStatus_Dashes(t *testing.T) { + // Make a temp dir + dir, err := ioutil.TempDir("", "helmtest-") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + chartpath := filepath.Join(dir, "charts") + if err := os.MkdirAll(chartpath, 0700); err != nil { + t.Fatal(err) + } + + // Add some fake charts + first := buildChart(withName("first-chart")) + _, err = chartutil.Save(first, chartpath) + if err != nil { + t.Fatal(err) + } + + second := buildChart(withName("first-chart-second-chart")) + _, err = chartutil.Save(second, chartpath) + if err != nil { + t.Fatal(err) + } + + dep := &chart.Dependency{ + Name: "first-chart", + Version: "0.1.0", + } + + // Now try to get the deps + stat := NewDependency().dependencyStatus(dir, dep, first) + if stat != "ok" { + t.Errorf("Unexpected status: %q", stat) + } +} + +func TestStatArchiveForStatus(t *testing.T) { + // Make a temp dir + dir, err := ioutil.TempDir("", "helmtest-") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + chartpath := filepath.Join(dir, "charts") + if err := os.MkdirAll(chartpath, 0700); err != nil { + t.Fatal(err) + } + + // unsaved chart + lilith := buildChart(withName("lilith")) + + // dep referring to chart + dep := &chart.Dependency{ + Name: "lilith", + Version: "1.2.3", + } + + is := assert.New(t) + + lilithpath := filepath.Join(chartpath, "lilith-1.2.3.tgz") + is.Empty(statArchiveForStatus(lilithpath, dep)) + + // save the chart (version 0.1.0, because that is the default) + where, err := chartutil.Save(lilith, chartpath) + is.NoError(err) + + // Should get "wrong version" because we asked for 1.2.3 and got 0.1.0 + is.Equal("wrong version", statArchiveForStatus(where, dep)) + + // Break version on dep + dep = &chart.Dependency{ + Name: "lilith", + Version: "1.2.3.4.5", + } + is.Equal("invalid version", statArchiveForStatus(where, dep)) + + // Break the name + dep = &chart.Dependency{ + Name: "lilith2", + Version: "1.2.3", + } + is.Equal("misnamed", statArchiveForStatus(where, dep)) + + // Now create the right version + dep = &chart.Dependency{ + Name: "lilith", + Version: "0.1.0", + } + is.Equal("ok", statArchiveForStatus(where, dep)) +} From 882eeac7271858124a3cecbe22c5d7d61560714f Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Fri, 11 Sep 2020 16:32:45 -0600 Subject: [PATCH 23/45] replace --no-update with --force-update and invert default. BREAKING. Signed-off-by: Matt Butcher --- cmd/helm/repo_add.go | 19 ++++++++++++------- cmd/helm/repo_add_test.go | 20 +++++++++++--------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/cmd/helm/repo_add.go b/cmd/helm/repo_add.go index 3eeb342f5..1c2162bfa 100644 --- a/cmd/helm/repo_add.go +++ b/cmd/helm/repo_add.go @@ -38,11 +38,11 @@ import ( ) type repoAddOptions struct { - name string - url string - username string - password string - noUpdate bool + name string + url string + username string + password string + forceUpdate bool certFile string keyFile string @@ -51,6 +51,9 @@ type repoAddOptions struct { repoFile string repoCache string + + // Deprecated, but cannot be removed until Helm 4 + deprecatedNoUpdate bool } func newRepoAddCmd(out io.Writer) *cobra.Command { @@ -74,7 +77,8 @@ func newRepoAddCmd(out io.Writer) *cobra.Command { f := cmd.Flags() f.StringVar(&o.username, "username", "", "chart repository username") f.StringVar(&o.password, "password", "", "chart repository password") - f.BoolVar(&o.noUpdate, "no-update", false, "raise error if repo is already registered") + f.BoolVar(&o.forceUpdate, "force-update", false, "replace (overwrite) the repo if it already exists") + f.BoolVar(&o.deprecatedNoUpdate, "no-update", false, "Ignored. Formerly, it would disabled forced updates. It is deprecated by force-update.") f.StringVar(&o.certFile, "cert-file", "", "identify HTTPS client using this SSL certificate file") f.StringVar(&o.keyFile, "key-file", "", "identify HTTPS client using this SSL key file") f.StringVar(&o.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle") @@ -112,7 +116,8 @@ func (o *repoAddOptions) run(out io.Writer) error { return err } - if o.noUpdate && f.Has(o.name) { + // If the repo exists and --force-update was not specified, error out. + if !o.forceUpdate && f.Has(o.name) { return errors.Errorf("repository name (%s) already exists, please specify a different name", o.name) } diff --git a/cmd/helm/repo_add_test.go b/cmd/helm/repo_add_test.go index 9ef64390b..05fa084df 100644 --- a/cmd/helm/repo_add_test.go +++ b/cmd/helm/repo_add_test.go @@ -65,10 +65,11 @@ func TestRepoAdd(t *testing.T) { const testRepoName = "test-name" o := &repoAddOptions{ - name: testRepoName, - url: ts.URL(), - noUpdate: true, - repoFile: repoFile, + name: testRepoName, + url: ts.URL(), + forceUpdate: false, + deprecatedNoUpdate: true, + repoFile: repoFile, } os.Setenv(xdg.CacheHomeEnvVar, rootDir) @@ -94,7 +95,7 @@ func TestRepoAdd(t *testing.T) { t.Errorf("Error cache charts file was not created for repository %s", testRepoName) } - o.noUpdate = false + o.forceUpdate = true if err := o.run(ioutil.Discard); err != nil { t.Errorf("Repository was not updated: %s", err) @@ -130,10 +131,11 @@ func repoAddConcurrent(t *testing.T, testName, repoFile string) { go func(name string) { defer wg.Done() o := &repoAddOptions{ - name: name, - url: ts.URL(), - noUpdate: true, - repoFile: repoFile, + name: name, + url: ts.URL(), + deprecatedNoUpdate: true, + forceUpdate: false, + repoFile: repoFile, } if err := o.run(ioutil.Discard); err != nil { t.Error(err) From e2da16f5146e2709211f116cb81dd8f9c9a62fd5 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Thu, 10 Sep 2020 16:45:40 -0600 Subject: [PATCH 24/45] improve the HTTP detection for tar archives Signed-off-by: Matt Butcher --- pkg/plugin/installer/http_installer.go | 12 +++++ pkg/plugin/installer/http_installer_test.go | 52 +++++++++++++++++++-- pkg/plugin/installer/installer.go | 22 ++++++++- pkg/plugin/installer/installer_test.go | 40 ++++++++++++++++ 4 files changed, 122 insertions(+), 4 deletions(-) create mode 100644 pkg/plugin/installer/installer_test.go diff --git a/pkg/plugin/installer/http_installer.go b/pkg/plugin/installer/http_installer.go index 28e50b72b..bcbcbde93 100644 --- a/pkg/plugin/installer/http_installer.go +++ b/pkg/plugin/installer/http_installer.go @@ -59,6 +59,18 @@ var Extractors = map[string]Extractor{ ".tgz": &TarGzExtractor{}, } +// Convert a media type to an extractor extension. +// +// This should be refactored in Helm 4, combined with the extension-based mechanism. +func mediaTypeToExtension(mt string) (string, bool) { + switch strings.ToLower(mt) { + case "application/gzip", "application/x-gzip", "application/x-tgz", "application/x-gtar": + return ".tgz", true + default: + return "", false + } +} + // NewExtractor creates a new extractor matching the source file name func NewExtractor(source string) (Extractor, error) { for suffix, extractor := range Extractors { diff --git a/pkg/plugin/installer/http_installer_test.go b/pkg/plugin/installer/http_installer_test.go index 3eb92ee77..e89fea29d 100644 --- a/pkg/plugin/installer/http_installer_test.go +++ b/pkg/plugin/installer/http_installer_test.go @@ -20,9 +20,13 @@ import ( "bytes" "compress/gzip" "encoding/base64" + "fmt" "io/ioutil" + "net/http" + "net/http/httptest" "os" "path/filepath" + "strings" "syscall" "testing" @@ -63,9 +67,24 @@ func TestStripName(t *testing.T) { } } +func mockArchiveServer() *httptest.Server { + return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if !strings.HasSuffix(r.URL.Path, ".tar.gz") { + w.Header().Add("Content-Type", "text/html") + fmt.Fprintln(w, "broken") + return + } + w.Header().Add("Content-Type", "application/gzip") + fmt.Fprintln(w, "test") + })) +} + func TestHTTPInstaller(t *testing.T) { defer ensure.HelmHome(t)() - source := "https://repo.localdomain/plugins/fake-plugin-0.0.1.tar.gz" + + srv := mockArchiveServer() + defer srv.Close() + source := srv.URL + "/plugins/fake-plugin-0.0.1.tar.gz" if err := os.MkdirAll(helmpath.DataPath("plugins"), 0755); err != nil { t.Fatalf("Could not create %s: %s", helmpath.DataPath("plugins"), err) @@ -111,7 +130,9 @@ func TestHTTPInstaller(t *testing.T) { func TestHTTPInstallerNonExistentVersion(t *testing.T) { defer ensure.HelmHome(t)() - source := "https://repo.localdomain/plugins/fake-plugin-0.0.2.tar.gz" + srv := mockArchiveServer() + defer srv.Close() + source := srv.URL + "/plugins/fake-plugin-0.0.1.tar.gz" if err := os.MkdirAll(helmpath.DataPath("plugins"), 0755); err != nil { t.Fatalf("Could not create %s: %s", helmpath.DataPath("plugins"), err) @@ -141,7 +162,9 @@ func TestHTTPInstallerNonExistentVersion(t *testing.T) { } func TestHTTPInstallerUpdate(t *testing.T) { - source := "https://repo.localdomain/plugins/fake-plugin-0.0.1.tar.gz" + srv := mockArchiveServer() + defer srv.Close() + source := srv.URL + "/plugins/fake-plugin-0.0.1.tar.gz" defer ensure.HelmHome(t)() if err := os.MkdirAll(helmpath.DataPath("plugins"), 0755); err != nil { @@ -307,3 +330,26 @@ func TestCleanJoin(t *testing.T) { } } + +func TestMediaTypeToExtension(t *testing.T) { + + for mt, shouldPass := range map[string]bool{ + "": false, + "application/gzip": true, + "application/x-gzip": true, + "application/x-tgz": true, + "application/x-gtar": true, + "application/json": false, + } { + ext, ok := mediaTypeToExtension(mt) + if ok != shouldPass { + t.Errorf("Media type %q failed test", mt) + } + if shouldPass && ext == "" { + t.Errorf("Expected an extension but got empty string") + } + if !shouldPass && len(ext) != 0 { + t.Error("Expected extension to be empty for unrecognized type") + } + } +} diff --git a/pkg/plugin/installer/installer.go b/pkg/plugin/installer/installer.go index 61b49ab3b..6f01494e5 100644 --- a/pkg/plugin/installer/installer.go +++ b/pkg/plugin/installer/installer.go @@ -18,6 +18,7 @@ package installer import ( "fmt" "log" + "net/http" "os" "path/filepath" "strings" @@ -89,10 +90,29 @@ func isLocalReference(source string) bool { } // isRemoteHTTPArchive checks if the source is a http/https url and is an archive +// +// It works by checking whether the source looks like a URL and, if it does, running a +// HEAD operation to see if the remote resource is a file that we understand. func isRemoteHTTPArchive(source string) bool { if strings.HasPrefix(source, "http://") || strings.HasPrefix(source, "https://") { + res, err := http.Head(source) + if err != nil { + // If we get an error at the network layer, we can't install it. So + // we return false. + return false + } + + // Next, we look for the content type or content disposition headers to see + // if they have matching extractors. + contentType := res.Header.Get("content-type") + foundSuffix, ok := mediaTypeToExtension(contentType) + if !ok { + // Media type not recognized + return false + } + for suffix := range Extractors { - if strings.HasSuffix(source, suffix) { + if strings.HasSuffix(foundSuffix, suffix) { return true } } diff --git a/pkg/plugin/installer/installer_test.go b/pkg/plugin/installer/installer_test.go new file mode 100644 index 000000000..a11464924 --- /dev/null +++ b/pkg/plugin/installer/installer_test.go @@ -0,0 +1,40 @@ +/* +Copyright The Helm Authors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package installer + +import "testing" + +func TestIsRemoteHTTPArchive(t *testing.T) { + srv := mockArchiveServer() + defer srv.Close() + source := srv.URL + "/plugins/fake-plugin-0.0.1.tar.gz" + + if isRemoteHTTPArchive("/not/a/URL") { + t.Errorf("Expected non-URL to return false") + } + + if isRemoteHTTPArchive("https://127.0.0.1:123/fake/plugin-1.2.3.tgz") { + t.Errorf("Bad URL should not have succeeded.") + } + + if !isRemoteHTTPArchive(source) { + t.Errorf("Expected %q to be a valid archive URL", source) + } + + if isRemoteHTTPArchive(source + "-not-an-extension") { + t.Error("Expected media type match to fail") + } +} From 2a74204508f005d89fe51b0e2824dae4f30b3252 Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Thu, 17 Sep 2020 11:09:37 -0700 Subject: [PATCH 25/45] go fmt Signed-off-by: Matthew Fisher --- pkg/chartutil/create_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/chartutil/create_test.go b/pkg/chartutil/create_test.go index 6cf3154ad..9a473fc59 100644 --- a/pkg/chartutil/create_test.go +++ b/pkg/chartutil/create_test.go @@ -154,7 +154,7 @@ func TestCreate_Overwrite(t *testing.T) { if errlog.Len() == 0 { t.Errorf("Expected warnings about overwriting files.") - } + } } func TestValidateChartName(t *testing.T) { From 59d5b94d35b24a500e30839a7c69f05d9ff077e2 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Thu, 17 Sep 2020 12:31:23 -0600 Subject: [PATCH 26/45] Merge pull request from GHSA-9vp5-m38w-j776 --- pkg/chart/chart.go | 4 +++ pkg/chart/errors.go | 7 ++++++ pkg/chart/metadata.go | 19 +++++++++++++++ pkg/chart/metadata_test.go | 50 +++++++++++++++++++++++++++++++++++++- 4 files changed, 79 insertions(+), 1 deletion(-) diff --git a/pkg/chart/chart.go b/pkg/chart/chart.go index bd75375a4..a3bed63a3 100644 --- a/pkg/chart/chart.go +++ b/pkg/chart/chart.go @@ -17,6 +17,7 @@ package chart import ( "path/filepath" + "regexp" "strings" ) @@ -26,6 +27,9 @@ const APIVersionV1 = "v1" // APIVersionV2 is the API version number for version 2. const APIVersionV2 = "v2" +// aliasNameFormat defines the characters that are legal in an alias name. +var aliasNameFormat = regexp.MustCompile("^[a-zA-Z0-9_-]+$") + // Chart is a helm package that contains metadata, a default config, zero or more // optionally parameterizable templates, and zero or more charts (dependencies). type Chart struct { diff --git a/pkg/chart/errors.go b/pkg/chart/errors.go index 4cb4189e6..2fad5f370 100644 --- a/pkg/chart/errors.go +++ b/pkg/chart/errors.go @@ -15,9 +15,16 @@ limitations under the License. package chart +import "fmt" + // ValidationError represents a data validation error. type ValidationError string func (v ValidationError) Error() string { return "validation: " + string(v) } + +// ValidationErrorf takes a message and formatting options and creates a ValidationError +func ValidationErrorf(msg string, args ...interface{}) ValidationError { + return ValidationError(fmt.Sprintf(msg, args...)) +} diff --git a/pkg/chart/metadata.go b/pkg/chart/metadata.go index 96a3965b9..1848eb280 100644 --- a/pkg/chart/metadata.go +++ b/pkg/chart/metadata.go @@ -81,6 +81,15 @@ func (md *Metadata) Validate() error { if !isValidChartType(md.Type) { return ValidationError("chart.metadata.type must be application or library") } + + // Aliases need to be validated here to make sure that the alias name does + // not contain any illegal characters. + for _, dependency := range md.Dependencies { + if err := validateDependency(dependency); err != nil { + return err + } + } + // TODO validate valid semver here? return nil } @@ -92,3 +101,13 @@ func isValidChartType(in string) bool { } return false } + +// validateDependency checks for common problems with the dependency datastructure in +// the chart. This check must be done at load time before the dependency's charts are +// loaded. +func validateDependency(dep *Dependency) error { + if len(dep.Alias) > 0 && !aliasNameFormat.MatchString(dep.Alias) { + return ValidationErrorf("dependency %q has disallowed characters in the alias", dep.Name) + } + return nil +} diff --git a/pkg/chart/metadata_test.go b/pkg/chart/metadata_test.go index 8b436000b..0c7b173dd 100644 --- a/pkg/chart/metadata_test.go +++ b/pkg/chart/metadata_test.go @@ -48,12 +48,60 @@ func TestValidate(t *testing.T) { &Metadata{Name: "test", APIVersion: "v2", Version: "1.0", Type: "application"}, nil, }, + { + &Metadata{ + Name: "test", + APIVersion: "v2", + Version: "1.0", + Type: "application", + Dependencies: []*Dependency{ + {Name: "dependency", Alias: "legal-alias"}, + }, + }, + nil, + }, + { + &Metadata{ + Name: "test", + APIVersion: "v2", + Version: "1.0", + Type: "application", + Dependencies: []*Dependency{ + {Name: "bad", Alias: "illegal alias"}, + }, + }, + ValidationError("dependency \"bad\" has disallowed characters in the alias"), + }, } for _, tt := range tests { result := tt.md.Validate() if result != tt.err { - t.Errorf("expected %s, got %s", tt.err, result) + t.Errorf("expected '%s', got '%s'", tt.err, result) + } + } +} + +func TestValidateDependency(t *testing.T) { + dep := &Dependency{ + Name: "example", + } + for value, shouldFail := range map[string]bool{ + "abcdefghijklmenopQRSTUVWXYZ-0123456780_": false, + "-okay": false, + "_okay": false, + "- bad": true, + " bad": true, + "bad\nvalue": true, + "bad ": true, + "bad$": true, + } { + dep.Alias = value + res := validateDependency(dep) + if res != nil && !shouldFail { + t.Errorf("Failed on case %q", dep.Alias) + } else if res == nil && shouldFail { + t.Errorf("Expected failure for %q", dep.Alias) } } } From 055dd41cbe53ce131ab0357524a7f6729e6e40dc Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Thu, 17 Sep 2020 12:33:59 -0600 Subject: [PATCH 27/45] Merge pull request from GHSA-jm56-5h66-w453 Signed-off-by: Matt Butcher --- pkg/downloader/chart_downloader_test.go | 2 +- pkg/repo/index.go | 19 +++++++++++++++- pkg/repo/index_test.go | 29 +++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/pkg/downloader/chart_downloader_test.go b/pkg/downloader/chart_downloader_test.go index b456143a1..b9fd3bf87 100644 --- a/pkg/downloader/chart_downloader_test.go +++ b/pkg/downloader/chart_downloader_test.go @@ -71,7 +71,7 @@ func TestResolveChartRef(t *testing.T) { if tt.fail { continue } - t.Errorf("%s: failed with error %s", tt.name, err) + t.Errorf("%s: failed with error %q", tt.name, err) continue } if got := u.String(); got != tt.expect { diff --git a/pkg/repo/index.go b/pkg/repo/index.go index 6ef2cf8b5..8b831029f 100644 --- a/pkg/repo/index.go +++ b/pkg/repo/index.go @@ -228,6 +228,23 @@ type ChartVersion struct { Created time.Time `json:"created,omitempty"` Removed bool `json:"removed,omitempty"` Digest string `json:"digest,omitempty"` + + // ChecksumDeprecated is deprecated in Helm 3, and therefore ignored. Helm 3 replaced + // this with Digest. However, with a strict YAML parser enabled, a field must be + // present on the struct for backwards compatibility. + ChecksumDeprecated string `json:"checksum,omitempty"` + + // EngineDeprecated is deprecated in Helm 3, and therefore ignored. However, with a strict + // YAML parser enabled, this field must be present. + EngineDeprecated string `json:"engine,omitempty"` + + // TillerVersionDeprecated is deprecated in Helm 3, and therefore ignored. However, with a strict + // YAML parser enabled, this field must be present. + TillerVersionDeprecated string `json:"tillerVersion,omitempty"` + + // URLDeprecated is deprectaed in Helm 3, superseded by URLs. It is ignored. However, + // with a strict YAML parser enabled, this must be present on the struct. + URLDeprecated string `json:"url,omitempty"` } // IndexDirectory reads a (flat) directory and generates an index. @@ -281,7 +298,7 @@ func IndexDirectory(dir, baseURL string) (*IndexFile, error) { // This will fail if API Version is not set (ErrNoAPIVersion) or if the unmarshal fails. func loadIndex(data []byte) (*IndexFile, error) { i := &IndexFile{} - if err := yaml.Unmarshal(data, i); err != nil { + if err := yaml.UnmarshalStrict(data, i); err != nil { return i, err } i.SortEntries() diff --git a/pkg/repo/index_test.go b/pkg/repo/index_test.go index 466a2c306..77b3a90ab 100644 --- a/pkg/repo/index_test.go +++ b/pkg/repo/index_test.go @@ -95,6 +95,35 @@ func TestLoadIndex(t *testing.T) { verifyLocalIndex(t, i) } +const indexWithDuplicates = ` +apiVersion: v1 +entries: + nginx: + - urls: + - https://kubernetes-charts.storage.googleapis.com/nginx-0.2.0.tgz + name: nginx + description: string + version: 0.2.0 + home: https://github.com/something/else + digest: "sha256:1234567890abcdef" + nginx: + - urls: + - https://kubernetes-charts.storage.googleapis.com/alpine-1.0.0.tgz + - http://storage2.googleapis.com/kubernetes-charts/alpine-1.0.0.tgz + name: alpine + description: string + version: 1.0.0 + home: https://github.com/something + digest: "sha256:1234567890abcdef" +` + +// TestLoadIndex_Duplicates is a regression to make sure that we don't non-deterministically allow duplicate packages. +func TestLoadIndex_Duplicates(t *testing.T) { + if _, err := loadIndex([]byte(indexWithDuplicates)); err == nil { + t.Errorf("Expected an error when duplicate entries are present") + } +} + func TestLoadIndexFile(t *testing.T) { i, err := LoadIndexFile(testfile) if err != nil { From 809e2d999e2c33e20e77f6bff30652d79c287542 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Thu, 17 Sep 2020 12:35:10 -0600 Subject: [PATCH 28/45] Merge pull request from GHSA-m54r-vrmv-hw33 Signed-off-by: Matt Butcher --- cmd/helm/load_plugins.go | 2 +- cmd/helm/plugin_install.go | 3 ++- pkg/plugin/plugin.go | 47 +++++++++++++++++++++++++++++++----- pkg/plugin/plugin_test.go | 49 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 93 insertions(+), 8 deletions(-) diff --git a/cmd/helm/load_plugins.go b/cmd/helm/load_plugins.go index e4aac6c0f..83590210a 100644 --- a/cmd/helm/load_plugins.go +++ b/cmd/helm/load_plugins.go @@ -59,7 +59,7 @@ func loadPlugins(baseCmd *cobra.Command, out io.Writer) { found, err := plugin.FindPlugins(settings.PluginsDirectory) if err != nil { - fmt.Fprintf(os.Stderr, "failed to load plugins: %s", err) + fmt.Fprintf(os.Stderr, "failed to load plugins: %s\n", err) return } diff --git a/cmd/helm/plugin_install.go b/cmd/helm/plugin_install.go index 183d3dc57..4e8ee327b 100644 --- a/cmd/helm/plugin_install.go +++ b/cmd/helm/plugin_install.go @@ -19,6 +19,7 @@ import ( "fmt" "io" + "github.com/pkg/errors" "github.com/spf13/cobra" "helm.sh/helm/v3/cmd/helm/require" @@ -81,7 +82,7 @@ func (o *pluginInstallOptions) run(out io.Writer) error { debug("loading plugin from %s", i.Path()) p, err := plugin.LoadDir(i.Path()) if err != nil { - return err + return errors.Wrap(err, "plugin is installed but unusable") } if err := runHook(p, plugin.Install); err != nil { diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go index caa34fbd3..9bac2244c 100644 --- a/pkg/plugin/plugin.go +++ b/pkg/plugin/plugin.go @@ -20,9 +20,11 @@ import ( "io/ioutil" "os" "path/filepath" + "regexp" "runtime" "strings" + "github.com/pkg/errors" "sigs.k8s.io/yaml" "helm.sh/helm/v3/pkg/cli" @@ -157,18 +159,51 @@ func (p *Plugin) PrepareCommand(extraArgs []string) (string, []string, error) { return main, baseArgs, nil } +// validPluginName is a regular expression that validates plugin names. +// +// Plugin names can only contain the ASCII characters a-z, A-Z, 0-9, ​_​ and ​-. +var validPluginName = regexp.MustCompile("^[A-Za-z0-9_-]+$") + +// validatePluginData validates a plugin's YAML data. +func validatePluginData(plug *Plugin, filepath string) error { + if !validPluginName.MatchString(plug.Metadata.Name) { + return fmt.Errorf("invalid plugin name at %q", filepath) + } + // We could also validate SemVer, executable, and other fields should we so choose. + return nil +} + +func detectDuplicates(plugs []*Plugin) error { + names := map[string]string{} + + for _, plug := range plugs { + if oldpath, ok := names[plug.Metadata.Name]; ok { + return fmt.Errorf( + "two plugins claim the name %q at %q and %q", + plug.Metadata.Name, + oldpath, + plug.Dir, + ) + } + names[plug.Metadata.Name] = plug.Dir + } + + return nil +} + // LoadDir loads a plugin from the given directory. func LoadDir(dirname string) (*Plugin, error) { - data, err := ioutil.ReadFile(filepath.Join(dirname, PluginFileName)) + pluginfile := filepath.Join(dirname, PluginFileName) + data, err := ioutil.ReadFile(pluginfile) if err != nil { - return nil, err + return nil, errors.Wrapf(err, "failed to read plugin at %q", pluginfile) } plug := &Plugin{Dir: dirname} if err := yaml.Unmarshal(data, &plug.Metadata); err != nil { - return nil, err + return nil, errors.Wrapf(err, "failed to load plugin at %q", pluginfile) } - return plug, nil + return plug, validatePluginData(plug, pluginfile) } // LoadAll loads all plugins found beneath the base directory. @@ -180,7 +215,7 @@ func LoadAll(basedir string) ([]*Plugin, error) { scanpath := filepath.Join(basedir, "*", PluginFileName) matches, err := filepath.Glob(scanpath) if err != nil { - return plugins, err + return plugins, errors.Wrapf(err, "failed to find plugins in %q", scanpath) } if matches == nil { @@ -195,7 +230,7 @@ func LoadAll(basedir string) ([]*Plugin, error) { } plugins = append(plugins, p) } - return plugins, nil + return plugins, detectDuplicates(plugins) } // FindPlugins returns a list of YAML files that describe plugins. diff --git a/pkg/plugin/plugin_test.go b/pkg/plugin/plugin_test.go index af0b61846..88add037d 100644 --- a/pkg/plugin/plugin_test.go +++ b/pkg/plugin/plugin_test.go @@ -16,6 +16,7 @@ limitations under the License. package plugin // import "helm.sh/helm/v3/pkg/plugin" import ( + "fmt" "os" "path/filepath" "reflect" @@ -320,3 +321,51 @@ func TestSetupEnv(t *testing.T) { } } } + +func TestValidatePluginData(t *testing.T) { + for i, item := range []struct { + pass bool + plug *Plugin + }{ + {true, mockPlugin("abcdefghijklmnopqrstuvwxyz0123456789_-ABC")}, + {true, mockPlugin("foo-bar-FOO-BAR_1234")}, + {false, mockPlugin("foo -bar")}, + {false, mockPlugin("$foo -bar")}, // Test leading chars + {false, mockPlugin("foo -bar ")}, // Test trailing chars + {false, mockPlugin("foo\nbar")}, // Test newline + } { + err := validatePluginData(item.plug, fmt.Sprintf("test-%d", i)) + if item.pass && err != nil { + t.Errorf("failed to validate case %d: %s", i, err) + } else if !item.pass && err == nil { + t.Errorf("expected case %d to fail", i) + } + } +} + +func TestDetectDuplicates(t *testing.T) { + plugs := []*Plugin{ + mockPlugin("foo"), + mockPlugin("bar"), + } + if err := detectDuplicates(plugs); err != nil { + t.Error("no duplicates in the first set") + } + plugs = append(plugs, mockPlugin("foo")) + if err := detectDuplicates(plugs); err == nil { + t.Error("duplicates in the second set") + } +} + +func mockPlugin(name string) *Plugin { + return &Plugin{ + Metadata: &Metadata{ + Name: name, + Version: "v0.1.2", + Usage: "Mock plugin", + Description: "Mock plugin for testing", + Command: "echo mock plugin", + }, + Dir: "no-such-dir", + } +} From 6eeec4a00241b7da1acaddcbf3278355de1f216e Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Thu, 17 Sep 2020 08:33:11 -0700 Subject: [PATCH 29/45] switched to stricter YAML parsing on plugin metadata files Signed-off-by: Matthew Fisher --- pkg/plugin/installer/local_installer_test.go | 2 +- pkg/plugin/installer/vcs_installer_test.go | 2 +- pkg/plugin/plugin.go | 8 +++++++- pkg/plugin/plugin_test.go | 15 +++++++++++---- .../plugdir/bad/duplicate-entries/plugin.yaml | 11 +++++++++++ .../plugdir/{ => good}/downloader/plugin.yaml | 0 .../testdata/plugdir/{ => good}/echo/plugin.yaml | 0 .../testdata/plugdir/{ => good}/hello/hello.sh | 0 .../testdata/plugdir/{ => good}/hello/plugin.yaml | 1 - 9 files changed, 31 insertions(+), 8 deletions(-) create mode 100644 pkg/plugin/testdata/plugdir/bad/duplicate-entries/plugin.yaml rename pkg/plugin/testdata/plugdir/{ => good}/downloader/plugin.yaml (100%) rename pkg/plugin/testdata/plugdir/{ => good}/echo/plugin.yaml (100%) rename pkg/plugin/testdata/plugdir/{ => good}/hello/hello.sh (100%) rename pkg/plugin/testdata/plugdir/{ => good}/hello/plugin.yaml (85%) diff --git a/pkg/plugin/installer/local_installer_test.go b/pkg/plugin/installer/local_installer_test.go index 3d9607331..96958ab09 100644 --- a/pkg/plugin/installer/local_installer_test.go +++ b/pkg/plugin/installer/local_installer_test.go @@ -37,7 +37,7 @@ func TestLocalInstaller(t *testing.T) { t.Fatal(err) } - source := "../testdata/plugdir/echo" + source := "../testdata/plugdir/good/echo" i, err := NewForSource(source, "") if err != nil { t.Fatalf("unexpected error: %s", err) diff --git a/pkg/plugin/installer/vcs_installer_test.go b/pkg/plugin/installer/vcs_installer_test.go index b8dc6b1e2..6785264b3 100644 --- a/pkg/plugin/installer/vcs_installer_test.go +++ b/pkg/plugin/installer/vcs_installer_test.go @@ -56,7 +56,7 @@ func TestVCSInstaller(t *testing.T) { } source := "https://github.com/adamreese/helm-env" - testRepoPath, _ := filepath.Abs("../testdata/plugdir/echo") + testRepoPath, _ := filepath.Abs("../testdata/plugdir/good/echo") repo := &testRepo{ local: testRepoPath, tags: []string{"0.1.0", "0.1.1"}, diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go index 9bac2244c..93b5527a1 100644 --- a/pkg/plugin/plugin.go +++ b/pkg/plugin/plugin.go @@ -96,6 +96,12 @@ type Metadata struct { // Downloaders field is used if the plugin supply downloader mechanism // for special protocols. Downloaders []Downloaders `json:"downloaders"` + + // UseTunnelDeprecated indicates that this command needs a tunnel. + // Setting this will cause a number of side effects, such as the + // automatic setting of HELM_HOST. + // DEPRECATED and unused, but retained for backwards compatibility with Helm 2 plugins. Remove in Helm 4 + UseTunnelDeprecated bool `json:"useTunnel,omitempty"` } // Plugin represents a plugin. @@ -200,7 +206,7 @@ func LoadDir(dirname string) (*Plugin, error) { } plug := &Plugin{Dir: dirname} - if err := yaml.Unmarshal(data, &plug.Metadata); err != nil { + if err := yaml.UnmarshalStrict(data, &plug.Metadata); err != nil { return nil, errors.Wrapf(err, "failed to load plugin at %q", pluginfile) } return plug, validatePluginData(plug, pluginfile) diff --git a/pkg/plugin/plugin_test.go b/pkg/plugin/plugin_test.go index 88add037d..2c4478953 100644 --- a/pkg/plugin/plugin_test.go +++ b/pkg/plugin/plugin_test.go @@ -178,7 +178,7 @@ func TestNoMatchPrepareCommand(t *testing.T) { } func TestLoadDir(t *testing.T) { - dirname := "testdata/plugdir/hello" + dirname := "testdata/plugdir/good/hello" plug, err := LoadDir(dirname) if err != nil { t.Fatalf("error loading Hello plugin: %s", err) @@ -205,8 +205,15 @@ func TestLoadDir(t *testing.T) { } } +func TestLoadDirDuplicateEntries(t *testing.T) { + dirname := "testdata/plugdir/bad/duplicate-entries" + if _, err := LoadDir(dirname); err == nil { + t.Errorf("successfully loaded plugin with duplicate entries when it should've failed") + } +} + func TestDownloader(t *testing.T) { - dirname := "testdata/plugdir/downloader" + dirname := "testdata/plugdir/good/downloader" plug, err := LoadDir(dirname) if err != nil { t.Fatalf("error loading Hello plugin: %s", err) @@ -244,7 +251,7 @@ func TestLoadAll(t *testing.T) { t.Fatalf("expected empty dir to have 0 plugins") } - basedir := "testdata/plugdir" + basedir := "testdata/plugdir/good" plugs, err := LoadAll(basedir) if err != nil { t.Fatalf("Could not load %q: %s", basedir, err) @@ -288,7 +295,7 @@ func TestFindPlugins(t *testing.T) { }, { name: "normal", - plugdirs: "./testdata/plugdir", + plugdirs: "./testdata/plugdir/good", expected: 3, }, } diff --git a/pkg/plugin/testdata/plugdir/bad/duplicate-entries/plugin.yaml b/pkg/plugin/testdata/plugdir/bad/duplicate-entries/plugin.yaml new file mode 100644 index 000000000..66498be96 --- /dev/null +++ b/pkg/plugin/testdata/plugdir/bad/duplicate-entries/plugin.yaml @@ -0,0 +1,11 @@ +name: "duplicate-entries" +version: "0.1.0" +usage: "usage" +description: |- + description +command: "echo hello" +ignoreFlags: true +hooks: + install: "echo installing..." +hooks: + install: "echo installing something different" diff --git a/pkg/plugin/testdata/plugdir/downloader/plugin.yaml b/pkg/plugin/testdata/plugdir/good/downloader/plugin.yaml similarity index 100% rename from pkg/plugin/testdata/plugdir/downloader/plugin.yaml rename to pkg/plugin/testdata/plugdir/good/downloader/plugin.yaml diff --git a/pkg/plugin/testdata/plugdir/echo/plugin.yaml b/pkg/plugin/testdata/plugdir/good/echo/plugin.yaml similarity index 100% rename from pkg/plugin/testdata/plugdir/echo/plugin.yaml rename to pkg/plugin/testdata/plugdir/good/echo/plugin.yaml diff --git a/pkg/plugin/testdata/plugdir/hello/hello.sh b/pkg/plugin/testdata/plugdir/good/hello/hello.sh similarity index 100% rename from pkg/plugin/testdata/plugdir/hello/hello.sh rename to pkg/plugin/testdata/plugdir/good/hello/hello.sh diff --git a/pkg/plugin/testdata/plugdir/hello/plugin.yaml b/pkg/plugin/testdata/plugdir/good/hello/plugin.yaml similarity index 85% rename from pkg/plugin/testdata/plugdir/hello/plugin.yaml rename to pkg/plugin/testdata/plugdir/good/hello/plugin.yaml index 6a78756d3..2b972da59 100644 --- a/pkg/plugin/testdata/plugdir/hello/plugin.yaml +++ b/pkg/plugin/testdata/plugdir/good/hello/plugin.yaml @@ -5,6 +5,5 @@ description: |- description command: "$HELM_PLUGIN_SELF/hello.sh" ignoreFlags: true -install: "echo installing..." hooks: install: "echo installing..." From 45d230fcc95c1c4d2e055b7451a988441f038509 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Thu, 17 Sep 2020 11:46:22 -0700 Subject: [PATCH 30/45] fix(cmd/helm): add build tags for architecture Signed-off-by: Adam Reese --- cmd/helm/root_unix.go | 2 ++ cmd/helm/root_unix_test.go | 2 ++ 2 files changed, 4 insertions(+) diff --git a/cmd/helm/root_unix.go b/cmd/helm/root_unix.go index 210842b35..4eb0b442b 100644 --- a/cmd/helm/root_unix.go +++ b/cmd/helm/root_unix.go @@ -1,3 +1,5 @@ +// +build !windows + /* Copyright The Helm Authors. diff --git a/cmd/helm/root_unix_test.go b/cmd/helm/root_unix_test.go index 73f18ec28..b1fcfbc66 100644 --- a/cmd/helm/root_unix_test.go +++ b/cmd/helm/root_unix_test.go @@ -1,3 +1,5 @@ +// +build !windows + /* Copyright The Helm Authors. From f19acbdc94578194d19a6758f01cd8eed85b792e Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Thu, 17 Sep 2020 14:54:55 -0700 Subject: [PATCH 31/45] fix: allow serverInfo field on index files A recent change merged into Helm fixes a number of security issues related to parsing malformed index files. Unfortunately, it also broke the ability for users to load index files from chartmuseum, which adds a "server info" field to add additional metadata. This commit adds that field so that index files from chartmuseum can be validated. Since Helm does not use this field for anything, the information is discarded and unused. Signed-off-by: Matthew Fisher --- pkg/repo/index.go | 2 + pkg/repo/index_test.go | 85 +++++++++++++++--------- pkg/repo/testdata/chartmuseum-index.yaml | 50 ++++++++++++++ 3 files changed, 105 insertions(+), 32 deletions(-) create mode 100644 pkg/repo/testdata/chartmuseum-index.yaml diff --git a/pkg/repo/index.go b/pkg/repo/index.go index 8b831029f..55b984eea 100644 --- a/pkg/repo/index.go +++ b/pkg/repo/index.go @@ -77,6 +77,8 @@ func (c ChartVersions) Less(a, b int) bool { // IndexFile represents the index file in a chart repository type IndexFile struct { + // This is used ONLY for validation against chartmuseum's index files and is discarded after validation. + ServerInfo map[string]interface{} `json:"serverInfo,omitempty"` APIVersion string `json:"apiVersion"` Generated time.Time `json:"generated"` Entries map[string]ChartVersions `json:"entries"` diff --git a/pkg/repo/index_test.go b/pkg/repo/index_test.go index 77b3a90ab..c22588971 100644 --- a/pkg/repo/index_test.go +++ b/pkg/repo/index_test.go @@ -35,9 +35,31 @@ import ( ) const ( - testfile = "testdata/local-index.yaml" - unorderedTestfile = "testdata/local-index-unordered.yaml" - testRepo = "test-repo" + testfile = "testdata/local-index.yaml" + chartmuseumtestfile = "testdata/chartmuseum-index.yaml" + unorderedTestfile = "testdata/local-index-unordered.yaml" + testRepo = "test-repo" + indexWithDuplicates = ` +apiVersion: v1 +entries: + nginx: + - urls: + - https://kubernetes-charts.storage.googleapis.com/nginx-0.2.0.tgz + name: nginx + description: string + version: 0.2.0 + home: https://github.com/something/else + digest: "sha256:1234567890abcdef" + nginx: + - urls: + - https://kubernetes-charts.storage.googleapis.com/alpine-1.0.0.tgz + - http://storage2.googleapis.com/kubernetes-charts/alpine-1.0.0.tgz + name: alpine + description: string + version: 1.0.0 + home: https://github.com/something + digest: "sha256:1234567890abcdef" +` ) func TestIndexFile(t *testing.T) { @@ -84,39 +106,38 @@ func TestIndexFile(t *testing.T) { } func TestLoadIndex(t *testing.T) { - b, err := ioutil.ReadFile(testfile) - if err != nil { - t.Fatal(err) + + tests := []struct { + Name string + Filename string + }{ + { + Name: "regular index file", + Filename: testfile, + }, + { + Name: "chartmuseum index file", + Filename: chartmuseumtestfile, + }, } - i, err := loadIndex(b) - if err != nil { - t.Fatal(err) + + for _, tc := range tests { + tc := tc + t.Run(tc.Name, func(t *testing.T) { + t.Parallel() + b, err := ioutil.ReadFile(tc.Filename) + if err != nil { + t.Fatal(err) + } + i, err := loadIndex(b) + if err != nil { + t.Fatal(err) + } + verifyLocalIndex(t, i) + }) } - verifyLocalIndex(t, i) } -const indexWithDuplicates = ` -apiVersion: v1 -entries: - nginx: - - urls: - - https://kubernetes-charts.storage.googleapis.com/nginx-0.2.0.tgz - name: nginx - description: string - version: 0.2.0 - home: https://github.com/something/else - digest: "sha256:1234567890abcdef" - nginx: - - urls: - - https://kubernetes-charts.storage.googleapis.com/alpine-1.0.0.tgz - - http://storage2.googleapis.com/kubernetes-charts/alpine-1.0.0.tgz - name: alpine - description: string - version: 1.0.0 - home: https://github.com/something - digest: "sha256:1234567890abcdef" -` - // TestLoadIndex_Duplicates is a regression to make sure that we don't non-deterministically allow duplicate packages. func TestLoadIndex_Duplicates(t *testing.T) { if _, err := loadIndex([]byte(indexWithDuplicates)); err == nil { diff --git a/pkg/repo/testdata/chartmuseum-index.yaml b/pkg/repo/testdata/chartmuseum-index.yaml new file mode 100644 index 000000000..3077596f4 --- /dev/null +++ b/pkg/repo/testdata/chartmuseum-index.yaml @@ -0,0 +1,50 @@ +serverInfo: + contextPath: /v1/helm +apiVersion: v1 +entries: + nginx: + - urls: + - https://kubernetes-charts.storage.googleapis.com/nginx-0.2.0.tgz + name: nginx + description: string + version: 0.2.0 + home: https://github.com/something/else + digest: "sha256:1234567890abcdef" + keywords: + - popular + - web server + - proxy + - urls: + - https://kubernetes-charts.storage.googleapis.com/nginx-0.1.0.tgz + name: nginx + description: string + version: 0.1.0 + home: https://github.com/something + digest: "sha256:1234567890abcdef" + keywords: + - popular + - web server + - proxy + alpine: + - urls: + - https://kubernetes-charts.storage.googleapis.com/alpine-1.0.0.tgz + - http://storage2.googleapis.com/kubernetes-charts/alpine-1.0.0.tgz + name: alpine + description: string + version: 1.0.0 + home: https://github.com/something + keywords: + - linux + - alpine + - small + - sumtin + digest: "sha256:1234567890abcdef" + chartWithNoURL: + - name: chartWithNoURL + description: string + version: 1.0.0 + home: https://github.com/something + keywords: + - small + - sumtin + digest: "sha256:1234567890abcdef" From 3eeeb0345d1ef7e8990efc761a7d10ddf6e67f52 Mon Sep 17 00:00:00 2001 From: Scott Rigby Date: Thu, 17 Sep 2020 21:29:44 -0400 Subject: [PATCH 32/45] Update docs links in release notes script Signed-off-by: Scott Rigby --- scripts/release-notes.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/release-notes.sh b/scripts/release-notes.sh index 3625aaa9a..b024a958d 100755 --- a/scripts/release-notes.sh +++ b/scripts/release-notes.sh @@ -90,7 +90,7 @@ Download Helm ${RELEASE}. The common platform binaries are here: - [Linux s390x](https://get.helm.sh/helm-${RELEASE}-linux-s390x.tar.gz) ([checksum](https://get.helm.sh/helm-${RELEASE}-linux-s390x.tar.gz.sha256sum) / $(cat _dist/helm-${RELEASE}-darwin-amd64.tar.gz.sha256)) - [Windows amd64](https://get.helm.sh/helm-${RELEASE}-windows-amd64.zip) ([checksum](https://get.helm.sh/helm-${RELEASE}-windows-amd64.zip.sha256sum) / $(cat _dist/helm-${RELEASE}-windows-amd64.zip.sha256)) -The [Quickstart Guide](https://docs.helm.sh/using_helm/#quickstart-guide) will get you going from there. For **upgrade instructions** or detailed installation notes, check the [install guide](https://docs.helm.sh/using_helm/#installing-helm). You can also use a [script to install](https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3) on any system with \`bash\`. +The [Quickstart Guide](https://helm.sh/docs/intro/quickstart/) will get you going from there. For **upgrade instructions** or detailed installation notes, check the [install guide](https://helm.sh/docs/intro/install/). You can also use a [script to install](https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3) on any system with \`bash\`. ## What's Next From 1138def202c95c2e76d0bd9d27bc36aa35224326 Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Thu, 17 Sep 2020 10:49:40 -0700 Subject: [PATCH 33/45] size/S and larger requiring 2 LGTMs Signed-off-by: Matthew Fisher --- CONTRIBUTING.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a8028dd01..ac88d13f2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -232,8 +232,9 @@ Like any good open source project, we use Pull Requests (PRs) to track code chan 3. Assigning reviews - Once a review has the `awaiting review` label, maintainers will review them as schedule permits. The maintainer who takes the issue should self-request a review. - - Any PR with the `size/large` label requires 2 review approvals from maintainers before it can - be merged. Those with `size/medium` or `size/small` are per the judgement of the maintainers. + - PRs from a community member with the label `size/S` or larger requires 2 review approvals from + maintainers before it can be merged. Those with `size/XS` are per the judgement of the + maintainers. 4. Reviewing/Discussion - All reviews will be completed using Github review tool. - A "Comment" review should be used when there are questions about the code that should be @@ -313,15 +314,16 @@ makes 30 lines of changes in 1 file, but it changes key functionality, it will l feature, but requires another 150 lines of tests to cover all cases, could be labeled as `size/S` even though the number of lines is greater than defined below. -PRs submitted by a core maintainer, regardless of size, only requires approval from one additional -maintainer. This ensures there are at least two maintainers who are aware of any significant PRs -introduced to the codebase. +Any changes from the community labeled as `size/S` or larger should be thoroughly tested before +merging and always requires approval from 2 core maintainers. PRs submitted by a core maintainer, +regardless of size, only requires approval from one additional maintainer. This ensures there are at +least two maintainers who are aware of any significant PRs introduced to the codebase. | Label | Description | | ----- | ----------- | | `size/XS` | Denotes a PR that changes 0-9 lines, ignoring generated files. Very little testing may be required depending on the change. | | `size/S` | Denotes a PR that changes 10-29 lines, ignoring generated files. Only small amounts of manual testing may be required. | | `size/M` | Denotes a PR that changes 30-99 lines, ignoring generated files. Manual validation should be required. | -| `size/L` | Denotes a PR that changes 100-499 lines, ignoring generated files. This should be thoroughly tested before merging and always requires 2 approvals. | -| `size/XL` | Denotes a PR that changes 500-999 lines, ignoring generated files. This should be thoroughly tested before merging and always requires 2 approvals. | -| `size/XXL` | Denotes a PR that changes 1000+ lines, ignoring generated files. This should be thoroughly tested before merging and always requires 2 approvals. | +| `size/L` | Denotes a PR that changes 100-499 lines, ignoring generated files. | +| `size/XL` | Denotes a PR that changes 500-999 lines, ignoring generated files. | +| `size/XXL` | Denotes a PR that changes 1000+ lines, ignoring generated files. | From 467bd49bb0cb0d613e802c738a0e38225eec054a Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sat, 19 Sep 2020 00:23:40 +0200 Subject: [PATCH 34/45] support passing signing passphrase from file or stdin (#8394) Signed-off-by: Sebastian Sdorra --- cmd/helm/package.go | 1 + pkg/action/package.go | 43 +++++++++++++++++++++++++++- pkg/action/package_test.go | 58 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 1 deletion(-) diff --git a/cmd/helm/package.go b/cmd/helm/package.go index 00fe0ef11..7134a8784 100644 --- a/cmd/helm/package.go +++ b/cmd/helm/package.go @@ -114,6 +114,7 @@ func newPackageCmd(out io.Writer) *cobra.Command { f.BoolVar(&client.Sign, "sign", false, "use a PGP private key to sign this package") f.StringVar(&client.Key, "key", "", "name of the key to use when signing. Used if --sign is true") f.StringVar(&client.Keyring, "keyring", defaultKeyring(), "location of a public keyring") + f.StringVar(&client.PassphraseFile, "passphrase-file", "", `location of a file which contains the passphrase for the signing key. Use "-" in order to read from stdin.`) f.StringVar(&client.Version, "version", "", "set the version on the chart to this semver version") f.StringVar(&client.AppVersion, "app-version", "", "set the appVersion on the chart to this version") f.StringVarP(&client.Destination, "destination", "d", ".", "location to write the chart.") diff --git a/pkg/action/package.go b/pkg/action/package.go index 0a927cd41..8f53bcac4 100644 --- a/pkg/action/package.go +++ b/pkg/action/package.go @@ -17,6 +17,7 @@ limitations under the License. package action import ( + "bufio" "fmt" "io/ioutil" "os" @@ -39,6 +40,7 @@ type Package struct { Sign bool Key string Keyring string + PassphraseFile string Version string AppVersion string Destination string @@ -120,7 +122,15 @@ func (p *Package) Clearsign(filename string) error { return err } - if err := signer.DecryptKey(promptUser); err != nil { + passphraseFetcher := promptUser + if p.PassphraseFile != "" { + passphraseFetcher, err = passphraseFileFetcher(p.PassphraseFile, os.Stdin) + if err != nil { + return err + } + } + + if err := signer.DecryptKey(passphraseFetcher); err != nil { return err } @@ -141,3 +151,34 @@ func promptUser(name string) ([]byte, error) { fmt.Println() return pw, err } + +func passphraseFileFetcher(passphraseFile string, stdin *os.File) (provenance.PassphraseFetcher, error) { + file, err := openPassphraseFile(passphraseFile, stdin) + if err != nil { + return nil, err + } + defer file.Close() + + reader := bufio.NewReader(file) + passphrase, _, err := reader.ReadLine() + if err != nil { + return nil, err + } + return func(name string) ([]byte, error) { + return passphrase, nil + }, nil +} + +func openPassphraseFile(passphraseFile string, stdin *os.File) (*os.File, error) { + if passphraseFile == "-" { + stat, err := stdin.Stat() + if err != nil { + return nil, err + } + if (stat.Mode() & os.ModeNamedPipe) == 0 { + return nil, errors.New("specified reading passphrase from stdin, without input on stdin") + } + return stdin, nil + } + return os.Open(passphraseFile) +} diff --git a/pkg/action/package_test.go b/pkg/action/package_test.go index 0f716118d..9a202cde4 100644 --- a/pkg/action/package_test.go +++ b/pkg/action/package_test.go @@ -17,8 +17,12 @@ limitations under the License. package action import ( + "io/ioutil" + "os" + "path" "testing" + "helm.sh/helm/v3/internal/test/ensure" "helm.sh/helm/v3/pkg/chart" ) @@ -42,3 +46,57 @@ func TestSetVersion(t *testing.T) { t.Error("Expected bogus version to return an error.") } } + +func TestPassphraseFileFetcher(t *testing.T) { + secret := "secret" + directory := ensure.TempFile(t, "passphrase-file", []byte(secret)) + defer os.RemoveAll(directory) + + fetcher, err := passphraseFileFetcher(path.Join(directory, "passphrase-file"), nil) + if err != nil { + t.Fatal("Unable to create passphraseFileFetcher", err) + } + + passphrase, err := fetcher("key") + if err != nil { + t.Fatal("Unable to fetch passphrase") + } + + if string(passphrase) != secret { + t.Errorf("Expected %s got %s", secret, string(passphrase)) + } +} + +func TestPassphraseFileFetcher_WithLineBreak(t *testing.T) { + secret := "secret" + directory := ensure.TempFile(t, "passphrase-file", []byte(secret+"\n\n.")) + defer os.RemoveAll(directory) + + fetcher, err := passphraseFileFetcher(path.Join(directory, "passphrase-file"), nil) + if err != nil { + t.Fatal("Unable to create passphraseFileFetcher", err) + } + + passphrase, err := fetcher("key") + if err != nil { + t.Fatal("Unable to fetch passphrase") + } + + if string(passphrase) != secret { + t.Errorf("Expected %s got %s", secret, string(passphrase)) + } +} + +func TestPassphraseFileFetcher_WithInvalidStdin(t *testing.T) { + directory := ensure.TempDir(t) + defer os.RemoveAll(directory) + + stdin, err := ioutil.TempFile(directory, "non-existing") + if err != nil { + t.Fatal("Unable to create test file", err) + } + + if _, err := passphraseFileFetcher("-", stdin); err == nil { + t.Error("Expected passphraseFileFetcher returning an error") + } +} From baf5b76a957dc52a2fca84fa1328628cc78cc307 Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Mon, 21 Sep 2020 10:42:47 -0400 Subject: [PATCH 35/45] Fixing issue with idempotent repo add A security issue fixed in 3.3.2 caught repos with the same name being added a second time and produced an error. This caused an issue for tools, such as helmfile, that will add the same name with the same configuration multiple times. This fix checks that the configuration on the existing and new repo are the same. If there is no change it notes it and exists with a 0 exit code. If there is a change the existing error is returned (for reverse compat). If --force-update is given the user opts in to changing the config for the name. Closes #8771 Signed-off-by: Matt Farina --- cmd/helm/repo_add.go | 22 +++++++++++++---- cmd/helm/repo_add_test.go | 34 ++++++++++++++++++++++---- cmd/helm/testdata/output/repo-add2.txt | 1 + 3 files changed, 47 insertions(+), 10 deletions(-) create mode 100644 cmd/helm/testdata/output/repo-add2.txt diff --git a/cmd/helm/repo_add.go b/cmd/helm/repo_add.go index 1c2162bfa..f79c213c0 100644 --- a/cmd/helm/repo_add.go +++ b/cmd/helm/repo_add.go @@ -116,11 +116,6 @@ func (o *repoAddOptions) run(out io.Writer) error { return err } - // If the repo exists and --force-update was not specified, error out. - if !o.forceUpdate && f.Has(o.name) { - return errors.Errorf("repository name (%s) already exists, please specify a different name", o.name) - } - if o.username != "" && o.password == "" { fd := int(os.Stdin.Fd()) fmt.Fprint(out, "Password: ") @@ -143,6 +138,23 @@ func (o *repoAddOptions) run(out io.Writer) error { InsecureSkipTLSverify: o.insecureSkipTLSverify, } + // If the repo exists do one of two things: + // 1. If the configuration for the name is the same continue without error + // 2. When the config is different require --force-update + if !o.forceUpdate && f.Has(o.name) { + existing := f.Get(o.name) + if c != *existing { + + // The input coming in for the name is different from what is already + // configured. Return an error. + return errors.Errorf("repository name (%s) already exists, please specify a different name", o.name) + } + + // The add is idempotent so do nothing + fmt.Fprintf(out, "%q already exists with the same configuration, skipping\n", o.name) + return nil + } + r, err := repo.NewChartRepository(&c, getter.All(settings)) if err != nil { return err diff --git a/cmd/helm/repo_add_test.go b/cmd/helm/repo_add_test.go index d358ad970..f3bc54985 100644 --- a/cmd/helm/repo_add_test.go +++ b/cmd/helm/repo_add_test.go @@ -40,14 +40,38 @@ func TestRepoAddCmd(t *testing.T) { } defer srv.Stop() + // A second test server is setup to verify URL changing + srv2, err := repotest.NewTempServerWithCleanup(t, "testdata/testserver/*.*") + if err != nil { + t.Fatal(err) + } + defer srv2.Stop() + tmpdir := ensure.TempDir(t) repoFile := filepath.Join(tmpdir, "repositories.yaml") - tests := []cmdTestCase{{ - name: "add a repository", - cmd: fmt.Sprintf("repo add test-name %s --repository-config %s --repository-cache %s", srv.URL(), repoFile, tmpdir), - golden: "output/repo-add.txt", - }} + tests := []cmdTestCase{ + { + name: "add a repository", + cmd: fmt.Sprintf("repo add test-name %s --repository-config %s --repository-cache %s", srv.URL(), repoFile, tmpdir), + golden: "output/repo-add.txt", + }, + { + name: "add repository second time", + cmd: fmt.Sprintf("repo add test-name %s --repository-config %s --repository-cache %s", srv.URL(), repoFile, tmpdir), + golden: "output/repo-add2.txt", + }, + { + name: "add repository different url", + cmd: fmt.Sprintf("repo add test-name %s --repository-config %s --repository-cache %s", srv2.URL(), repoFile, tmpdir), + wantError: true, + }, + { + name: "add repository second time", + cmd: fmt.Sprintf("repo add test-name %s --repository-config %s --repository-cache %s --force-update", srv2.URL(), repoFile, tmpdir), + golden: "output/repo-add.txt", + }, + } runTestCmd(t, tests) } diff --git a/cmd/helm/testdata/output/repo-add2.txt b/cmd/helm/testdata/output/repo-add2.txt new file mode 100644 index 000000000..263ffa9e4 --- /dev/null +++ b/cmd/helm/testdata/output/repo-add2.txt @@ -0,0 +1 @@ +"test-name" already exists with the same configuration, skipping From 8b546e90a96aaa541ccf818f515e418a18d77fbc Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Mon, 21 Sep 2020 15:05:24 -0400 Subject: [PATCH 36/45] Adding size labels pointer Add size of labels and number of reviewers is listed twice, pointing the area with less detail to the one with more detail. Signed-off-by: Matt Farina --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ac88d13f2..308154af5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -234,7 +234,7 @@ Like any good open source project, we use Pull Requests (PRs) to track code chan permits. The maintainer who takes the issue should self-request a review. - PRs from a community member with the label `size/S` or larger requires 2 review approvals from maintainers before it can be merged. Those with `size/XS` are per the judgement of the - maintainers. + maintainers. For more detail see the [Size Labels](#size-labels) section. 4. Reviewing/Discussion - All reviews will be completed using Github review tool. - A "Comment" review should be used when there are questions about the code that should be From 92c4bda184cda8323ad961d889a8186b2baa98bd Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Mon, 21 Sep 2020 08:31:08 -0700 Subject: [PATCH 37/45] use warning function This ensures warning messages are displayed on stderr rather than stdout. Signed-off-by: Matthew Fisher --- cmd/helm/registry_login.go | 2 +- cmd/helm/root.go | 2 +- cmd/helm/root_unix.go | 8 +++----- cmd/helm/root_unix_test.go | 33 +++++++++++++++++++++++---------- cmd/helm/root_windows.go | 2 +- cmd/helm/search_repo.go | 3 +-- 6 files changed, 30 insertions(+), 20 deletions(-) diff --git a/cmd/helm/registry_login.go b/cmd/helm/registry_login.go index e3435bf9d..43228f90a 100644 --- a/cmd/helm/registry_login.go +++ b/cmd/helm/registry_login.go @@ -104,7 +104,7 @@ func getUsernamePassword(usernameOpt string, passwordOpt string, passwordFromStd } } } else { - fmt.Fprintln(os.Stderr, "WARNING! Using --password via the CLI is insecure. Use --password-stdin.") + warning("Using --password via the CLI is insecure. Use --password-stdin.") } return username, password, nil diff --git a/cmd/helm/root.go b/cmd/helm/root.go index 91542bb7e..0a2b1be8f 100644 --- a/cmd/helm/root.go +++ b/cmd/helm/root.go @@ -205,7 +205,7 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string loadPlugins(cmd, out) // Check permissions on critical files - checkPerms(out) + checkPerms() return cmd, nil } diff --git a/cmd/helm/root_unix.go b/cmd/helm/root_unix.go index 4eb0b442b..b1b0f3896 100644 --- a/cmd/helm/root_unix.go +++ b/cmd/helm/root_unix.go @@ -19,14 +19,12 @@ limitations under the License. package main import ( - "fmt" - "io" "os" "os/user" "path/filepath" ) -func checkPerms(out io.Writer) { +func checkPerms() { // This function MUST NOT FAIL, as it is just a check for a common permissions problem. // If for some reason the function hits a stopping condition, it may panic. But only if // we can be sure that it is panicing because Helm cannot proceed. @@ -52,9 +50,9 @@ func checkPerms(out io.Writer) { perm := fi.Mode().Perm() if perm&0040 > 0 { - fmt.Fprintf(out, "WARNING: Kubernetes configuration file is group-readable. This is insecure. Location: %s\n", kc) + warning("Kubernetes configuration file is group-readable. This is insecure. Location: %s", kc) } if perm&0004 > 0 { - fmt.Fprintf(out, "WARNING: Kubernetes configuration file is world-readable. This is insecure. Location: %s\n", kc) + warning("Kubernetes configuration file is world-readable. This is insecure. Location: %s", kc) } } diff --git a/cmd/helm/root_unix_test.go b/cmd/helm/root_unix_test.go index b1fcfbc66..89c3c1eea 100644 --- a/cmd/helm/root_unix_test.go +++ b/cmd/helm/root_unix_test.go @@ -19,7 +19,7 @@ limitations under the License. package main import ( - "bytes" + "bufio" "io/ioutil" "os" "path/filepath" @@ -28,6 +28,14 @@ import ( ) func TestCheckPerms(t *testing.T) { + // NOTE(bacongobbler): have to open a new file handler here as the default os.Sterr cannot be read from + stderr, err := os.Open("/dev/stderr") + if err != nil { + t.Fatalf("could not open /dev/stderr for reading: %s", err) + } + defer stderr.Close() + reader := bufio.NewReader(stderr) + tdir, err := ioutil.TempDir("", "helmtest") if err != nil { t.Fatal(err) @@ -43,21 +51,26 @@ func TestCheckPerms(t *testing.T) { settings.KubeConfig = tfile defer func() { settings.KubeConfig = tconfig }() - var b bytes.Buffer - checkPerms(&b) + checkPerms() + text, err := reader.ReadString('\n') + if err != nil { + t.Fatalf("could not read from stderr: %s", err) + } expectPrefix := "WARNING: Kubernetes configuration file is group-readable. This is insecure. Location:" - if !strings.HasPrefix(b.String(), expectPrefix) { - t.Errorf("Expected to get a warning for group perms. Got %q", b.String()) + if !strings.HasPrefix(text, expectPrefix) { + t.Errorf("Expected to get a warning for group perms. Got %q", text) } if err := fh.Chmod(0404); err != nil { t.Errorf("Could not change mode on file: %s", err) } - b.Reset() - checkPerms(&b) + checkPerms() + text, err = reader.ReadString('\n') + if err != nil { + t.Fatalf("could not read from stderr: %s", err) + } expectPrefix = "WARNING: Kubernetes configuration file is world-readable. This is insecure. Location:" - if !strings.HasPrefix(b.String(), expectPrefix) { - t.Errorf("Expected to get a warning for world perms. Got %q", b.String()) + if !strings.HasPrefix(text, expectPrefix) { + t.Errorf("Expected to get a warning for world perms. Got %q", text) } - } diff --git a/cmd/helm/root_windows.go b/cmd/helm/root_windows.go index 243780d40..0b390f16c 100644 --- a/cmd/helm/root_windows.go +++ b/cmd/helm/root_windows.go @@ -18,7 +18,7 @@ package main import "io" -func checkPerms(out io.Writer) { +func checkPerms() { // Not yet implemented on Windows. If you know how to do a comprehensive perms // check on Windows, contributions welcomed! } diff --git a/cmd/helm/search_repo.go b/cmd/helm/search_repo.go index a7f27f179..bf82a6051 100644 --- a/cmd/helm/search_repo.go +++ b/cmd/helm/search_repo.go @@ -22,7 +22,6 @@ import ( "fmt" "io" "io/ioutil" - "os" "path/filepath" "strings" @@ -184,7 +183,7 @@ func (o *searchRepoOptions) buildIndex() (*search.Index, error) { f := filepath.Join(o.repoCacheDir, helmpath.CacheIndexFile(n)) ind, err := repo.LoadIndexFile(f) if err != nil { - fmt.Fprintf(os.Stderr, "WARNING: Repo %q is corrupt or missing. Try 'helm repo update'.", n) + warning("Repo %q is corrupt or missing. Try 'helm repo update'.", n) continue } From 3baaace868910d9b74f0d960e9441a2f3181fd00 Mon Sep 17 00:00:00 2001 From: lemonli Date: Tue, 22 Sep 2020 10:58:17 +0800 Subject: [PATCH 38/45] Update go version to 1.14 in go.mod Signed-off-by: lemonli --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 4b7c90f9d..0ebb377b0 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module helm.sh/helm/v3 -go 1.13 +go 1.14 require ( github.com/BurntSushi/toml v0.3.1 From 036832eba9dd604683d0803af96479bbfbe3b58f Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Tue, 22 Sep 2020 15:39:57 -0400 Subject: [PATCH 39/45] Fixing import package issue When #8779 was merged it introduced an issue with windows builds, which we do not test for in PR CI. This change fixes that problem. Signed-off-by: Matt Farina --- cmd/helm/root_windows.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/cmd/helm/root_windows.go b/cmd/helm/root_windows.go index 0b390f16c..7b5000f4f 100644 --- a/cmd/helm/root_windows.go +++ b/cmd/helm/root_windows.go @@ -16,8 +16,6 @@ limitations under the License. package main -import "io" - func checkPerms() { // Not yet implemented on Windows. If you know how to do a comprehensive perms // check on Windows, contributions welcomed! From b7c38c879a91cabec02699be1c4070206f84c88e Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Fri, 25 Sep 2020 12:15:06 -0400 Subject: [PATCH 40/45] Adding support for k8s 1.19 Closes #8806 Signed-off-by: Matt Farina --- go.mod | 15 ++-- go.sum | 171 +++++++++++++++++++++++++++++++++++++++++ pkg/action/validate.go | 2 +- pkg/kube/client.go | 4 +- 4 files changed, 182 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index 0ebb377b0..ed1ac1e7c 100644 --- a/go.mod +++ b/go.mod @@ -17,10 +17,11 @@ require ( github.com/docker/distribution v2.7.1+incompatible github.com/docker/docker v1.4.2-0.20200203170920-46ec8731fbce github.com/docker/go-units v0.4.0 - github.com/evanphx/json-patch v0.0.0-20200808040245-162e5629780b + github.com/evanphx/json-patch v4.9.0+incompatible github.com/gobwas/glob v0.2.3 github.com/gofrs/flock v0.7.1 github.com/gosuri/uitable v0.0.4 + github.com/jessevdk/go-flags v1.4.0 // indirect github.com/jmoiron/sqlx v1.2.0 github.com/lib/pq v1.7.0 github.com/mattn/go-shellwords v1.0.10 @@ -35,13 +36,13 @@ require ( github.com/stretchr/testify v1.6.1 github.com/xeipuuv/gojsonschema v1.2.0 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 - k8s.io/api v0.18.8 - k8s.io/apiextensions-apiserver v0.18.8 - k8s.io/apimachinery v0.18.8 - k8s.io/cli-runtime v0.18.8 - k8s.io/client-go v0.18.8 + k8s.io/api v0.19.2 + k8s.io/apiextensions-apiserver v0.19.2 + k8s.io/apimachinery v0.19.2 + k8s.io/cli-runtime v0.19.2 + k8s.io/client-go v0.19.2 k8s.io/klog v1.0.0 - k8s.io/kubectl v0.18.8 + k8s.io/kubectl v0.19.2 sigs.k8s.io/yaml v1.2.0 ) diff --git a/go.sum b/go.sum index 94f5fee82..e516e8d66 100644 --- a/go.sum +++ b/go.sum @@ -3,6 +3,17 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.51.0 h1:PvKAVQWCtlGUSlZkGW3QLelKaWq7KYv/MW1EboG8bfM= +cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= @@ -10,19 +21,27 @@ github.com/Azure/go-autorest v13.3.2+incompatible h1:VxzPyuhtnlBOzc4IWCZHqpyH2d+ github.com/Azure/go-autorest v13.3.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.9.0 h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs= github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= +github.com/Azure/go-autorest/autorest v0.9.6 h1:5YWtOnckcudzIw8lPPBcWOnmIFWMtHci1ZWAZulMSx0= +github.com/Azure/go-autorest/autorest v0.9.6/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630= github.com/Azure/go-autorest/autorest/adal v0.5.0 h1:q2gDruN08/guU9vAjuPWff0+QIrpH6ediguzdAzXAUU= github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= +github.com/Azure/go-autorest/autorest/adal v0.8.2 h1:O1X4oexUxnZCaEUGsvMnr8ZGj8HI37tNezwY4npRqA0= +github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= github.com/Azure/go-autorest/autorest/date v0.1.0 h1:YGrhWfrgtFs84+h0o46rJrlmsZtyZRg470CqAXTZaGM= github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSWlm5Ew6bxipnr/tbM= +github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= github.com/Azure/go-autorest/autorest/mocks v0.2.0 h1:Ww5g4zThfD/6cLb4z6xxgeyDa7QDkizMkJKe0ysZXp0= github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY= github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k= github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.4.1 h1:ThlnYciV1iM/V0OSF/dtkqWb6xo5qITT1TJBG1MRDJM= github.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= @@ -107,6 +126,9 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= @@ -181,6 +203,7 @@ github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arX github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QLSV/BsnenAOcDXdX4cMv4wP0B/5QbPg= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= @@ -201,6 +224,8 @@ github.com/evanphx/json-patch v4.2.0+incompatible h1:fUDGZCv/7iAN7u0puUVhvKCcsR6 github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M= github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses= +github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= @@ -210,6 +235,7 @@ github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVB github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7 h1:LofdAjjjqCSXMwLGgOgnE+rdPuvX9DxCqaHwKy7i/ko= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -217,6 +243,7 @@ github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= @@ -225,6 +252,8 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/logr v0.2.0 h1:QvGt2nLcHH0WK9orKa+ppBPAxREcH364nPUedEpK0TY= +github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= @@ -306,13 +335,24 @@ github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9 github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAONeMXrxql8uvOKuAZSu8aM5RUGv+1C6IJaEho= github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995/go.mod h1:lJgMEyOkYFkPcDKwRXegd+iM6E7matEszMG5HhwytU8= @@ -331,15 +371,20 @@ github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d h1:7XGaL1e6bYS1yIonGp9761ExpPPV1ui0SAC59Yube9k= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.1.0 h1:rVsPeBmXbYv4If/cumu1AzZPwV58q433hvONV1UEZoI= github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.4.1 h1:DLJCy1n/vrD4HPjOvYcT8aYQXpPIzoRZONaYwyycI+I= +github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/391+sT5o= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -391,6 +436,7 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= @@ -410,7 +456,10 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= @@ -424,6 +473,7 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= @@ -467,6 +517,8 @@ github.com/mattn/go-sqlite3 v1.12.0 h1:u/x3mp++qUxvYfulZ4HKOvVO0JWhk7HtE8lWhbGz/ github.com/mattn/go-sqlite3 v1.12.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= @@ -484,6 +536,8 @@ github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f h1:2+myh5ml7lgEU/5 github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/moby/term v0.0.0-20200312100748-672ec06f55cd h1:aY7OQNf2XqY/JQ6qREWamhI/81os/agb2BAGpcx5yWI= +github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -574,6 +628,8 @@ github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0 h1:miYCvYqFXtl/J9FIy8eNpBfYthAEFg+Ys0XyUVEcDsc= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= +github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= @@ -591,6 +647,8 @@ github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6 github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.7.0 h1:L+1lyG48J1zAQXA3RBX/nG/B3gjlHq0zTt2tlbJLyCY= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= @@ -600,6 +658,8 @@ github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQl github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= @@ -691,6 +751,7 @@ github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE= @@ -701,7 +762,9 @@ github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.etcd.io/etcd v0.5.0-alpha.5.0.20200819165624-17cef6e3e9d5/go.mod h1:skWido08r9w6Lq/w70DO5XYIKMu4QFu1+4VsqLQuJy8= go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= @@ -729,11 +792,13 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d h1:9FCpayM9Egr1baVnV1SX0H87m+XB0B8S0hAMi99X/3U= golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo= @@ -743,13 +808,27 @@ golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -766,6 +845,7 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -775,10 +855,17 @@ golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI= golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6 h1:pE8b58s1HRDMi8RDc79m0HISf9D4TzseP40cEA6IGfs= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -798,27 +885,43 @@ golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3 h1:7TYNF4UdlohbFwpNH04CoPMp1cHUZgO1Ebq5r2hIjfo= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7 h1:HmbHVPwrPEKPGLAcHSrMe6+hqSUlvZU0rab6x5EXfGU= golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f h1:68K/z8GLUxV76xGSqwTWw2gyk/jwn79LUL43rES2g8o= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4 h1:5/PjkGUjvEU5Gl6BxmvKRPpqo2uNMv4rcHBMwzk/st8= +golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= @@ -834,17 +937,30 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= @@ -852,10 +968,15 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= @@ -863,27 +984,45 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= @@ -911,62 +1050,94 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= k8s.io/api v0.18.4 h1:8x49nBRxuXGUlDlwlWd3RMY1SayZrzFfxea3UZSkFw4= k8s.io/api v0.18.4/go.mod h1:lOIQAKYgai1+vz9J7YcDZwC26Z0zQewYOGWdyIPUUQ4= k8s.io/api v0.18.8 h1:aIKUzJPb96f3fKec2lxtY7acZC9gQNDLVhfSGpxBAC4= k8s.io/api v0.18.8/go.mod h1:d/CXqwWv+Z2XEG1LgceeDmHQwpUJhROPx16SlxJgERY= +k8s.io/api v0.19.2 h1:q+/krnHWKsL7OBZg/rxnycsl9569Pud76UJ77MvKXms= +k8s.io/api v0.19.2/go.mod h1:IQpK0zFQ1xc5iNIQPqzgoOwuFugaYHK4iCknlAQP9nI= k8s.io/apiextensions-apiserver v0.18.4 h1:Y3HGERmS8t9u12YNUFoOISqefaoGRuTc43AYCLzWmWE= k8s.io/apiextensions-apiserver v0.18.4/go.mod h1:NYeyeYq4SIpFlPxSAB6jHPIdvu3hL0pc36wuRChybio= k8s.io/apiextensions-apiserver v0.18.8 h1:pkqYPKTHa0/3lYwH7201RpF9eFm0lmZDFBNzhN+k/sA= k8s.io/apiextensions-apiserver v0.18.8/go.mod h1:7f4ySEkkvifIr4+BRrRWriKKIJjPyg9mb/p63dJKnlM= +k8s.io/apiextensions-apiserver v0.19.2 h1:oG84UwiDsVDu7dlsGQs5GySmQHCzMhknfhFExJMz9tA= +k8s.io/apiextensions-apiserver v0.19.2/go.mod h1:EYNjpqIAvNZe+svXVx9j4uBaVhTB4C94HkY3w058qcg= k8s.io/apimachinery v0.18.4 h1:ST2beySjhqwJoIFk6p7Hp5v5O0hYY6Gngq/gUYXTPIA= k8s.io/apimachinery v0.18.4/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko= k8s.io/apimachinery v0.18.8 h1:jimPrycCqgx2QPearX3to1JePz7wSbVLq+7PdBTTwQ0= k8s.io/apimachinery v0.18.8/go.mod h1:6sQd+iHEqmOtALqOFjSWp2KZ9F0wlU/nWm0ZgsYWMig= +k8s.io/apimachinery v0.19.2 h1:5Gy9vQpAGTKHPVOh5c4plE274X8D/6cuEiTO2zve7tc= +k8s.io/apimachinery v0.19.2/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= k8s.io/apiserver v0.18.4/go.mod h1:q+zoFct5ABNnYkGIaGQ3bcbUNdmPyOCoEBcg51LChY8= k8s.io/apiserver v0.18.8/go.mod h1:12u5FuGql8Cc497ORNj79rhPdiXQC4bf53X/skR/1YM= +k8s.io/apiserver v0.19.2/go.mod h1:FreAq0bJ2vtZFj9Ago/X0oNGC51GfubKK/ViOKfVAOA= k8s.io/cli-runtime v0.18.4 h1:IUx7quIOb4gbQ4M+B1ksF/PTBovQuL5tXWzplX3t+FM= k8s.io/cli-runtime v0.18.4/go.mod h1:9/hS/Cuf7NVzWR5F/5tyS6xsnclxoPLVtwhnkJG1Y4g= k8s.io/cli-runtime v0.18.8 h1:ycmbN3hs7CfkJIYxJAOB10iW7BVPmXGXkfEyiV9NJ+k= k8s.io/cli-runtime v0.18.8/go.mod h1:7EzWiDbS9PFd0hamHHVoCY4GrokSTPSL32MA4rzIu0M= +k8s.io/cli-runtime v0.19.2 h1:d4uOtKhy3ImdaKqZJ8yQgLrdtUwsJLfP4Dw7L/kVPOo= +k8s.io/cli-runtime v0.19.2/go.mod h1:CMynmJM4Yf02TlkbhKxoSzi4Zf518PukJ5xep/NaNeY= k8s.io/client-go v0.18.4 h1:un55V1Q/B3JO3A76eS0kUSywgGK/WR3BQ8fHQjNa6Zc= k8s.io/client-go v0.18.4/go.mod h1:f5sXwL4yAZRkAtzOxRWUhA/N8XzGCb+nPZI8PfobZ9g= k8s.io/client-go v0.18.8 h1:SdbLpIxk5j5YbFr1b7fq8S7mDgDjYmUxSbszyoesoDM= k8s.io/client-go v0.18.8/go.mod h1:HqFqMllQ5NnQJNwjro9k5zMyfhZlOwpuTLVrxjkYSxU= +k8s.io/client-go v0.19.2 h1:gMJuU3xJZs86L1oQ99R4EViAADUPMHHtS9jFshasHSc= +k8s.io/client-go v0.19.2/go.mod h1:S5wPhCqyDNAlzM9CnEdgTGV4OqhsW3jGO1UM1epwfJA= k8s.io/code-generator v0.18.4/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c= k8s.io/code-generator v0.18.8/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c= +k8s.io/code-generator v0.19.2/go.mod h1:moqLn7w0t9cMs4+5CQyxnfA/HV8MF6aAVENF+WZZhgk= k8s.io/component-base v0.18.4 h1:Kr53Fp1iCGNsl9Uv4VcRvLy7YyIqi9oaJOQ7SXtKI98= k8s.io/component-base v0.18.4/go.mod h1:7jr/Ef5PGmKwQhyAz/pjByxJbC58mhKAhiaDu0vXfPk= k8s.io/component-base v0.18.8 h1:BW5CORobxb6q5mb+YvdwQlyXXS6NVH5fDXWbU7tf2L8= k8s.io/component-base v0.18.8/go.mod h1:00frPRDas29rx58pPCxNkhUfPbwajlyyvu8ruNgSErU= +k8s.io/component-base v0.19.2 h1:jW5Y9RcZTb79liEhW3XDVTW7MuvEGP0tQZnfSX6/+gs= +k8s.io/component-base v0.19.2/go.mod h1:g5LrsiTiabMLZ40AR6Hl45f088DevyGY+cCE2agEIVo= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= +k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= +k8s.io/klog/v2 v2.2.0 h1:XRvcwJozkgZ1UQJmfMGpvRthQHOvihEhYtDfAaxMz/A= +k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6 h1:Oh3Mzx5pJ+yIumsAD0MOECPVeXsVot0UkiaCGVyfGQY= k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= +k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6 h1:+WnxoVtG8TMiudHBSEtrVL1egv36TkkJm+bA8AxicmQ= +k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= k8s.io/kubectl v0.18.4 h1:l9DUYPTEMs1+qNtoqPpTyaJOosvj7l7tQqphCO1K52s= k8s.io/kubectl v0.18.4/go.mod h1:EzB+nfeUWk6fm6giXQ8P4Fayw3dsN+M7Wjy23mTRtB0= k8s.io/kubectl v0.18.8 h1:qTkHCz21YmK0+S0oE6TtjtxmjeDP42gJcZJyRKsIenA= k8s.io/kubectl v0.18.8/go.mod h1:PlEgIAjOMua4hDFTEkVf+W5M0asHUKfE4y7VDZkpLHM= +k8s.io/kubectl v0.19.2 h1:/Dxz9u7S0GnchLA6Avqi5k1qhZH4Fusgecj8dHsSnbk= +k8s.io/kubectl v0.19.2/go.mod h1:4ib3oj5ma6gF95QukTvC7ZBMxp60+UEAhDPjLuBIrV4= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/metrics v0.18.4/go.mod h1:luze4fyI9JG4eLDZy0kFdYEebqNfi0QrG4xNEbPkHOs= k8s.io/metrics v0.18.8/go.mod h1:j7JzZdiyhLP2BsJm/Fzjs+j5Lb1Y7TySjhPWqBPwRXA= +k8s.io/metrics v0.19.2/go.mod h1:IlLaAGXN0q7yrtB+SV0q3JIraf6VtlDr+iuTcX21fCU= k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89 h1:d4vVOjXm687F1iLSP2q3lyPPuyvTUt3aVoBpi2DqRsU= k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +k8s.io/utils v0.0.0-20200729134348-d5654de09c73 h1:uJmqzgNWG7XyClnU/mLPBWwfKKF1K8Hf8whTseBgJcg= +k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.9/go.mod h1:dzAXnQbTRyDlZPJX2SUPEqvnB+j7AJjtlox7PEwigU0= sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0= sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= sigs.k8s.io/structured-merge-diff/v3 v3.0.0 h1:dOmIZBMfhcHS09XZkMyUgkq5trg3/jRyJYFZUiaOp8E= sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= +sigs.k8s.io/structured-merge-diff/v4 v4.0.1 h1:YXTMot5Qz/X1iBRJhAt+vI+HVttY0WkSqqhKxQ0xVbA= +sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= diff --git a/pkg/action/validate.go b/pkg/action/validate.go index 0c40a9c3c..6e074f78b 100644 --- a/pkg/action/validate.go +++ b/pkg/action/validate.go @@ -46,7 +46,7 @@ func existingResourceConflict(resources kube.ResourceList, releaseName, releaseN } helper := resource.NewHelper(info.Client, info.Mapping) - existing, err := helper.Get(info.Namespace, info.Name, info.Export) + existing, err := helper.Get(info.Namespace, info.Name) if err != nil { if apierrors.IsNotFound(err) { return nil diff --git a/pkg/kube/client.go b/pkg/kube/client.go index decfc2e6e..83bebf51f 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -178,7 +178,7 @@ func (c *Client) Update(original, target ResourceList, force bool) (*Result, err } helper := resource.NewHelper(info.Client, info.Mapping) - if _, err := helper.Get(info.Namespace, info.Name, info.Export); err != nil { + if _, err := helper.Get(info.Namespace, info.Name); err != nil { if !apierrors.IsNotFound(err) { return errors.Wrap(err, "could not get information about the resource") } @@ -374,7 +374,7 @@ func createPatch(target *resource.Info, current runtime.Object) ([]byte, types.P // Fetch the current object for the three way merge helper := resource.NewHelper(target.Client, target.Mapping) - currentObj, err := helper.Get(target.Namespace, target.Name, target.Export) + currentObj, err := helper.Get(target.Namespace, target.Name) if err != nil && !apierrors.IsNotFound(err) { return nil, types.StrategicMergePatchType, errors.Wrapf(err, "unable to get data for current object %s/%s", target.Namespace, target.Name) } From 66034e403548417ac90d9769f5dcde2d354e0033 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Wed, 6 May 2020 13:43:17 -0700 Subject: [PATCH 41/45] ref(cmd): prevent klogs flags from polluting the help text Remove klog flags from help text. No change to behavior. ``` ... Flags: --debug enable verbose output -h, --help help for helm --kube-apiserver string the address and the port for the Kubernetes API server --kube-context string name of the kubeconfig context to use --kube-token string bearer token used for authentication --kubeconfig string path to the kubeconfig file -n, --namespace string namespace scope for this request --registry-config string path to the registry config file (default "/Users/areese/.config/helm/registry.json") --repository-cache string path to the file containing cached repository indexes (default "/Users/areese/.cache/helm/repository") --repository-config string path to the file containing repository names and URLs (default "/Users/areese/.config/helm/repositories.yaml") ``` Signed-off-by: Adam Reese --- cmd/helm/flags.go | 23 +++++++++++++++++++++++ cmd/helm/helm.go | 18 ------------------ cmd/helm/root.go | 1 + 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/cmd/helm/flags.go b/cmd/helm/flags.go index d1329c279..75b9056f0 100644 --- a/cmd/helm/flags.go +++ b/cmd/helm/flags.go @@ -17,6 +17,7 @@ limitations under the License. package main import ( + "flag" "fmt" "log" "path/filepath" @@ -24,6 +25,7 @@ import ( "github.com/spf13/cobra" "github.com/spf13/pflag" + "k8s.io/klog" "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/cli/output" @@ -155,3 +157,24 @@ func compVersionFlag(chartRef string, toComplete string) ([]string, cobra.ShellC return versions, cobra.ShellCompDirectiveNoFileComp } + +// addKlogFlags adds flags from k8s.io/klog +// marks the flags as hidden to avoid polluting the help text +func addKlogFlags(fs *pflag.FlagSet) { + local := flag.NewFlagSet("klog", flag.ExitOnError) + klog.InitFlags(local) + local.VisitAll(func(fl *flag.Flag) { + fl.Name = normalize(fl.Name) + if fs.Lookup(fl.Name) != nil { + return + } + newflag := pflag.PFlagFromGoFlag(fl) + newflag.Hidden = true + fs.AddFlag(newflag) + }) +} + +// normalize replaces underscores with hyphens +func normalize(s string) string { + return strings.ReplaceAll(s, "_", "-") +} diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index 98cb00f43..88a5ddcb9 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -17,7 +17,6 @@ limitations under the License. package main // import "helm.sh/helm/v3/cmd/helm" import ( - "flag" "fmt" "io/ioutil" "log" @@ -25,8 +24,6 @@ import ( "strings" "github.com/spf13/cobra" - "github.com/spf13/pflag" - "k8s.io/klog" "sigs.k8s.io/yaml" // Import to initialize client auth plugins. @@ -61,17 +58,7 @@ func warning(format string, v ...interface{}) { fmt.Fprintf(os.Stderr, format, v...) } -func initKubeLogs() { - pflag.CommandLine.SetNormalizeFunc(wordSepNormalizeFunc) - gofs := flag.NewFlagSet("klog", flag.ExitOnError) - klog.InitFlags(gofs) - pflag.CommandLine.AddGoFlagSet(gofs) - pflag.CommandLine.Set("logtostderr", "true") -} - func main() { - initKubeLogs() - actionConfig := new(action.Configuration) cmd, err := newRootCmd(actionConfig, os.Stdout, os.Args[1:]) if err != nil { @@ -101,11 +88,6 @@ func main() { } } -// wordSepNormalizeFunc changes all flags that contain "_" separators -func wordSepNormalizeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName { - return pflag.NormalizedName(strings.ReplaceAll(name, "_", "-")) -} - func checkOCIFeatureGate() func(_ *cobra.Command, _ []string) error { return func(_ *cobra.Command, _ []string) error { if !FeatureGateOCI.IsEnabled() { diff --git a/cmd/helm/root.go b/cmd/helm/root.go index 0a2b1be8f..cc97a5541 100644 --- a/cmd/helm/root.go +++ b/cmd/helm/root.go @@ -93,6 +93,7 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string flags := cmd.PersistentFlags() settings.AddFlags(flags) + addKlogFlags(flags) // Setup shell completion for the namespace flag err := cmd.RegisterFlagCompletionFunc("namespace", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { From a167b3fc8719db19e87280bc134a24801808272e Mon Sep 17 00:00:00 2001 From: zouyu Date: Sun, 27 Sep 2020 14:41:27 +0800 Subject: [PATCH 42/45] Fix wrong function's name in comment Signed-off-by: zouyu --- pkg/repo/chartrepo.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/repo/chartrepo.go b/pkg/repo/chartrepo.go index edb86eaeb..92892bb85 100644 --- a/pkg/repo/chartrepo.go +++ b/pkg/repo/chartrepo.go @@ -208,7 +208,7 @@ func FindChartInAuthRepoURL(repoURL, username, password, chartName, chartVersion return FindChartInAuthAndTLSRepoURL(repoURL, username, password, chartName, chartVersion, certFile, keyFile, caFile, false, getters) } -// FindChartInAuthRepoURL finds chart in chart repository pointed by repoURL +// FindChartInAuthAndTLSRepoURL finds chart in chart repository pointed by repoURL // without adding repo to repositories, like FindChartInRepoURL, // but it also receives credentials and TLS verify flag for the chart repository. // TODO Helm 4, FindChartInAuthAndTLSRepoURL should be integrated into FindChartInAuthRepoURL. From a6e76cbbbeee153ad93cb7a269714080b4c37e1d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Sep 2020 13:27:48 +0000 Subject: [PATCH 43/45] Bump github.com/lib/pq from 1.7.0 to 1.8.0 Bumps [github.com/lib/pq](https://github.com/lib/pq) from 1.7.0 to 1.8.0. - [Release notes](https://github.com/lib/pq/releases) - [Commits](https://github.com/lib/pq/compare/v1.7.0...v1.8.0) Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index ed1ac1e7c..6d69113fa 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/gosuri/uitable v0.0.4 github.com/jessevdk/go-flags v1.4.0 // indirect github.com/jmoiron/sqlx v1.2.0 - github.com/lib/pq v1.7.0 + github.com/lib/pq v1.8.0 github.com/mattn/go-shellwords v1.0.10 github.com/mitchellh/copystructure v1.0.0 github.com/opencontainers/go-digest v1.0.0 diff --git a/go.sum b/go.sum index e516e8d66..2dd3d7fca 100644 --- a/go.sum +++ b/go.sum @@ -486,6 +486,8 @@ github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.7.0 h1:h93mCPfUSkaul3Ka/VG8uZdmW1uMHDGxzu0NWHuJmHY= github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg= +github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= From 253a9500d7e4b1ae3a9c7943e39cfc19c55a1e25 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Sep 2020 13:28:25 +0000 Subject: [PATCH 44/45] Bump github.com/gofrs/flock from 0.7.1 to 0.8.0 Bumps [github.com/gofrs/flock](https://github.com/gofrs/flock) from 0.7.1 to 0.8.0. - [Release notes](https://github.com/gofrs/flock/releases) - [Commits](https://github.com/gofrs/flock/compare/v0.7.1...v0.8.0) Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index ed1ac1e7c..3b17e3e62 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/docker/go-units v0.4.0 github.com/evanphx/json-patch v4.9.0+incompatible github.com/gobwas/glob v0.2.3 - github.com/gofrs/flock v0.7.1 + github.com/gofrs/flock v0.8.0 github.com/gosuri/uitable v0.0.4 github.com/jessevdk/go-flags v1.4.0 // indirect github.com/jmoiron/sqlx v1.2.0 diff --git a/go.sum b/go.sum index e516e8d66..1a7e720a8 100644 --- a/go.sum +++ b/go.sum @@ -320,6 +320,8 @@ github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6 github.com/godror/godror v0.13.3/go.mod h1:2ouUT4kdhUBk7TAkHWD4SN0CdI0pgEQbo8FVHhbSKWg= github.com/gofrs/flock v0.7.1 h1:DP+LD/t0njgoPBvT5MJLeliUIVQR03hiKR6vezdwHlc= github.com/gofrs/flock v0.7.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gofrs/flock v0.8.0 h1:MSdYClljsF3PbENUUEx85nkWfJSGfzYI9yEBZOJz6CY= +github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= From 6aa54eacc52db045e82faa6b256678863e325a20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikula=CC=81s=CC=8C=20Di=CC=81te=CC=8C?= Date: Thu, 27 Aug 2020 11:45:14 +0200 Subject: [PATCH 45/45] feat(install): add requested version to error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mikuláš Dítě --- cmd/helm/show_test.go | 7 +++++++ pkg/action/install.go | 6 +++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/cmd/helm/show_test.go b/cmd/helm/show_test.go index ac5294d3c..9781a3de4 100644 --- a/cmd/helm/show_test.go +++ b/cmd/helm/show_test.go @@ -49,6 +49,13 @@ func TestShowPreReleaseChart(t *testing.T) { fail: true, expectedErr: "failed to download \"test/pre-release-chart\"", }, + { + name: "show pre-release chart", + args: "test/pre-release-chart", + fail: true, + flags: "--version 1.0.0", + expectedErr: "failed to download \"test/pre-release-chart\" at version \"1.0.0\"", + }, { name: "show pre-release chart with 'devel' flag", args: "test/pre-release-chart", diff --git a/pkg/action/install.go b/pkg/action/install.go index 9bfecd915..caeefca68 100644 --- a/pkg/action/install.go +++ b/pkg/action/install.go @@ -677,5 +677,9 @@ func (c *ChartPathOptions) LocateChart(name string, settings *cli.EnvSettings) ( return filename, err } - return filename, errors.Errorf("failed to download %q (hint: running `helm repo update` may help)", name) + atVersion := "" + if version != "" { + atVersion = fmt.Sprintf(" at version %q", version) + } + return filename, errors.Errorf("failed to download %q%s (hint: running `helm repo update` may help)", name, atVersion) }