Merge branch 'main' into readiness-generation-check

pull/10920/head
James Oden 3 years ago
commit f067073e23

@ -5,7 +5,7 @@ jobs:
build:
working_directory: ~/helm.sh/helm
docker:
- image: circleci/golang:1.17
- image: cimg/go:1.18
auth:
username: $DOCKER_USER
@ -13,7 +13,7 @@ jobs:
environment:
GOCACHE: "/tmp/go/cache"
GOLANGCI_LINT_VERSION: "1.43.0"
GOLANGCI_LINT_VERSION: "1.46.2"
steps:
- checkout

@ -12,7 +12,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: '1.17'
go-version: '1.18'
- name: Install golangci-lint
run: |
curl -sSLO https://github.com/golangci/golangci-lint/releases/download/v$GOLANGCI_LINT_VERSION/golangci-lint-$GOLANGCI_LINT_VERSION-linux-amd64.tar.gz
@ -21,8 +21,8 @@ jobs:
sudo mv golangci-lint-$GOLANGCI_LINT_VERSION-linux-amd64/golangci-lint /usr/local/bin/golangci-lint
rm -rf golangci-lint-$GOLANGCI_LINT_VERSION-linux-amd64*
env:
GOLANGCI_LINT_VERSION: '1.43.0'
GOLANGCI_LINT_SHA256: 'f3515cebec926257da703ba0a2b169e4a322c11dc31a8b4656b50a43e48877f4'
GOLANGCI_LINT_VERSION: '1.46.2'
GOLANGCI_LINT_SHA256: '242cd4f2d6ac0556e315192e8555784d13da5d1874e51304711570769c4f2b9b'
- name: Test style
run: make test-style
- name: Run unit tests

@ -149,15 +149,15 @@ gen-test-golden: test-unit
# ------------------------------------------------------------------------------
# dependencies
# If go get is run from inside the project directory it will add the dependencies
# to the go.mod file. To avoid that we change to a directory without a go.mod file
# when downloading the following dependencies
# If go install is run from inside the project directory it will add the
# dependencies to the go.mod file. To avoid that we change to a directory
# without a go.mod file when downloading the following dependencies
$(GOX):
(cd /; GO111MODULE=on go get -u github.com/mitchellh/gox)
(cd /; GO111MODULE=on go install github.com/mitchellh/gox@latest)
$(GOIMPORTS):
(cd /; GO111MODULE=on go get -u golang.org/x/tools/cmd/goimports)
(cd /; GO111MODULE=on go install golang.org/x/tools/cmd/goimports@latest)
# ------------------------------------------------------------------------------
# release

@ -5,6 +5,7 @@ maintainers:
- jdolitsky
- marckhouzam
- mattfarina
- sabre1041
- scottrigby
- SlickNik
- technosophos

@ -25,6 +25,8 @@ import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/cobra/doc"
"golang.org/x/text/cases"
"golang.org/x/text/language"
"helm.sh/helm/v3/cmd/helm/require"
)
@ -84,7 +86,7 @@ func (o *docsOptions) run(out io.Writer) error {
hdrFunc := func(filename string) string {
base := filepath.Base(filename)
name := strings.TrimSuffix(base, path.Ext(base))
title := strings.Title(strings.Replace(name, "_", " ", -1))
title := cases.Title(language.Und).String(strings.Replace(name, "_", " ", -1))
return fmt.Sprintf("---\ntitle: \"%s\"\n---\n\n", title)
}

@ -86,13 +86,14 @@ the '--debug' and '--dry-run' flags can be combined.
If --verify is set, the chart MUST have a provenance file, and the provenance
file MUST pass all verification steps.
There are five different ways you can express the chart you want to install:
There are six different ways you can express the chart you want to install:
1. By chart reference: helm install mymaria example/mariadb
2. By path to a packaged chart: helm install mynginx ./nginx-1.2.3.tgz
3. By path to an unpacked chart directory: helm install mynginx ./nginx
4. By absolute URL: helm install mynginx https://example.com/charts/nginx-1.2.3.tgz
5. By chart reference and repo url: helm install --repo https://example.com/charts/ mynginx nginx
6. By OCI registries: helm install mynginx --version 1.2.3 oci://example.com/charts/nginx
CHART REFERENCES

@ -29,6 +29,7 @@ import (
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/cli/values"
"helm.sh/helm/v3/pkg/getter"
"helm.sh/helm/v3/pkg/lint/support"
)
var longLintHelp = `
@ -76,12 +77,23 @@ func newLintCmd(out io.Writer) *cobra.Command {
var message strings.Builder
failed := 0
errorsOrWarnings := 0
for _, path := range paths {
fmt.Fprintf(&message, "==> Linting %s\n", path)
result := client.Run([]string{path}, vals)
// If there is no errors/warnings and quiet flag is set
// go to the next chart
hasWarningsOrErrors := action.HasWarningsOrErrors(result)
if hasWarningsOrErrors {
errorsOrWarnings++
}
if client.Quiet && !hasWarningsOrErrors {
continue
}
fmt.Fprintf(&message, "==> Linting %s\n", path)
// All the Errors that are generated by a chart
// that failed a lint will be included in the
// results.Messages so we only need to print
@ -93,8 +105,10 @@ func newLintCmd(out io.Writer) *cobra.Command {
}
for _, msg := range result.Messages {
if !client.Quiet || msg.Severity > support.InfoSev {
fmt.Fprintf(&message, "%s\n", msg)
}
}
if len(result.Errors) != 0 {
failed++
@ -112,7 +126,9 @@ func newLintCmd(out io.Writer) *cobra.Command {
if failed > 0 {
return errors.New(summary)
}
if !client.Quiet || errorsOrWarnings > 0 {
fmt.Fprintln(out, summary)
}
return nil
},
}
@ -120,6 +136,7 @@ func newLintCmd(out io.Writer) *cobra.Command {
f := cmd.Flags()
f.BoolVar(&client.Strict, "strict", false, "fail on lint warnings")
f.BoolVar(&client.WithSubcharts, "with-subcharts", false, "lint dependent charts")
f.BoolVar(&client.Quiet, "quiet", false, "print only warnings and errors")
addValueOptionsFlags(f, valueOpts)
return cmd

@ -37,6 +37,27 @@ func TestLintCmdWithSubchartsFlag(t *testing.T) {
runTestCmd(t, tests)
}
func TestLintCmdWithQuietFlag(t *testing.T) {
testChart1 := "testdata/testcharts/alpine"
testChart2 := "testdata/testcharts/chart-bad-requirements"
tests := []cmdTestCase{{
name: "lint good chart using --quiet flag",
cmd: fmt.Sprintf("lint --quiet %s", testChart1),
golden: "output/lint-quiet.txt",
}, {
name: "lint two charts, one with error using --quiet flag",
cmd: fmt.Sprintf("lint --quiet %s %s", testChart1, testChart2),
golden: "output/lint-quiet-with-error.txt",
wantError: true,
}, {
name: "lint chart with warning using --quiet flag",
cmd: "lint --quiet testdata/testcharts/chart-with-only-crds",
golden: "output/lint-quiet-with-warning.txt",
}}
runTestCmd(t, tests)
}
func TestLintFileCompletion(t *testing.T) {
checkFileCompletion(t, "lint", true)
checkFileCompletion(t, "lint mypath", true) // Multiple paths can be given

@ -83,8 +83,7 @@ func newListCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
}
if client.Short {
names := make([]string, 0)
names := make([]string, 0, len(results))
for _, res := range results {
names = append(names, res.Name)
}
@ -103,17 +102,16 @@ func newListCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
fmt.Fprintln(out, res.Name)
}
return nil
default:
return outfmt.Write(out, newReleaseListWriter(results, client.TimeFormat))
}
}
return outfmt.Write(out, newReleaseListWriter(results, client.TimeFormat))
return outfmt.Write(out, newReleaseListWriter(results, client.TimeFormat, client.NoHeaders))
},
}
f := cmd.Flags()
f.BoolVarP(&client.Short, "short", "q", false, "output short (quiet) listing format")
f.BoolVarP(&client.NoHeaders, "no-headers", "", false, "don't print headers when using the default output format")
f.StringVar(&client.TimeFormat, "time-format", "", `format time using golang time formatter. Example: --time-format "2006-01-02 15:04:05Z0700"`)
f.BoolVarP(&client.ByDate, "date", "d", false, "sort by release date")
f.BoolVarP(&client.SortReverse, "reverse", "r", false, "reverse the sort order")
@ -146,9 +144,10 @@ type releaseElement struct {
type releaseListWriter struct {
releases []releaseElement
noHeaders bool
}
func newReleaseListWriter(releases []*release.Release, timeFormat string) *releaseListWriter {
func newReleaseListWriter(releases []*release.Release, timeFormat string, noHeaders bool) *releaseListWriter {
// Initialize the array so no results returns an empty array instead of null
elements := make([]releaseElement, 0, len(releases))
for _, r := range releases {
@ -173,12 +172,14 @@ func newReleaseListWriter(releases []*release.Release, timeFormat string) *relea
elements = append(elements, element)
}
return &releaseListWriter{elements}
return &releaseListWriter{elements, noHeaders}
}
func (r *releaseListWriter) WriteTable(out io.Writer) error {
table := uitable.New()
if !r.noHeaders {
table.AddRow("NAME", "NAMESPACE", "REVISION", "UPDATED", "STATUS", "CHART", "APP VERSION")
}
for _, r := range r.releases {
table.AddRow(r.Name, r.Namespace, r.Revision, r.Updated, r.Status, r.Chart, r.AppVersion)
}

@ -148,6 +148,11 @@ func TestListCmd(t *testing.T) {
cmd: "list",
golden: "output/list.txt",
rels: releaseFixture,
}, {
name: "list without headers",
cmd: "list --no-headers",
golden: "output/list-no-headers.txt",
rels: releaseFixture,
}, {
name: "list all releases",
cmd: "list --all",

@ -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", "--kube-as-user", "--kube-as-group", "--kube-ca-file", "--registry-config", "--repository-cache", "--repository-config"}
kvargs := []string{"--kube-context", "--namespace", "-n", "--kubeconfig", "--kube-apiserver", "--kube-token", "--kube-as-user", "--kube-as-group", "--kube-ca-file", "--registry-config", "--repository-cache", "--repository-config", "--insecure-skip-tls-verify", "--tls-server-name"}
knownArg := func(a string) bool {
for _, pre := range kvargs {
if strings.HasPrefix(a, pre+"=") {

@ -80,7 +80,7 @@ func newPullCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
f.BoolVar(&client.Untar, "untar", false, "if set to true, will untar the chart after downloading it")
f.BoolVar(&client.VerifyLater, "prov", false, "fetch the provenance file, but don't perform verification")
f.StringVar(&client.UntarDir, "untardir", ".", "if untar is specified, this flag specifies the name of the directory into which the chart is expanded")
f.StringVarP(&client.DestDir, "destination", "d", ".", "location to write the chart. If this and tardir are specified, tardir is appended to this")
f.StringVarP(&client.DestDir, "destination", "d", ".", "location to write the chart. If this and untardir are specified, untardir is appended to this")
addChartPathOptionsFlags(f, &client.ChartPathOptions)
err := cmd.RegisterFlagCompletionFunc("version", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {

@ -25,7 +25,7 @@ import (
"os"
"strings"
"github.com/docker/docker/pkg/term" //nolint
"github.com/moby/term"
"github.com/spf13/cobra"
"helm.sh/helm/v3/cmd/helm/require"

@ -38,8 +38,8 @@ func newRepoListCmd(out io.Writer) *cobra.Command {
Args: require.NoArgs,
ValidArgsFunction: noCompletions,
RunE: func(cmd *cobra.Command, args []string) error {
f, err := repo.LoadFile(settings.RepositoryConfig)
if isNotExist(err) || (len(f.Repositories) == 0 && !(outfmt == output.JSON || outfmt == output.YAML)) {
f, _ := repo.LoadFile(settings.RepositoryConfig)
if len(f.Repositories) == 0 && !(outfmt == output.JSON || outfmt == output.YAML) {
return errors.New("no repositories to show")
}

@ -46,7 +46,7 @@ Common actions for Helm:
Environment variables:
| Name | Description |
|------------------------------------|-----------------------------------------------------------------------------------|
|------------------------------------|---------------------------------------------------------------------------------------------------|
| $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. |
@ -67,6 +67,9 @@ Environment variables:
| $HELM_KUBEASUSER | set the Username to impersonate for the operation. |
| $HELM_KUBECONTEXT | set the name of the kubeconfig context. |
| $HELM_KUBETOKEN | set the Bearer KubeToken used for authentication. |
| $HELM_KUBEINSECURE_SKIP_TLS_VERIFY | indicate if the Kubernetes API server's certificate validation should be skipped (insecure) |
| $HELM_KUBETLS_SERVER_NAME | set the server name used to validate the Kubernetes API server certificate |
| $HELM_BURST_LIMIT | set the default burst limit in the case the server contains many CRDs (default 100, -1 to disable)|
Helm stores cache, configuration, and data based on the following configuration order:
@ -151,6 +154,7 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string
registryClient, err := registry.NewClient(
registry.ClientOptDebug(settings.Debug),
registry.ClientOptEnableCache(true),
registry.ClientOptWriter(out),
registry.ClientOptCredentialsFile(settings.RegistryConfig),
)

@ -47,14 +47,14 @@ func TestShowPreReleaseChart(t *testing.T) {
name: "show pre-release chart",
args: "test/pre-release-chart",
fail: true,
expectedErr: "failed to download \"test/pre-release-chart\"",
expectedErr: "chart \"pre-release-chart\" matching not found in test index. (try 'helm repo update'): no chart version found for 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\"",
expectedErr: "chart \"pre-release-chart\" matching 1.0.0 not found in test index. (try 'helm repo update'): no chart version found for pre-release-chart-1.0.0",
},
{
name: "show pre-release chart with 'devel' flag",

@ -1,4 +1,5 @@
HELM_BIN
HELM_BURST_LIMIT
HELM_CACHE_HOME
HELM_CONFIG_HOME
HELM_DATA_HOME
@ -8,6 +9,8 @@ HELM_KUBEASGROUPS
HELM_KUBEASUSER
HELM_KUBECAFILE
HELM_KUBECONTEXT
HELM_KUBEINSECURE_SKIP_TLS_VERIFY
HELM_KUBETLS_SERVER_NAME
HELM_KUBETOKEN
HELM_MAX_HISTORY
HELM_NAMESPACE

@ -0,0 +1,8 @@
==> Linting testdata/testcharts/chart-bad-requirements
[ERROR] Chart.yaml: unable to parse YAML
error converting YAML to JSON: yaml: line 6: did not find expected '-' indicator
[WARNING] templates/: directory not found
[ERROR] : unable to load chart
cannot load Chart.yaml: error converting YAML to JSON: yaml: line 6: did not find expected '-' indicator
Error: 2 chart(s) linted, 1 chart(s) failed

@ -0,0 +1,4 @@
==> Linting testdata/testcharts/chart-with-only-crds
[WARNING] templates/: directory not found
1 chart(s) linted, 0 chart(s) failed

@ -0,0 +1,4 @@
hummingbird default 1 2016-01-16 00:00:03 +0000 UTC deployed chickadee-1.0.0 0.0.1
iguana default 2 2016-01-16 00:00:04 +0000 UTC deployed chickadee-1.0.0 0.0.1
rocket default 1 2016-01-16 00:00:02 +0000 UTC failed chickadee-1.0.0 0.0.1
starlord default 2 2016-01-16 00:00:01 +0000 UTC deployed chickadee-1.0.0 0.0.1

@ -1 +1 @@
version.BuildInfo{Version:"v3.8", GitCommit:"", GitTreeState:"", GoVersion:""}
version.BuildInfo{Version:"v3.9", GitCommit:"", GitTreeState:"", GoVersion:""}

@ -1 +1 @@
version.BuildInfo{Version:"v3.8", GitCommit:"", GitTreeState:"", GoVersion:""}
version.BuildInfo{Version:"v3.9", GitCommit:"", GitTreeState:"", GoVersion:""}

@ -1 +1 @@
Version: v3.8
Version: v3.9

@ -1 +1 @@
version.BuildInfo{Version:"v3.8", GitCommit:"", GitTreeState:"", GoVersion:""}
version.BuildInfo{Version:"v3.9", GitCommit:"", GitTreeState:"", GoVersion:""}

@ -1,47 +1,48 @@
module helm.sh/helm/v3
go 1.17
go 1.18
require (
github.com/BurntSushi/toml v1.0.0
github.com/BurntSushi/toml v1.1.0
github.com/DATA-DOG/go-sqlmock v1.5.0
github.com/Masterminds/semver/v3 v3.1.1
github.com/Masterminds/sprig/v3 v3.2.2
github.com/Masterminds/squirrel v1.5.2
github.com/Masterminds/squirrel v1.5.3
github.com/Masterminds/vcs v1.13.3
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535
github.com/containerd/containerd v1.6.3
github.com/containerd/containerd v1.6.6
github.com/cyphar/filepath-securejoin v0.2.3
github.com/distribution/distribution/v3 v3.0.0-20211118083504-a29a3c99a684
github.com/docker/docker v20.10.14+incompatible
github.com/evanphx/json-patch v4.12.0+incompatible
github.com/distribution/distribution/v3 v3.0.0-20220526142353-ffbd94cbe269
github.com/evanphx/json-patch v5.6.0+incompatible
github.com/gobwas/glob v0.2.3
github.com/gofrs/flock v0.8.1
github.com/gosuri/uitable v0.0.4
github.com/jmoiron/sqlx v1.3.4
github.com/lib/pq v1.10.4
github.com/jmoiron/sqlx v1.3.5
github.com/lib/pq v1.10.6
github.com/mattn/go-shellwords v1.0.12
github.com/mitchellh/copystructure v1.2.0
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5
github.com/pkg/errors v0.9.1
github.com/rubenv/sql-migrate v1.1.1
github.com/rubenv/sql-migrate v1.1.2
github.com/sirupsen/logrus v1.8.1
github.com/spf13/cobra v1.4.0
github.com/spf13/cobra v1.5.0
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.7.1
github.com/stretchr/testify v1.8.0
github.com/xeipuuv/gojsonschema v1.2.0
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
k8s.io/api v0.23.5
k8s.io/apiextensions-apiserver v0.23.5
k8s.io/apimachinery v0.23.5
k8s.io/apiserver v0.23.5
k8s.io/cli-runtime v0.23.5
k8s.io/client-go v0.23.5
k8s.io/klog/v2 v2.30.0
k8s.io/kubectl v0.23.5
oras.land/oras-go v1.1.0
golang.org/x/text v0.3.7
k8s.io/api v0.24.2
k8s.io/apiextensions-apiserver v0.24.2
k8s.io/apimachinery v0.24.2
k8s.io/apiserver v0.24.2
k8s.io/cli-runtime v0.24.2
k8s.io/client-go v0.24.2
k8s.io/klog/v2 v2.60.1
k8s.io/kubectl v0.24.2
oras.land/oras-go v1.2.0
sigs.k8s.io/yaml v1.3.0
)
@ -49,8 +50,8 @@ require (
cloud.google.com/go v0.99.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest v0.11.20 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.15 // indirect
github.com/Azure/go-autorest/autorest v0.11.24 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.18 // indirect
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
@ -66,16 +67,18 @@ require (
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/docker/cli v20.10.11+incompatible // indirect
github.com/docker/distribution v2.7.1+incompatible // indirect
github.com/docker/cli v20.10.17+incompatible // indirect
github.com/docker/distribution v2.8.1+incompatible // indirect
github.com/docker/docker v20.10.17+incompatible // indirect
github.com/docker/docker-credential-helpers v0.6.4 // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
github.com/docker/go-metrics v0.0.1 // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 // indirect
github.com/emicklei/go-restful/v3 v3.8.0 // indirect
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/felixge/httpsnoop v1.0.1 // indirect
@ -86,15 +89,15 @@ require (
github.com/go-openapi/jsonreference v0.19.5 // indirect
github.com/go-openapi/swag v0.19.14 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.0.0 // indirect
github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/gomodule/redigo v1.8.2 // indirect
github.com/google/btree v1.0.1 // indirect
github.com/google/gnostic v0.5.7-v3refs // indirect
github.com/google/go-cmp v0.5.6 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/uuid v1.2.0 // indirect
github.com/googleapis/gnostic v0.5.5 // indirect
github.com/gorilla/handlers v1.5.1 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect
@ -116,18 +119,18 @@ require (
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/locker v1.0.1 // indirect
github.com/moby/spdystream v0.2.0 // indirect
github.com/moby/term v0.0.0-20210610120745-9d4ed1856297 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/onsi/gomega v1.10.3 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/onsi/ginkgo v1.16.4 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.11.1 // indirect
github.com/prometheus/client_golang v1.12.1 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.30.0 // indirect
github.com/prometheus/common v0.32.1 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/russross/blackfriday v1.5.2 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
@ -140,24 +143,23 @@ require (
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 // indirect
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f // indirect
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect
golang.org/x/net v0.0.0-20220107192237-5cfca573fb4d // indirect
golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f // indirect
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368 // indirect
google.golang.org/grpc v1.43.0 // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
k8s.io/component-base v0.23.5 // indirect
k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 // indirect
k8s.io/utils v0.0.0-20211116205334-6203023598ed // indirect
sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 // indirect
sigs.k8s.io/kustomize/api v0.10.1 // indirect
sigs.k8s.io/kustomize/kyaml v0.13.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/component-base v0.24.2 // indirect
k8s.io/kube-openapi v0.0.0-20220627174259-011e075b9cb8 // indirect
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect
sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect
sigs.k8s.io/kustomize/api v0.11.4 // indirect
sigs.k8s.io/kustomize/kyaml v0.13.6 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect
)

729
go.sum

File diff suppressed because it is too large Load Diff

@ -71,7 +71,7 @@ func symwalk(path string, info os.FileInfo, walkFn filepath.WalkFunc) error {
if err != nil {
return errors.Wrapf(err, "error evaluating symlink %s", path)
}
log.Printf("found symbolic link in path: %s resolves to %s", path, resolved)
log.Printf("found symbolic link in path: %s resolves to %s. Contents of linked file included and used", path, resolved)
if info, err = os.Lstat(resolved); err != nil {
return err
}

@ -40,21 +40,12 @@ type HelperT interface {
Helper()
}
// AssertGoldenBytes asserts that the give actual content matches the contents of the given filename
func AssertGoldenBytes(t TestingT, actual []byte, filename string) {
t.Helper()
if err := compare(actual, path(filename)); err != nil {
t.Fatalf("%v", err)
}
}
// AssertGoldenString asserts that the given string matches the contents of the given file.
func AssertGoldenString(t TestingT, actual, filename string) {
t.Helper()
if err := compare([]byte(actual), path(filename)); err != nil {
t.Fatalf("%v", err)
t.Fatalf("%v\n", err)
}
}
@ -66,7 +57,7 @@ func AssertGoldenFile(t TestingT, actualFileName string, expectedFilename string
if err != nil {
t.Fatalf("%v", err)
}
AssertGoldenBytes(t, actual, expectedFilename)
AssertGoldenString(t, string(actual), expectedFilename)
}
func path(filename string) string {
@ -88,7 +79,7 @@ func compare(actual []byte, filename string) error {
}
expected = normalize(expected)
if !bytes.Equal(expected, actual) {
return errors.Errorf("does not match golden file %s\n\nWANT:\n'%s'\n\nGOT:\n'%s'\n", filename, expected, actual)
return errors.Errorf("does not match golden file %s\n\nWANT:\n'%s'\n\nGOT:\n'%s'", filename, expected, actual)
}
return nil
}

@ -29,7 +29,7 @@ var (
//
// Increment major number for new feature additions and behavioral changes.
// Increment minor number for bug fixes and performance enhancements.
version = "v3.8"
version = "v3.9"
// metadata is extra build time data
metadata = ""

@ -59,7 +59,7 @@ func TestList(t *testing.T) {
if err := NewDependency().List(tcase.chart, &buf); err != nil {
t.Fatal(err)
}
test.AssertGoldenBytes(t, buf.Bytes(), tcase.golden)
test.AssertGoldenString(t, buf.String(), tcase.golden)
}
}

@ -712,6 +712,10 @@ func (c *ChartPathOptions) LocateChart(name string, settings *cli.EnvSettings) (
RegistryClient: c.registryClient,
}
if registry.IsOCI(name) {
dl.Options = append(dl.Options, getter.WithRegistryClient(c.registryClient))
}
if c.Verify {
dl.Verify = downloader.VerifyAlways
}
@ -751,20 +755,13 @@ func (c *ChartPathOptions) LocateChart(name string, settings *cli.EnvSettings) (
}
filename, _, err := dl.DownloadTo(name, version, settings.RepositoryCache)
if err == nil {
if err != nil {
return "", err
}
lname, err := filepath.Abs(filename)
if err != nil {
return filename, err
}
return lname, nil
} else if settings.Debug {
return filename, err
}
atVersion := ""
if version != "" {
atVersion = fmt.Sprintf(" at version %q", version)
}
return filename, errors.Errorf("failed to download %q%s", name, atVersion)
}

@ -36,6 +36,7 @@ type Lint struct {
Strict bool
Namespace string
WithSubcharts bool
Quiet bool
}
// LintResult is the result of Lint
@ -75,6 +76,16 @@ func (l *Lint) Run(paths []string, vals map[string]interface{}) *LintResult {
return result
}
// HasWaringsOrErrors checks is LintResult has any warnings or errors
func HasWarningsOrErrors(result *LintResult) bool {
for _, msg := range result.Messages {
if msg.Severity > support.InfoSev {
return true
}
}
return false
}
func lintChart(path string, vals map[string]interface{}, namespace string, strict bool) (support.Linter, error) {
var chartPath string
linter := support.Linter{}

@ -125,6 +125,7 @@ type List struct {
// Filter is a filter that is applied to the results
Filter string
Short bool
NoHeaders bool
TimeFormat string
Uninstalled bool
Superseded bool

@ -113,6 +113,10 @@ func (u *Uninstall) Run(name string) (*release.UninstallReleaseResponse, error)
}
deletedResources, kept, errs := u.deleteRelease(rel)
if errs != nil {
u.cfg.Log("uninstall: Failed to delete release: %s", errs)
return nil, errors.Errorf("failed to delete release: %s", name)
}
if kept != "" {
kept = "These resources were kept due to the resource policy:\n" + kept

@ -391,6 +391,9 @@ func (u *Upgrade) releasingUpgrade(c chan<- resultMessage, upgradedRelease *rele
}
if u.Wait {
u.cfg.Log(
"waiting for release %s resources (created: %d updated: %d deleted: %d)",
upgradedRelease.Name, len(results.Created), len(results.Updated), len(results.Deleted))
if u.WaitForJobs {
if err := u.cfg.KubeClient.WaitWithJobs(target, u.Timeout); err != nil {
u.cfg.recordRelease(originalRelease)

@ -62,8 +62,8 @@ func TestDefaultCapabilities(t *testing.T) {
func TestDefaultCapabilitiesHelmVersion(t *testing.T) {
hv := DefaultCapabilities.HelmVersion
if hv.Version != "v3.8" {
t.Errorf("Expected default HelmVersion to be v3.8, got %q", hv.Version)
if hv.Version != "v3.9" {
t.Errorf("Expected default HelmVersion to be v3.9, got %q", hv.Version)
}
}

@ -68,7 +68,7 @@ func (v Values) Table(name string) (Values, error) {
//
// It protects against nil map panics.
func (v Values) AsMap() map[string]interface{} {
if v == nil || len(v) == 0 {
if len(v) == 0 {
return map[string]interface{}{}
}
return v

@ -30,6 +30,7 @@ import (
"github.com/spf13/pflag"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/client-go/rest"
"helm.sh/helm/v3/pkg/helmpath"
)
@ -37,6 +38,9 @@ import (
// defaultMaxHistory sets the maximum number of releases to 0: unlimited
const defaultMaxHistory = 10
// defaultBurstLimit sets the default client-side throttling limit
const defaultBurstLimit = 100
// EnvSettings describes all of the environment settings.
type EnvSettings struct {
namespace string
@ -56,6 +60,12 @@ type EnvSettings struct {
KubeAPIServer string
// Custom certificate authority file.
KubeCaFile string
// KubeInsecureSkipTLSVerify indicates if server's certificate will not be checked for validity.
// This makes the HTTPS connections insecure
KubeInsecureSkipTLSVerify bool
// KubeTLSServerName overrides the name to use for server certificate validation.
// If it is not provided, the hostname used to contact the server is used
KubeTLSServerName string
// Debug indicates whether or not Helm is running in Debug mode.
Debug bool
// RegistryConfig is the path to the registry config file.
@ -68,6 +78,8 @@ type EnvSettings struct {
PluginsDirectory string
// MaxHistory is the max release history maintained.
MaxHistory int
// BurstLimit is the default client-side throttling limit.
BurstLimit int
}
func New() *EnvSettings {
@ -80,10 +92,13 @@ func New() *EnvSettings {
KubeAsGroups: envCSV("HELM_KUBEASGROUPS"),
KubeAPIServer: os.Getenv("HELM_KUBEAPISERVER"),
KubeCaFile: os.Getenv("HELM_KUBECAFILE"),
KubeTLSServerName: os.Getenv("HELM_KUBETLS_SERVER_NAME"),
KubeInsecureSkipTLSVerify: envBoolOr("HELM_KUBEINSECURE_SKIP_TLS_VERIFY", false),
PluginsDirectory: envOr("HELM_PLUGINS", helmpath.DataPath("plugins")),
RegistryConfig: envOr("HELM_REGISTRY_CONFIG", helmpath.ConfigPath("registry/config.json")),
RepositoryConfig: envOr("HELM_REPOSITORY_CONFIG", helmpath.ConfigPath("repositories.yaml")),
RepositoryCache: envOr("HELM_REPOSITORY_CACHE", helmpath.CachePath("repository")),
BurstLimit: envIntOr("HELM_BURST_LIMIT", defaultBurstLimit),
}
env.Debug, _ = strconv.ParseBool(os.Getenv("HELM_DEBUG"))
@ -96,7 +111,13 @@ func New() *EnvSettings {
CAFile: &env.KubeCaFile,
KubeConfig: &env.KubeConfig,
Impersonate: &env.KubeAsUser,
Insecure: &env.KubeInsecureSkipTLSVerify,
TLSServerName: &env.KubeTLSServerName,
ImpersonateGroup: &env.KubeAsGroups,
WrapConfigFn: func(config *rest.Config) *rest.Config {
config.Burst = env.BurstLimit
return config
},
}
return env
}
@ -111,10 +132,13 @@ func (s *EnvSettings) AddFlags(fs *pflag.FlagSet) {
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.StringVar(&s.KubeCaFile, "kube-ca-file", s.KubeCaFile, "the certificate authority file for the Kubernetes API server connection")
fs.StringVar(&s.KubeTLSServerName, "kube-tls-server-name", s.KubeTLSServerName, "server name to use for Kubernetes API server certificate validation. If it is not provided, the hostname used to contact the server is used")
fs.BoolVar(&s.KubeInsecureSkipTLSVerify, "kube-insecure-skip-tls-verify", s.KubeInsecureSkipTLSVerify, "if true, the Kubernetes API server's certificate will not be checked for validity. This will make your HTTPS connections insecure")
fs.BoolVar(&s.Debug, "debug", s.Debug, "enable verbose output")
fs.StringVar(&s.RegistryConfig, "registry-config", s.RegistryConfig, "path to the registry config file")
fs.StringVar(&s.RepositoryConfig, "repository-config", s.RepositoryConfig, "path to the file containing repository names and URLs")
fs.StringVar(&s.RepositoryCache, "repository-cache", s.RepositoryCache, "path to the file containing cached repository indexes")
fs.IntVar(&s.BurstLimit, "burst-limit", s.BurstLimit, "client-side default throttling limit")
}
func envOr(name, def string) string {
@ -124,6 +148,18 @@ func envOr(name, def string) string {
return def
}
func envBoolOr(name string, def bool) bool {
if name == "" {
return def
}
envVal := envOr(name, strconv.FormatBool(def))
ret, err := strconv.ParseBool(envVal)
if err != nil {
return def
}
return ret
}
func envIntOr(name string, def int) int {
if name == "" {
return def
@ -157,6 +193,7 @@ func (s *EnvSettings) EnvVars() map[string]string {
"HELM_REPOSITORY_CONFIG": s.RepositoryConfig,
"HELM_NAMESPACE": s.Namespace(),
"HELM_MAX_HISTORY": strconv.Itoa(s.MaxHistory),
"HELM_BURST_LIMIT": strconv.Itoa(s.BurstLimit),
// broken, these are populated from helm flags and not kubeconfig.
"HELM_KUBECONTEXT": s.KubeContext,
@ -165,6 +202,8 @@ func (s *EnvSettings) EnvVars() map[string]string {
"HELM_KUBEASGROUPS": strings.Join(s.KubeAsGroups, ","),
"HELM_KUBEAPISERVER": s.KubeAPIServer,
"HELM_KUBECAFILE": s.KubeCaFile,
"HELM_KUBEINSECURE_SKIP_TLS_VERIFY": strconv.FormatBool(s.KubeInsecureSkipTLSVerify),
"HELM_KUBETLS_SERVER_NAME": s.KubeTLSServerName,
}
if s.KubeConfig != "" {
envvars["KUBECONFIG"] = s.KubeConfig

@ -54,42 +54,55 @@ func TestEnvSettings(t *testing.T) {
kubeAsUser string
kubeAsGroups []string
kubeCaFile string
kubeInsecure bool
kubeTLSServer string
burstLimit int
}{
{
name: "defaults",
ns: "default",
maxhistory: defaultMaxHistory,
burstLimit: defaultBurstLimit,
},
{
name: "with flags set",
args: "--debug --namespace=myns --kube-as-user=poro --kube-as-group=admins --kube-as-group=teatime --kube-as-group=snackeaters --kube-ca-file=/tmp/ca.crt",
args: "--debug --namespace=myns --kube-as-user=poro --kube-as-group=admins --kube-as-group=teatime --kube-as-group=snackeaters --kube-ca-file=/tmp/ca.crt --burst-limit 100 --kube-insecure-skip-tls-verify=true --kube-tls-server-name=example.org",
ns: "myns",
debug: true,
maxhistory: defaultMaxHistory,
burstLimit: 100,
kubeAsUser: "poro",
kubeAsGroups: []string{"admins", "teatime", "snackeaters"},
kubeCaFile: "/tmp/ca.crt",
kubeTLSServer: "example.org",
kubeInsecure: true,
},
{
name: "with envvars set",
envvars: map[string]string{"HELM_DEBUG": "1", "HELM_NAMESPACE": "yourns", "HELM_KUBEASUSER": "pikachu", "HELM_KUBEASGROUPS": ",,,operators,snackeaters,partyanimals", "HELM_MAX_HISTORY": "5", "HELM_KUBECAFILE": "/tmp/ca.crt"},
envvars: map[string]string{"HELM_DEBUG": "1", "HELM_NAMESPACE": "yourns", "HELM_KUBEASUSER": "pikachu", "HELM_KUBEASGROUPS": ",,,operators,snackeaters,partyanimals", "HELM_MAX_HISTORY": "5", "HELM_KUBECAFILE": "/tmp/ca.crt", "HELM_BURST_LIMIT": "150", "HELM_KUBEINSECURE_SKIP_TLS_VERIFY": "true", "HELM_KUBETLS_SERVER_NAME": "example.org"},
ns: "yourns",
maxhistory: 5,
burstLimit: 150,
debug: true,
kubeAsUser: "pikachu",
kubeAsGroups: []string{"operators", "snackeaters", "partyanimals"},
kubeCaFile: "/tmp/ca.crt",
kubeTLSServer: "example.org",
kubeInsecure: true,
},
{
name: "with flags and envvars set",
args: "--debug --namespace=myns --kube-as-user=poro --kube-as-group=admins --kube-as-group=teatime --kube-as-group=snackeaters --kube-ca-file=/my/ca.crt",
envvars: map[string]string{"HELM_DEBUG": "1", "HELM_NAMESPACE": "yourns", "HELM_KUBEASUSER": "pikachu", "HELM_KUBEASGROUPS": ",,,operators,snackeaters,partyanimals", "HELM_MAX_HISTORY": "5", "HELM_KUBECAFILE": "/tmp/ca.crt"},
args: "--debug --namespace=myns --kube-as-user=poro --kube-as-group=admins --kube-as-group=teatime --kube-as-group=snackeaters --kube-ca-file=/my/ca.crt --burst-limit 175 --kube-insecure-skip-tls-verify=true --kube-tls-server-name=example.org",
envvars: map[string]string{"HELM_DEBUG": "1", "HELM_NAMESPACE": "yourns", "HELM_KUBEASUSER": "pikachu", "HELM_KUBEASGROUPS": ",,,operators,snackeaters,partyanimals", "HELM_MAX_HISTORY": "5", "HELM_KUBECAFILE": "/tmp/ca.crt", "HELM_BURST_LIMIT": "200", "HELM_KUBEINSECURE_SKIP_TLS_VERIFY": "true", "HELM_KUBETLS_SERVER_NAME": "example.org"},
ns: "myns",
debug: true,
maxhistory: 5,
burstLimit: 175,
kubeAsUser: "poro",
kubeAsGroups: []string{"admins", "teatime", "snackeaters"},
kubeCaFile: "/my/ca.crt",
kubeTLSServer: "example.org",
kubeInsecure: true,
},
}
@ -128,6 +141,92 @@ func TestEnvSettings(t *testing.T) {
if tt.kubeCaFile != settings.KubeCaFile {
t.Errorf("expected kCaFile %q, got %q", tt.kubeCaFile, settings.KubeCaFile)
}
if tt.burstLimit != settings.BurstLimit {
t.Errorf("expected BurstLimit %d, got %d", tt.burstLimit, settings.BurstLimit)
}
if tt.kubeInsecure != settings.KubeInsecureSkipTLSVerify {
t.Errorf("expected kubeInsecure %t, got %t", tt.kubeInsecure, settings.KubeInsecureSkipTLSVerify)
}
if tt.kubeTLSServer != settings.KubeTLSServerName {
t.Errorf("expected kubeTLSServer %q, got %q", tt.kubeTLSServer, settings.KubeTLSServerName)
}
})
}
}
func TestEnvOrBool(t *testing.T) {
const envName = "TEST_ENV_OR_BOOL"
tests := []struct {
name string
env string
val string
def bool
expected bool
}{
{
name: "unset with default false",
def: false,
expected: false,
},
{
name: "unset with default true",
def: true,
expected: true,
},
{
name: "blank env with default false",
env: envName,
def: false,
expected: false,
},
{
name: "blank env with default true",
env: envName,
def: true,
expected: true,
},
{
name: "env true with default false",
env: envName,
val: "true",
def: false,
expected: true,
},
{
name: "env false with default true",
env: envName,
val: "false",
def: true,
expected: false,
},
{
name: "env fails parsing with default true",
env: envName,
val: "NOT_A_BOOL",
def: true,
expected: true,
},
{
name: "env fails parsing with default false",
env: envName,
val: "NOT_A_BOOL",
def: false,
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.env != "" {
t.Cleanup(func() {
os.Unsetenv(tt.env)
})
os.Setenv(tt.env, tt.val)
}
actual := envBoolOr(tt.env, tt.def)
if actual != tt.expected {
t.Errorf("expected result %t, got %t", tt.expected, actual)
}
})
}
}

@ -112,7 +112,10 @@ func readFile(filePath string, p getter.Providers) ([]byte, error) {
if strings.TrimSpace(filePath) == "-" {
return ioutil.ReadAll(os.Stdin)
}
u, _ := url.Parse(filePath)
u, err := url.Parse(filePath)
if err != nil {
return nil, err
}
// FIXME: maybe someone handle other protocols like ftp.
g, err := p.ByScheme(u.Scheme)

@ -19,6 +19,8 @@ package values
import (
"reflect"
"testing"
"helm.sh/helm/v3/pkg/getter"
)
func TestMergeValues(t *testing.T) {
@ -75,3 +77,12 @@ func TestMergeValues(t *testing.T) {
t.Errorf("Expected a map with different keys to merge properly with another map. Expected: %v, got %v", expectedMap, testMap)
}
}
func TestReadFile(t *testing.T) {
var p getter.Providers
filePath := "%a.txt"
_, err := readFile(filePath, p)
if err == nil {
t.Errorf("Expected error when has special strings")
}
}

@ -117,9 +117,9 @@ func TestFuncs(t *testing.T) {
// version of mergo (even accidentally) that causes a breaking change. See
// sprig changelog and notes for more details.
// Note, Go modules assume semver is never broken. So, there is no way to tell
// the tooling to not update to a minor or patch version. `go get -u` could be
// used to accidentally update mergo. This test and message should catch the
// problem and explain why it's happening.
// the tooling to not update to a minor or patch version. `go install` could
// be used to accidentally update mergo. This test and message should catch
// the problem and explain why it's happening.
func TestMerge(t *testing.T) {
dict := map[string]interface{}{
"src2": map[string]interface{}{

@ -63,7 +63,9 @@ func (g *OCIGetter) get(href string) (*bytes.Buffer, error) {
// NewOCIGetter constructs a valid http/https client as a Getter
func NewOCIGetter(ops ...Option) (Getter, error) {
registryClient, err := registry.NewClient()
registryClient, err := registry.NewClient(
registry.ClientOptEnableCache(true),
)
if err != nil {
return nil, err
}

@ -192,7 +192,18 @@ func (c *Client) newBuilder() *resource.Builder {
// Build validates for Kubernetes objects and returns unstructured infos.
func (c *Client) Build(reader io.Reader, validate bool) (ResourceList, error) {
schema, err := c.Factory.Validator(validate)
validationDirective := metav1.FieldValidationIgnore
if validate {
validationDirective = metav1.FieldValidationStrict
}
dynamicClient, err := c.Factory.DynamicClient()
if err != nil {
return nil, err
}
verifier := resource.NewQueryParamVerifier(dynamicClient, c.Factory.OpenAPIGetter(), resource.QueryParamFieldValidation)
schema, err := c.Factory.Validator(validationDirective, verifier)
if err != nil {
return nil, err
}

@ -18,6 +18,8 @@ package kube // import "helm.sh/helm/v3/pkg/kube"
import (
"k8s.io/cli-runtime/pkg/resource"
"k8s.io/client-go/discovery"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/kubectl/pkg/validation"
@ -28,11 +30,19 @@ import (
type Factory interface {
// ToRawKubeConfigLoader return kubeconfig loader as-is
ToRawKubeConfigLoader() clientcmd.ClientConfig
// DynamicClient returns a dynamic client ready for use
DynamicClient() (dynamic.Interface, error)
// KubernetesClientSet gives you back an external clientset
KubernetesClientSet() (*kubernetes.Clientset, error)
// NewBuilder returns an object that assists in loading objects from both disk and the server
// and which implements the common patterns for CLI interactions with generic resources.
NewBuilder() *resource.Builder
// Returns a schema that can validate objects stored on disk.
Validator(validate bool) (validation.Schema, error)
Validator(validationDirective string, verifier *resource.QueryParamVerifier) (validation.Schema, error)
// OpenAPIGetter returns a getter for the openapi schema document
OpenAPIGetter() discovery.OpenAPISchemaInterface
}

@ -387,9 +387,16 @@ func (c *ReadyChecker) statefulSetReady(sts *appsv1.StatefulSet) bool {
// If the update strategy is not a rolling update, there will be nothing to wait for
if sts.Spec.UpdateStrategy.Type != appsv1.RollingUpdateStatefulSetStrategyType {
c.log("StatefulSet skipped ready check: %s/%s. updateStrategy is %v", sts.Namespace, sts.Name, sts.Spec.UpdateStrategy.Type)
return true
}
// Make sure the status is up-to-date with the StatefulSet changes
if sts.Status.ObservedGeneration < sts.Generation {
c.log("StatefulSet is not ready: %s/%s. update has not yet been observed", sts.Namespace, sts.Name)
return false
}
// Dereference all the pointers because StatefulSets like them
var partition int
// 1 is the default for replicas if not set
@ -420,6 +427,13 @@ func (c *ReadyChecker) statefulSetReady(sts *appsv1.StatefulSet) bool {
c.log("StatefulSet is not ready: %s/%s. %d out of %d expected pods are ready", sts.Namespace, sts.Name, sts.Status.ReadyReplicas, replicas)
return false
}
if sts.Status.CurrentRevision != sts.Status.UpdateRevision {
c.log("StatefulSet is not ready: %s/%s. currentRevision %s does not yet match updateRevision %s", sts.Namespace, sts.Name, sts.Status.CurrentRevision, sts.Status.UpdateRevision)
return false
}
c.log("StatefulSet is ready: %s/%s. %d out of %d expected pods are ready", sts.Namespace, sts.Name, sts.Status.ReadyReplicas, replicas)
return true
}

@ -216,7 +216,7 @@ func (s *Signatory) ClearSign(chartpath string) (string, error) {
b, err := messageBlock(chartpath)
if err != nil {
return "", nil
return "", err
}
// Sign the buffer
@ -224,9 +224,24 @@ func (s *Signatory) ClearSign(chartpath string) (string, error) {
if err != nil {
return "", err
}
_, err = io.Copy(w, b)
w.Close()
return out.String(), err
if err != nil {
// NB: We intentionally don't call `w.Close()` here! `w.Close()` is the method which
// actually does the PGP signing, and therefore is the part which uses the private key.
// In other words, if we call Close here, there's a risk that there's an attempt to use the
// private key to sign garbage data (since we know that io.Copy failed, `w` won't contain
// anything useful).
return "", errors.Wrap(err, "failed to write to clearsign encoder")
}
err = w.Close()
if err != nil {
return "", errors.Wrap(err, "failed to either sign or armor message block")
}
return out.String(), nil
}
// Verify checks a signature and verifies that it is legit for a chart.

@ -16,6 +16,9 @@ limitations under the License.
package provenance
import (
"crypto"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
@ -230,6 +233,36 @@ func TestClearSign(t *testing.T) {
}
}
// failSigner always fails to sign and returns an error
type failSigner struct{}
func (s failSigner) Public() crypto.PublicKey {
return nil
}
func (s failSigner) Sign(_ io.Reader, _ []byte, _ crypto.SignerOpts) ([]byte, error) {
return nil, fmt.Errorf("always fails")
}
func TestClearSignError(t *testing.T) {
signer, err := NewFromFiles(testKeyfile, testPubfile)
if err != nil {
t.Fatal(err)
}
// ensure that signing always fails
signer.Entity.PrivateKey.PrivateKey = failSigner{}
sig, err := signer.ClearSign(testChartfile)
if err == nil {
t.Fatal("didn't get an error from ClearSign but expected one")
}
if sig != "" {
t.Fatalf("expected an empty signature after failed ClearSign but got %q", sig)
}
}
func TestDecodeSignature(t *testing.T) {
// Unlike other tests, this does a round-trip test, ensuring that a signature
// generated by the library can also be verified by the library.

@ -85,7 +85,9 @@ func (pusher *OCIPusher) push(chartRef, href string) error {
// NewOCIPusher constructs a valid OCI client as a Pusher
func NewOCIPusher(ops ...Option) (Pusher, error) {
registryClient, err := registry.NewClient()
registryClient, err := registry.NewClient(
registry.ClientOptEnableCache(true),
)
if err != nil {
return nil, err
}

@ -54,6 +54,7 @@ type (
// Client works with OCI-compliant registries
Client struct {
debug bool
enableCache bool
// path to repository config file e.g. ~/.docker/config.json
credentialsFile string
out io.Writer
@ -95,12 +96,18 @@ func NewClient(options ...ClientOption) (*Client, error) {
}
client.resolver = resolver
}
// allocate a cache if option is set
var cache registryauth.Cache
if client.enableCache {
cache = registryauth.DefaultCache
}
if client.registryAuthorizer == nil {
client.registryAuthorizer = &registryauth.Client{
Header: http.Header{
"User-Agent": {version.GetUserAgent()},
},
Cache: registryauth.DefaultCache,
Cache: cache,
Credential: func(ctx context.Context, reg string) (registryauth.Credential, error) {
dockerClient, ok := client.authorizer.(*dockerauth.Client)
if !ok {
@ -138,6 +145,13 @@ func ClientOptDebug(debug bool) ClientOption {
}
}
// ClientOptEnableCache returns a function that sets the enableCache setting on a client options set
func ClientOptEnableCache(enableCache bool) ClientOption {
return func(client *Client) {
client.enableCache = enableCache
}
}
// ClientOptWriter returns a function that sets the writer setting on client options set
func ClientOptWriter(out io.Writer) ClientOption {
return func(client *Client) {

@ -70,6 +70,7 @@ func (suite *RegistryClientTestSuite) SetupSuite() {
var err error
suite.RegistryClient, err = NewClient(
ClientOptDebug(true),
ClientOptEnableCache(true),
ClientOptWriter(suite.Out),
ClientOptCredentialsFile(credentialsFile),
)

@ -24,6 +24,7 @@ import (
"os"
"path/filepath"
"reflect"
"runtime"
"strings"
"testing"
"time"
@ -309,8 +310,15 @@ func TestFindChartInAuthAndTLSAndPassRepoURL(t *testing.T) {
// If the insecureSkipTLsverify is false, it will return an error that contains "x509: certificate signed by unknown authority".
_, err = FindChartInAuthAndTLSAndPassRepoURL(srv.URL, "", "", "nginx", "0.1.0", "", "", "", false, false, getter.All(&cli.EnvSettings{}))
if !strings.Contains(err.Error(), "x509: certificate signed by unknown authority") {
// Go communicates with the platform and different platforms return different messages. Go itself tests darwin
// differently for its message. On newer versions of Darwin the message includes the "Acme Co" portion while older
// versions of Darwin do not. As there are people developing Helm using both old and new versions of Darwin we test
// for both messages.
if runtime.GOOS == "darwin" {
if !strings.Contains(err.Error(), "x509: “Acme Co” certificate is not trusted") && !strings.Contains(err.Error(), "x509: certificate signed by unknown authority") {
t.Errorf("Expected TLS error for function FindChartInAuthAndTLSAndPassRepoURL not found, but got a different error (%v)", err)
}
} else if !strings.Contains(err.Error(), "x509: certificate signed by unknown authority") {
t.Errorf("Expected TLS error for function FindChartInAuthAndTLSAndPassRepoURL not found, but got a different error (%v)", err)
}
}

@ -208,15 +208,16 @@ func TestMerge(t *testing.T) {
if len(ind1.Entries) != 2 {
t.Errorf("Expected 2 entries, got %d", len(ind1.Entries))
}
vs := ind1.Entries["dreadnought"]
if len(vs) != 2 {
t.Errorf("Expected 2 versions, got %d", len(vs))
}
v := vs[0]
if v.Version != "0.2.0" {
if v := vs[1]; v.Version != "0.2.0" {
t.Errorf("Expected %q version to be 0.2.0, got %s", v.Name, v.Version)
}
}
}

@ -153,6 +153,7 @@ func (srv *OCIServer) Run(t *testing.T, opts ...OCIServerOpt) {
// init test client
registryClient, err := ociRegistry.NewClient(
ociRegistry.ClientOptDebug(true),
ociRegistry.ClientOptEnableCache(true),
ociRegistry.ClientOptWriter(os.Stdout),
ociRegistry.ClientOptCredentialsFile(credentialsFile),
)

@ -63,7 +63,7 @@ func decodeRelease(data string) (*rspb.Release, error) {
// For backwards compatibility with releases that were stored before
// compression was introduced we skip decompression if the
// gzip magic header is not found
if bytes.Equal(b[0:3], magicGzip) {
if len(b) > 3 && bytes.Equal(b[0:3], magicGzip) {
r, err := gzip.NewReader(bytes.NewReader(b))
if err != nil {
return nil, err

@ -177,7 +177,7 @@ func (s *Storage) removeLeastRecent(name string, max int) error {
relutil.SortByRevision(h)
lastDeployed, err := s.Deployed(name)
if err != nil {
if err != nil && !errors.Is(err, driver.ErrNoDeployedReleases) {
return err
}

@ -278,8 +278,40 @@ func TestStorageHistory(t *testing.T) {
}
}
func TestStorageRemoveLeastRecentWithError(t *testing.T) {
storage := Init(driver.NewMemory())
var errMaxHistoryMockDriverSomethingHappened = errors.New("something happened")
type MaxHistoryMockDriver struct {
Driver driver.Driver
}
func NewMaxHistoryMockDriver(d driver.Driver) *MaxHistoryMockDriver {
return &MaxHistoryMockDriver{Driver: d}
}
func (d *MaxHistoryMockDriver) Create(key string, rls *rspb.Release) error {
return d.Driver.Create(key, rls)
}
func (d *MaxHistoryMockDriver) Update(key string, rls *rspb.Release) error {
return d.Driver.Update(key, rls)
}
func (d *MaxHistoryMockDriver) Delete(key string) (*rspb.Release, error) {
return nil, errMaxHistoryMockDriverSomethingHappened
}
func (d *MaxHistoryMockDriver) Get(key string) (*rspb.Release, error) {
return d.Driver.Get(key)
}
func (d *MaxHistoryMockDriver) List(filter func(*rspb.Release) bool) ([]*rspb.Release, error) {
return d.Driver.List(filter)
}
func (d *MaxHistoryMockDriver) Query(labels map[string]string) ([]*rspb.Release, error) {
return d.Driver.Query(labels)
}
func (d *MaxHistoryMockDriver) Name() string {
return d.Driver.Name()
}
func TestMaxHistoryErrorHandling(t *testing.T) {
//func TestStorageRemoveLeastRecentWithError(t *testing.T) {
storage := Init(NewMaxHistoryMockDriver(driver.NewMemory()))
storage.Log = t.Logf
storage.MaxHistory = 1
@ -297,7 +329,7 @@ func TestStorageRemoveLeastRecentWithError(t *testing.T) {
setup()
rls2 := ReleaseTestData{Name: name, Version: 2, Status: rspb.StatusSuperseded}.ToRelease()
wantErr := driver.ErrNoDeployedReleases
wantErr := errMaxHistoryMockDriverSomethingHappened
gotErr := storage.Create(rls2)
if !errors.Is(gotErr, wantErr) {
t.Fatalf("Storing release 'angry-bird' (v2) should return the error %#v, but returned %#v", wantErr, gotErr)
@ -444,6 +476,65 @@ func TestStorageLast(t *testing.T) {
}
}
// TestUpgradeInitiallyFailedRelease tests a case when there are no deployed release yet, but history limit has been
// reached: the has-no-deployed-releases error should not occur in such case.
func TestUpgradeInitiallyFailedReleaseWithHistoryLimit(t *testing.T) {
storage := Init(driver.NewMemory())
storage.MaxHistory = 4
const name = "angry-bird"
// setup storage with test releases
setup := func() {
// release records
rls0 := ReleaseTestData{Name: name, Version: 1, Status: rspb.StatusFailed}.ToRelease()
rls1 := ReleaseTestData{Name: name, Version: 2, Status: rspb.StatusFailed}.ToRelease()
rls2 := ReleaseTestData{Name: name, Version: 3, Status: rspb.StatusFailed}.ToRelease()
rls3 := ReleaseTestData{Name: name, Version: 4, Status: rspb.StatusFailed}.ToRelease()
// create the release records in the storage
assertErrNil(t.Fatal, storage.Create(rls0), "Storing release 'angry-bird' (v1)")
assertErrNil(t.Fatal, storage.Create(rls1), "Storing release 'angry-bird' (v2)")
assertErrNil(t.Fatal, storage.Create(rls2), "Storing release 'angry-bird' (v3)")
assertErrNil(t.Fatal, storage.Create(rls3), "Storing release 'angry-bird' (v4)")
hist, err := storage.History(name)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
wantHistoryLen := 4
if len(hist) != wantHistoryLen {
t.Fatalf("expected history of release %q to contain %d releases, got %d", name, wantHistoryLen, len(hist))
}
}
setup()
rls5 := ReleaseTestData{Name: name, Version: 5, Status: rspb.StatusFailed}.ToRelease()
err := storage.Create(rls5)
if err != nil {
t.Fatalf("Failed to create a new release version: %s", err)
}
hist, err := storage.History(name)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
for i, rel := range hist {
wantVersion := i + 2
if rel.Version != wantVersion {
t.Fatalf("Expected history release %d version to equal %d, got %d", i+1, wantVersion, rel.Version)
}
wantStatus := rspb.StatusFailed
if rel.Info.Status != wantStatus {
t.Fatalf("Expected history release %d status to equal %q, got %q", i+1, wantStatus, rel.Info.Status)
}
}
}
type ReleaseTestData struct {
Name string
Version int

@ -21,7 +21,7 @@ coverdir=$(mktemp -d /tmp/coverage.XXXXXXXXXX)
profile="${coverdir}/cover.out"
pushd /
hash goveralls 2>/dev/null || go get github.com/mattn/goveralls
hash goveralls 2>/dev/null || go install github.com/mattn/goveralls@v0.0.11
popd
generate_cover_data() {

Loading…
Cancel
Save