diff options
| author | Shelley-BaoYue <baoyue2@huawei.com> | 2023-05-25 17:23:57 +0800 |
|---|---|---|
| committer | fisherxu <xufei40@huawei.com> | 2023-07-01 03:57:26 +0800 |
| commit | 95bc4f55be0c27fd3fa4a9433eb689979dd2f913 (patch) | |
| tree | d3eb7e3c77a64e21565eeb971f0783aa799cbcaf /vendor/helm.sh | |
| parent | Merge pull request #4734 from wenchajun/kubectl-attach (diff) | |
| download | kubeedge-95bc4f55be0c27fd3fa4a9433eb689979dd2f913.tar.gz | |
update k8s version to 1.24
Signed-off-by: Shelley-BaoYue <baoyue2@huawei.com>
Diffstat (limited to 'vendor/helm.sh')
47 files changed, 1115 insertions, 270 deletions
diff --git a/vendor/helm.sh/helm/v3/internal/experimental/registry/util.go b/vendor/helm.sh/helm/v3/internal/experimental/registry/util.go deleted file mode 100644 index 257e7af87..000000000 --- a/vendor/helm.sh/helm/v3/internal/experimental/registry/util.go +++ /dev/null @@ -1,56 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package registry // import "helm.sh/helm/v3/internal/experimental/registry" - -import ( - "bytes" - "context" - "fmt" - "io" - "strings" - - "github.com/sirupsen/logrus" - orascontext "oras.land/oras-go/pkg/context" - - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chart/loader" -) - -// IsOCI determines whether or not a URL is to be treated as an OCI URL -func IsOCI(url string) bool { - return strings.HasPrefix(url, fmt.Sprintf("%s://", OCIScheme)) -} - -// extractChartMeta is used to extract a chart metadata from a byte array -func extractChartMeta(chartData []byte) (*chart.Metadata, error) { - ch, err := loader.LoadArchive(bytes.NewReader(chartData)) - if err != nil { - return nil, err - } - return ch.Metadata, nil -} - -// ctx retrieves a fresh context. -// disable verbose logging coming from ORAS (unless debug is enabled) -func ctx(out io.Writer, debug bool) context.Context { - if !debug { - return orascontext.Background() - } - ctx := orascontext.WithLoggerFromWriter(context.Background(), out) - orascontext.GetLogger(ctx).Logger.SetLevel(logrus.DebugLevel) - return ctx -} diff --git a/vendor/helm.sh/helm/v3/internal/resolver/resolver.go b/vendor/helm.sh/helm/v3/internal/resolver/resolver.go index 70ce6a55b..5e8921f96 100644 --- a/vendor/helm.sh/helm/v3/internal/resolver/resolver.go +++ b/vendor/helm.sh/helm/v3/internal/resolver/resolver.go @@ -18,6 +18,7 @@ package resolver import ( "bytes" "encoding/json" + "fmt" "os" "path/filepath" "strings" @@ -26,28 +27,27 @@ import ( "github.com/Masterminds/semver/v3" "github.com/pkg/errors" - "helm.sh/helm/v3/internal/experimental/registry" "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/chart/loader" - "helm.sh/helm/v3/pkg/gates" "helm.sh/helm/v3/pkg/helmpath" "helm.sh/helm/v3/pkg/provenance" + "helm.sh/helm/v3/pkg/registry" "helm.sh/helm/v3/pkg/repo" ) -const FeatureGateOCI = gates.Gate("HELM_EXPERIMENTAL_OCI") - // Resolver resolves dependencies from semantic version ranges to a particular version. type Resolver struct { - chartpath string - cachepath string + chartpath string + cachepath string + registryClient *registry.Client } -// New creates a new resolver for a given chart and a given helm home. -func New(chartpath, cachepath string) *Resolver { +// New creates a new resolver for a given chart, helm home and registry client. +func New(chartpath, cachepath string, registryClient *registry.Client) *Resolver { return &Resolver{ - chartpath: chartpath, - cachepath: cachepath, + chartpath: chartpath, + cachepath: cachepath, + registryClient: registryClient, } } @@ -135,9 +135,36 @@ func (r *Resolver) Resolve(reqs []*chart.Dependency, repoNames map[string]string found = false } else { version = d.Version - if !FeatureGateOCI.IsEnabled() { - return nil, errors.Wrapf(FeatureGateOCI.Error(), - "repository %s is an OCI registry", d.Repository) + + // Check to see if an explicit version has been provided + _, err := semver.NewVersion(version) + + // Use an explicit version, otherwise search for tags + if err == nil { + vs = []*repo.ChartVersion{{ + Metadata: &chart.Metadata{ + Version: version, + }, + }} + + } else { + // Retrieve list of tags for repository + ref := fmt.Sprintf("%s/%s", strings.TrimPrefix(d.Repository, fmt.Sprintf("%s://", registry.OCIScheme)), d.Name) + tags, err := r.registryClient.Tags(ref) + if err != nil { + return nil, errors.Wrapf(err, "could not retrieve list of tags for repository %s", d.Repository) + } + + vs = make(repo.ChartVersions, len(tags)) + for ti, t := range tags { + // Mock chart version objects + version := &repo.ChartVersion{ + Metadata: &chart.Metadata{ + Version: t, + }, + } + vs[ti] = version + } } } @@ -149,7 +176,8 @@ func (r *Resolver) Resolve(reqs []*chart.Dependency, repoNames map[string]string // The version are already sorted and hence the first one to satisfy the constraint is used for _, ver := range vs { v, err := semver.NewVersion(ver.Version) - if err != nil || len(ver.URLs) == 0 { + // OCI does not need URLs + if err != nil || (!registry.IsOCI(d.Repository) && len(ver.URLs) == 0) { // Not a legit entry. continue } diff --git a/vendor/helm.sh/helm/v3/internal/sympath/walk.go b/vendor/helm.sh/helm/v3/internal/sympath/walk.go index 752526fe9..a276cfeff 100644 --- a/vendor/helm.sh/helm/v3/internal/sympath/walk.go +++ b/vendor/helm.sh/helm/v3/internal/sympath/walk.go @@ -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 } diff --git a/vendor/helm.sh/helm/v3/internal/third_party/dep/fs/rename.go b/vendor/helm.sh/helm/v3/internal/third_party/dep/fs/rename.go index 0bb600949..a3e5e56a6 100644 --- a/vendor/helm.sh/helm/v3/internal/third_party/dep/fs/rename.go +++ b/vendor/helm.sh/helm/v3/internal/third_party/dep/fs/rename.go @@ -1,4 +1,4 @@ -// +build !windows +//go:build !windows /* Copyright (c) for portions of rename.go are held by The Go Authors, 2016 and are provided under diff --git a/vendor/helm.sh/helm/v3/internal/third_party/dep/fs/rename_windows.go b/vendor/helm.sh/helm/v3/internal/third_party/dep/fs/rename_windows.go index 14f017d09..a377720a6 100644 --- a/vendor/helm.sh/helm/v3/internal/third_party/dep/fs/rename_windows.go +++ b/vendor/helm.sh/helm/v3/internal/third_party/dep/fs/rename_windows.go @@ -1,4 +1,4 @@ -// +build windows +//go:build windows /* Copyright (c) for portions of rename_windows.go are held by The Go Authors, 2016 and are provided under diff --git a/vendor/helm.sh/helm/v3/internal/version/version.go b/vendor/helm.sh/helm/v3/internal/version/version.go index 201eee359..6919e29d8 100644 --- a/vendor/helm.sh/helm/v3/internal/version/version.go +++ b/vendor/helm.sh/helm/v3/internal/version/version.go @@ -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.7" + version = "v3.9" // metadata is extra build time data metadata = "" diff --git a/vendor/helm.sh/helm/v3/pkg/action/action.go b/vendor/helm.sh/helm/v3/pkg/action/action.go index f093ed7f8..82760250f 100644 --- a/vendor/helm.sh/helm/v3/pkg/action/action.go +++ b/vendor/helm.sh/helm/v3/pkg/action/action.go @@ -32,12 +32,12 @@ import ( "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" - "helm.sh/helm/v3/internal/experimental/registry" "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/chartutil" "helm.sh/helm/v3/pkg/engine" "helm.sh/helm/v3/pkg/kube" "helm.sh/helm/v3/pkg/postrender" + "helm.sh/helm/v3/pkg/registry" "helm.sh/helm/v3/pkg/release" "helm.sh/helm/v3/pkg/releaseutil" "helm.sh/helm/v3/pkg/storage" @@ -272,6 +272,7 @@ func (cfg *Configuration) getCapabilities() (*chartutil.Capabilities, error) { Major: kubeVersion.Major, Minor: kubeVersion.Minor, }, + HelmVersion: chartutil.DefaultCapabilities.HelmVersion, } return cfg.Capabilities, nil } diff --git a/vendor/helm.sh/helm/v3/pkg/action/install.go b/vendor/helm.sh/helm/v3/pkg/action/install.go index b84a57271..3872ed5c9 100644 --- a/vendor/helm.sh/helm/v3/pkg/action/install.go +++ b/vendor/helm.sh/helm/v3/pkg/action/install.go @@ -38,7 +38,6 @@ import ( "k8s.io/cli-runtime/pkg/resource" "sigs.k8s.io/yaml" - "helm.sh/helm/v3/internal/experimental/registry" "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/chartutil" "helm.sh/helm/v3/pkg/cli" @@ -47,6 +46,7 @@ import ( "helm.sh/helm/v3/pkg/kube" kubefake "helm.sh/helm/v3/pkg/kube/fake" "helm.sh/helm/v3/pkg/postrender" + "helm.sh/helm/v3/pkg/registry" "helm.sh/helm/v3/pkg/release" "helm.sh/helm/v3/pkg/releaseutil" "helm.sh/helm/v3/pkg/repo" @@ -54,13 +54,6 @@ import ( "helm.sh/helm/v3/pkg/storage/driver" ) -// releaseNameMaxLen is the maximum length of a release name. -// -// As of Kubernetes 1.4, the max limit on a name is 63 chars. We reserve 10 for -// charts to add data. Effectively, that gives us 53 chars. -// See https://github.com/helm/helm/issues/1528 -const releaseNameMaxLen = 53 - // NOTESFILE_SUFFIX that we want to treat special. It goes through the templating engine // but it's not a yaml file (resource) hence can't have hooks, etc. And the user actually // wants to see this file after rendering in the status command. However, it must be a suffix @@ -124,13 +117,20 @@ type ChartPathOptions struct { Username string // --username Verify bool // --verify Version string // --version + + // registryClient provides a registry client but is not added with + // options from a flag + registryClient *registry.Client } // NewInstall creates a new Install object with the given configuration. func NewInstall(cfg *Configuration) *Install { - return &Install{ + in := &Install{ cfg: cfg, } + in.ChartPathOptions.registryClient = cfg.RegistryClient + + return in } func (i *Install) installCRDs(crds []chart.CRD) error { @@ -198,6 +198,10 @@ func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals ma return nil, err } + if err := chartutil.ProcessDependencies(chrt, vals); err != nil { + return nil, err + } + // Pre-install anything in the crd/ directory. We do this before Helm // contacts the upstream server and builds the capabilities object. if crds := chrt.CRDObjects(); !i.ClientOnly && !i.SkipCRDs && len(crds) > 0 { @@ -226,10 +230,6 @@ func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals ma i.cfg.Log("API Version list given outside of client only mode, this list will be ignored") } - if err := chartutil.ProcessDependencies(chrt, vals); err != nil { - return nil, err - } - // Make sure if Atomic is set, that wait is set as well. This makes it so // the user doesn't have to specify both i.Wait = i.Wait || i.Atomic @@ -344,8 +344,10 @@ func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals ma return rel, err } rChan := make(chan resultMessage) + doneChan := make(chan struct{}) + defer close(doneChan) go i.performInstall(rChan, rel, toBeAdopted, resources) - go i.handleContext(ctx, rChan, rel) + go i.handleContext(ctx, rChan, doneChan, rel) result := <-rChan //start preformInstall go routine return result.r, result.e @@ -416,12 +418,14 @@ func (i *Install) performInstall(c chan<- resultMessage, rel *release.Release, t i.reportToRun(c, rel, nil) } -func (i *Install) handleContext(ctx context.Context, c chan<- resultMessage, rel *release.Release) { - go func() { - <-ctx.Done() +func (i *Install) handleContext(ctx context.Context, c chan<- resultMessage, done chan struct{}, rel *release.Release) { + select { + case <-ctx.Done(): err := ctx.Err() i.reportToRun(c, rel, err) - }() + case <-done: + return + } } func (i *Install) reportToRun(c chan<- resultMessage, rel *release.Release, err error) { i.Lock.Lock() @@ -458,14 +462,10 @@ func (i *Install) failRelease(rel *release.Release, err error) (*release.Release // - used by a deleted release, and i.Replace is false func (i *Install) availableName() error { start := i.ReleaseName - if start == "" { - return errors.New("name is required") - } - if len(start) > releaseNameMaxLen { - return errors.Errorf("release name %q exceeds max length of %d", start, releaseNameMaxLen) + if err := chartutil.ValidateReleaseName(start); err != nil { + return errors.Wrapf(err, "release name %q", start) } - if i.DryRun { return nil } @@ -673,6 +673,12 @@ OUTER: // // If 'verify' was set on ChartPathOptions, this will attempt to also verify the chart. func (c *ChartPathOptions) LocateChart(name string, settings *cli.EnvSettings) (string, error) { + // If there is no registry client and the name is in an OCI registry return + // an error and a lookup will not occur. + if registry.IsOCI(name) && c.registryClient == nil { + return "", fmt.Errorf("unable to lookup chart %q, missing registry client", name) + } + name = strings.TrimSpace(name) version := strings.TrimSpace(c.Version) @@ -703,13 +709,7 @@ func (c *ChartPathOptions) LocateChart(name string, settings *cli.EnvSettings) ( }, RepositoryConfig: settings.RepositoryConfig, RepositoryCache: settings.RepositoryCache, - } - - if registry.IsOCI(name) { - if version == "" { - return "", errors.New("version is explicitly required for OCI registries") - } - dl.Options = append(dl.Options, getter.WithTagName(version)) + RegistryClient: c.registryClient, } if c.Verify { diff --git a/vendor/helm.sh/helm/v3/pkg/action/lint.go b/vendor/helm.sh/helm/v3/pkg/action/lint.go index bdb93dcc2..5b566e9d3 100644 --- a/vendor/helm.sh/helm/v3/pkg/action/lint.go +++ b/vendor/helm.sh/helm/v3/pkg/action/lint.go @@ -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{} diff --git a/vendor/helm.sh/helm/v3/pkg/action/pull.go b/vendor/helm.sh/helm/v3/pkg/action/pull.go index 2f5127ea9..b4018869e 100644 --- a/vendor/helm.sh/helm/v3/pkg/action/pull.go +++ b/vendor/helm.sh/helm/v3/pkg/action/pull.go @@ -25,11 +25,11 @@ import ( "github.com/pkg/errors" - "helm.sh/helm/v3/internal/experimental/registry" "helm.sh/helm/v3/pkg/chartutil" "helm.sh/helm/v3/pkg/cli" "helm.sh/helm/v3/pkg/downloader" "helm.sh/helm/v3/pkg/getter" + "helm.sh/helm/v3/pkg/registry" "helm.sh/helm/v3/pkg/repo" ) @@ -87,18 +87,14 @@ func (p *Pull) Run(chartRef string) (string, error) { getter.WithTLSClientConfig(p.CertFile, p.KeyFile, p.CaFile), getter.WithInsecureSkipVerifyTLS(p.InsecureSkipTLSverify), }, + RegistryClient: p.cfg.RegistryClient, RepositoryConfig: p.Settings.RepositoryConfig, RepositoryCache: p.Settings.RepositoryCache, } if registry.IsOCI(chartRef) { - if p.Version == "" { - return out.String(), errors.Errorf("--version flag is explicitly required for OCI registries") - } - c.Options = append(c.Options, - getter.WithRegistryClient(p.cfg.RegistryClient), - getter.WithTagName(p.Version)) + getter.WithRegistryClient(p.cfg.RegistryClient)) } if p.Verify { diff --git a/vendor/helm.sh/helm/v3/pkg/action/push.go b/vendor/helm.sh/helm/v3/pkg/action/push.go new file mode 100644 index 000000000..99d1beadc --- /dev/null +++ b/vendor/helm.sh/helm/v3/pkg/action/push.go @@ -0,0 +1,70 @@ +/* +Copyright The Helm Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package action + +import ( + "strings" + + "helm.sh/helm/v3/pkg/cli" + "helm.sh/helm/v3/pkg/pusher" + "helm.sh/helm/v3/pkg/registry" + "helm.sh/helm/v3/pkg/uploader" +) + +// Push is the action for uploading a chart. +// +// It provides the implementation of 'helm push'. +type Push struct { + Settings *cli.EnvSettings + cfg *Configuration +} + +// PushOpt is a type of function that sets options for a push action. +type PushOpt func(*Push) + +// WithPushConfig sets the cfg field on the push configuration object. +func WithPushConfig(cfg *Configuration) PushOpt { + return func(p *Push) { + p.cfg = cfg + } +} + +// NewPushWithOpts creates a new push, with configuration options. +func NewPushWithOpts(opts ...PushOpt) *Push { + p := &Push{} + for _, fn := range opts { + fn(p) + } + return p +} + +// Run executes 'helm push' against the given chart archive. +func (p *Push) Run(chartRef string, remote string) (string, error) { + var out strings.Builder + + c := uploader.ChartUploader{ + Out: &out, + Pushers: pusher.All(p.Settings), + Options: []pusher.Option{}, + } + + if registry.IsOCI(remote) { + c.Options = append(c.Options, pusher.WithRegistryClient(p.cfg.RegistryClient)) + } + + return out.String(), c.UploadTo(chartRef, remote) +} diff --git a/vendor/helm.sh/helm/v3/pkg/action/registry_login.go b/vendor/helm.sh/helm/v3/pkg/action/registry_login.go new file mode 100644 index 000000000..68bcc7442 --- /dev/null +++ b/vendor/helm.sh/helm/v3/pkg/action/registry_login.go @@ -0,0 +1,43 @@ +/* +Copyright The Helm Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package action + +import ( + "io" + + "helm.sh/helm/v3/pkg/registry" +) + +// RegistryLogin performs a registry login operation. +type RegistryLogin struct { + cfg *Configuration +} + +// NewRegistryLogin creates a new RegistryLogin object with the given configuration. +func NewRegistryLogin(cfg *Configuration) *RegistryLogin { + return &RegistryLogin{ + cfg: cfg, + } +} + +// Run executes the registry login operation +func (a *RegistryLogin) Run(out io.Writer, hostname string, username string, password string, insecure bool) error { + return a.cfg.RegistryClient.Login( + hostname, + registry.LoginOptBasicAuth(username, password), + registry.LoginOptInsecure(insecure)) +} diff --git a/vendor/helm.sh/helm/v3/pkg/action/registry_logout.go b/vendor/helm.sh/helm/v3/pkg/action/registry_logout.go new file mode 100644 index 000000000..69add4163 --- /dev/null +++ b/vendor/helm.sh/helm/v3/pkg/action/registry_logout.go @@ -0,0 +1,38 @@ +/* +Copyright The Helm Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package action + +import ( + "io" +) + +// RegistryLogout performs a registry login operation. +type RegistryLogout struct { + cfg *Configuration +} + +// NewRegistryLogout creates a new RegistryLogout object with the given configuration. +func NewRegistryLogout(cfg *Configuration) *RegistryLogout { + return &RegistryLogout{ + cfg: cfg, + } +} + +// Run executes the registry logout operation +func (a *RegistryLogout) Run(out io.Writer, hostname string) error { + return a.cfg.RegistryClient.Logout(hostname) +} diff --git a/vendor/helm.sh/helm/v3/pkg/action/rollback.go b/vendor/helm.sh/helm/v3/pkg/action/rollback.go index f3f958f3d..dda8c700b 100644 --- a/vendor/helm.sh/helm/v3/pkg/action/rollback.go +++ b/vendor/helm.sh/helm/v3/pkg/action/rollback.go @@ -164,6 +164,11 @@ func (r *Rollback) performRollback(currentRelease, targetRelease *release.Releas r.cfg.Log("rollback hooks disabled for %s", targetRelease.Name) } + // It is safe to use "force" here because these are resources currently rendered by the chart. + err = target.Visit(setMetadataVisitor(targetRelease.Name, targetRelease.Namespace, true)) + if err != nil { + return targetRelease, errors.Wrap(err, "unable to set metadata visitor from target release") + } results, err := r.cfg.KubeClient.Update(current, target, r.Force) if err != nil { diff --git a/vendor/helm.sh/helm/v3/pkg/action/show.go b/vendor/helm.sh/helm/v3/pkg/action/show.go index 1e3da3bdc..9ba85234d 100644 --- a/vendor/helm.sh/helm/v3/pkg/action/show.go +++ b/vendor/helm.sh/helm/v3/pkg/action/show.go @@ -64,12 +64,24 @@ type Show struct { } // NewShow creates a new Show object with the given configuration. +// Deprecated: Use NewShowWithConfig +// TODO Helm 4: Fold NewShowWithConfig back into NewShow func NewShow(output ShowOutputFormat) *Show { return &Show{ OutputFormat: output, } } +// NewShowWithConfig creates a new Show object with the given configuration. +func NewShowWithConfig(output ShowOutputFormat, cfg *Configuration) *Show { + sh := &Show{ + OutputFormat: output, + } + sh.ChartPathOptions.registryClient = cfg.RegistryClient + + return sh +} + // Run executes 'helm show' against the given release. func (s *Show) Run(chartpath string) (string, error) { if s.chart == nil { diff --git a/vendor/helm.sh/helm/v3/pkg/action/upgrade.go b/vendor/helm.sh/helm/v3/pkg/action/upgrade.go index 27c1f01e7..690397d4a 100644 --- a/vendor/helm.sh/helm/v3/pkg/action/upgrade.go +++ b/vendor/helm.sh/helm/v3/pkg/action/upgrade.go @@ -112,18 +112,21 @@ type resultMessage struct { // NewUpgrade creates a new Upgrade object with the given configuration. func NewUpgrade(cfg *Configuration) *Upgrade { - return &Upgrade{ + up := &Upgrade{ cfg: cfg, } + up.ChartPathOptions.registryClient = cfg.RegistryClient + + return up } -// Run executes the upgrade on the given release +// Run executes the upgrade on the given release. func (u *Upgrade) Run(name string, chart *chart.Chart, vals map[string]interface{}) (*release.Release, error) { ctx := context.Background() return u.RunWithContext(ctx, name, chart, vals) } -// Run executes the upgrade on the given release with context. +// RunWithContext executes the upgrade on the given release with context. func (u *Upgrade) RunWithContext(ctx context.Context, name string, chart *chart.Chart, vals map[string]interface{}) (*release.Release, error) { if err := u.cfg.KubeClient.IsReachable(); err != nil { return nil, err @@ -323,11 +326,11 @@ func (u *Upgrade) performUpgrade(ctx context.Context, originalRelease, upgradedR rChan := make(chan resultMessage) ctxChan := make(chan resultMessage) doneChan := make(chan interface{}) + defer close(doneChan) go u.releasingUpgrade(rChan, upgradedRelease, current, target, originalRelease) go u.handleContext(ctx, doneChan, ctxChan, upgradedRelease) select { case result := <-rChan: - doneChan <- true return result.r, result.e case result := <-ctxChan: return result.r, result.e @@ -348,17 +351,15 @@ func (u *Upgrade) reportToPerformUpgrade(c chan<- resultMessage, rel *release.Re // Setup listener for SIGINT and SIGTERM func (u *Upgrade) handleContext(ctx context.Context, done chan interface{}, c chan<- resultMessage, upgradedRelease *release.Release) { - go func() { - select { - case <-ctx.Done(): - err := ctx.Err() + select { + case <-ctx.Done(): + err := ctx.Err() - // when the atomic flag is set the ongoing release finish first and doesn't give time for the rollback happens. - u.reportToPerformUpgrade(c, upgradedRelease, kube.ResourceList{}, err) - case <-done: - return - } - }() + // when the atomic flag is set the ongoing release finish first and doesn't give time for the rollback happens. + u.reportToPerformUpgrade(c, upgradedRelease, kube.ResourceList{}, err) + case <-done: + return + } } func (u *Upgrade) releasingUpgrade(c chan<- resultMessage, upgradedRelease *release.Release, current kube.ResourceList, target kube.ResourceList, originalRelease *release.Release) { // pre-upgrade hooks @@ -390,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) diff --git a/vendor/helm.sh/helm/v3/pkg/action/validate.go b/vendor/helm.sh/helm/v3/pkg/action/validate.go index 6e074f78b..73eb1937b 100644 --- a/vendor/helm.sh/helm/v3/pkg/action/validate.go +++ b/vendor/helm.sh/helm/v3/pkg/action/validate.go @@ -51,7 +51,7 @@ func existingResourceConflict(resources kube.ResourceList, releaseName, releaseN if apierrors.IsNotFound(err) { return nil } - return errors.Wrap(err, "could not get information about the resource") + return errors.Wrapf(err, "could not get information about the resource %s", resourceString(info)) } // Allow adoption of the resource if it is managed by Helm and is annotated with correct release name and namespace. diff --git a/vendor/helm.sh/helm/v3/pkg/chartutil/coalesce.go b/vendor/helm.sh/helm/v3/pkg/chartutil/coalesce.go index b49a31b01..f634d6425 100644 --- a/vendor/helm.sh/helm/v3/pkg/chartutil/coalesce.go +++ b/vendor/helm.sh/helm/v3/pkg/chartutil/coalesce.go @@ -17,6 +17,7 @@ limitations under the License. package chartutil import ( + "fmt" "log" "github.com/mitchellh/copystructure" @@ -25,6 +26,13 @@ import ( "helm.sh/helm/v3/pkg/chart" ) +func concatPrefix(a, b string) string { + if a == "" { + return b + } + return fmt.Sprintf("%s.%s", a, b) +} + // CoalesceValues coalesces all of the values in a chart (and its subcharts). // // Values are coalesced together using the following rules: @@ -45,19 +53,21 @@ func CoalesceValues(chrt *chart.Chart, vals map[string]interface{}) (Values, err if valsCopy == nil { valsCopy = make(map[string]interface{}) } - return coalesce(chrt, valsCopy) + return coalesce(log.Printf, chrt, valsCopy, "") } +type printFn func(format string, v ...interface{}) + // coalesce coalesces the dest values and the chart values, giving priority to the dest values. // // This is a helper function for CoalesceValues. -func coalesce(ch *chart.Chart, dest map[string]interface{}) (map[string]interface{}, error) { - coalesceValues(ch, dest) - return coalesceDeps(ch, dest) +func coalesce(printf printFn, ch *chart.Chart, dest map[string]interface{}, prefix string) (map[string]interface{}, error) { + coalesceValues(printf, ch, dest, prefix) + return coalesceDeps(printf, ch, dest, prefix) } // coalesceDeps coalesces the dependencies of the given chart. -func coalesceDeps(chrt *chart.Chart, dest map[string]interface{}) (map[string]interface{}, error) { +func coalesceDeps(printf printFn, chrt *chart.Chart, dest map[string]interface{}, prefix string) (map[string]interface{}, error) { for _, subchart := range chrt.Dependencies() { if c, ok := dest[subchart.Name()]; !ok { // If dest doesn't already have the key, create it. @@ -67,13 +77,14 @@ func coalesceDeps(chrt *chart.Chart, dest map[string]interface{}) (map[string]in } if dv, ok := dest[subchart.Name()]; ok { dvmap := dv.(map[string]interface{}) + subPrefix := concatPrefix(prefix, chrt.Metadata.Name) // Get globals out of dest and merge them into dvmap. - coalesceGlobals(dvmap, dest) + coalesceGlobals(printf, dvmap, dest, subPrefix) // Now coalesce the rest of the values. var err error - dest[subchart.Name()], err = coalesce(subchart, dvmap) + dest[subchart.Name()], err = coalesce(printf, subchart, dvmap, subPrefix) if err != nil { return dest, err } @@ -85,20 +96,20 @@ func coalesceDeps(chrt *chart.Chart, dest map[string]interface{}) (map[string]in // coalesceGlobals copies the globals out of src and merges them into dest. // // For convenience, returns dest. -func coalesceGlobals(dest, src map[string]interface{}) { +func coalesceGlobals(printf printFn, dest, src map[string]interface{}, prefix string) { var dg, sg map[string]interface{} if destglob, ok := dest[GlobalKey]; !ok { dg = make(map[string]interface{}) } else if dg, ok = destglob.(map[string]interface{}); !ok { - log.Printf("warning: skipping globals because destination %s is not a table.", GlobalKey) + printf("warning: skipping globals because destination %s is not a table.", GlobalKey) return } if srcglob, ok := src[GlobalKey]; !ok { sg = make(map[string]interface{}) } else if sg, ok = srcglob.(map[string]interface{}); !ok { - log.Printf("warning: skipping globals because source %s is not a table.", GlobalKey) + printf("warning: skipping globals because source %s is not a table.", GlobalKey) return } @@ -114,17 +125,18 @@ func coalesceGlobals(dest, src map[string]interface{}) { dg[key] = vv } else { if destvmap, ok := destv.(map[string]interface{}); !ok { - log.Printf("Conflict: cannot merge map onto non-map for %q. Skipping.", key) + printf("Conflict: cannot merge map onto non-map for %q. Skipping.", key) } else { // Basically, we reverse order of coalesce here to merge // top-down. - CoalesceTables(vv, destvmap) + subPrefix := concatPrefix(prefix, key) + coalesceTablesFullKey(printf, vv, destvmap, subPrefix) dg[key] = vv } } } else if dv, ok := dg[key]; ok && istable(dv) { // It's not clear if this condition can actually ever trigger. - log.Printf("key %s is table. Skipping", key) + printf("key %s is table. Skipping", key) } else { // TODO: Do we need to do any additional checking on the value? dg[key] = val @@ -144,7 +156,8 @@ func copyMap(src map[string]interface{}) map[string]interface{} { // coalesceValues builds up a values map for a particular chart. // // Values in v will override the values in the chart. -func coalesceValues(c *chart.Chart, v map[string]interface{}) { +func coalesceValues(printf printFn, c *chart.Chart, v map[string]interface{}, prefix string) { + subPrefix := concatPrefix(prefix, c.Metadata.Name) for key, val := range c.Values { if value, ok := v[key]; ok { if value == nil { @@ -159,12 +172,12 @@ func coalesceValues(c *chart.Chart, v map[string]interface{}) { // If the original value is nil, there is nothing to coalesce, so we don't print // the warning if val != nil { - log.Printf("warning: skipped value for %s: Not a table.", key) + printf("warning: skipped value for %s.%s: Not a table.", subPrefix, key) } } else { // Because v has higher precedence than nv, dest values override src // values. - CoalesceTables(dest, src) + coalesceTablesFullKey(printf, dest, src, concatPrefix(subPrefix, key)) } } } else { @@ -178,6 +191,13 @@ func coalesceValues(c *chart.Chart, v map[string]interface{}) { // // dest is considered authoritative. func CoalesceTables(dst, src map[string]interface{}) map[string]interface{} { + return coalesceTablesFullKey(log.Printf, dst, src, "") +} + +// coalesceTablesFullKey merges a source map into a destination map. +// +// dest is considered authoritative. +func coalesceTablesFullKey(printf printFn, dst, src map[string]interface{}, prefix string) map[string]interface{} { // When --reuse-values is set but there are no modifications yet, return new values if src == nil { return dst @@ -188,18 +208,19 @@ func CoalesceTables(dst, src map[string]interface{}) map[string]interface{} { // Because dest has higher precedence than src, dest values override src // values. for key, val := range src { + fullkey := concatPrefix(prefix, key) if dv, ok := dst[key]; ok && dv == nil { delete(dst, key) } else if !ok { dst[key] = val } else if istable(val) { if istable(dv) { - CoalesceTables(dv.(map[string]interface{}), val.(map[string]interface{})) + coalesceTablesFullKey(printf, dv.(map[string]interface{}), val.(map[string]interface{}), fullkey) } else { - log.Printf("warning: cannot overwrite table with non table for %s (%v)", key, val) + printf("warning: cannot overwrite table with non table for %s (%v)", fullkey, val) } } else if istable(dv) && val != nil { - log.Printf("warning: destination for %s is a table. Ignoring non-table value %v", key, val) + printf("warning: destination for %s is a table. Ignoring non-table value (%v)", fullkey, val) } } return dst diff --git a/vendor/helm.sh/helm/v3/pkg/chartutil/dependencies.go b/vendor/helm.sh/helm/v3/pkg/chartutil/dependencies.go index d2e7d6dc9..e01b95bf7 100644 --- a/vendor/helm.sh/helm/v3/pkg/chartutil/dependencies.go +++ b/vendor/helm.sh/helm/v3/pkg/chartutil/dependencies.go @@ -268,7 +268,7 @@ func processImportValues(c *chart.Chart) error { } // set the new values - c.Values = CoalesceTables(b, cvals) + c.Values = CoalesceTables(cvals, b) return nil } diff --git a/vendor/helm.sh/helm/v3/pkg/chartutil/validate_name.go b/vendor/helm.sh/helm/v3/pkg/chartutil/validate_name.go index d253731ec..05c090cb6 100644 --- a/vendor/helm.sh/helm/v3/pkg/chartutil/validate_name.go +++ b/vendor/helm.sh/helm/v3/pkg/chartutil/validate_name.go @@ -40,19 +40,24 @@ var ( errMissingName = errors.New("no name provided") // errInvalidName indicates that an invalid release name was provided - errInvalidName = errors.New(fmt.Sprintf( + errInvalidName = fmt.Errorf( "invalid release name, must match regex %s and the length must not be longer than 53", - validName.String())) + validName.String()) // errInvalidKubernetesName indicates that the name does not meet the Kubernetes // restrictions on metadata names. - errInvalidKubernetesName = errors.New(fmt.Sprintf( + errInvalidKubernetesName = fmt.Errorf( "invalid metadata name, must match regex %s and the length must not be longer than 253", - validName.String())) + validName.String()) ) const ( - // maxNameLen is the maximum length Helm allows for a release name + // According to the Kubernetes docs (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#rfc-1035-label-names) + // some resource names have a max length of 63 characters while others have a max + // length of 253 characters. As we cannot be sure the resources used in a chart, we + // therefore need to limit it to 63 chars and reserve 10 chars for additional part to name + // of the resource. The reason is that chart maintainers can use release name as part of + // the resource name (and some additional chars). maxReleaseNameLen = 53 // maxMetadataNameLen is the maximum length Kubernetes allows for any name. maxMetadataNameLen = 253 diff --git a/vendor/helm.sh/helm/v3/pkg/cli/environment.go b/vendor/helm.sh/helm/v3/pkg/cli/environment.go index ee60d981f..d5b208015 100644 --- a/vendor/helm.sh/helm/v3/pkg/cli/environment.go +++ b/vendor/helm.sh/helm/v3/pkg/cli/environment.go @@ -81,7 +81,7 @@ func New() *EnvSettings { KubeAPIServer: os.Getenv("HELM_KUBEAPISERVER"), KubeCaFile: os.Getenv("HELM_KUBECAFILE"), PluginsDirectory: envOr("HELM_PLUGINS", helmpath.DataPath("plugins")), - RegistryConfig: envOr("HELM_REGISTRY_CONFIG", helmpath.ConfigPath("registry.json")), + 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")), } @@ -180,6 +180,11 @@ func (s *EnvSettings) Namespace() string { return "default" } +// SetNamespace sets the namespace in the configuration +func (s *EnvSettings) SetNamespace(namespace string) { + s.namespace = namespace +} + // RESTClientGetter gets the kubeconfig from EnvSettings func (s *EnvSettings) RESTClientGetter() genericclioptions.RESTClientGetter { return s.config diff --git a/vendor/helm.sh/helm/v3/pkg/downloader/chart_downloader.go b/vendor/helm.sh/helm/v3/pkg/downloader/chart_downloader.go index 93afb1461..3feb5b702 100644 --- a/vendor/helm.sh/helm/v3/pkg/downloader/chart_downloader.go +++ b/vendor/helm.sh/helm/v3/pkg/downloader/chart_downloader.go @@ -23,14 +23,15 @@ import ( "path/filepath" "strings" + "github.com/Masterminds/semver/v3" "github.com/pkg/errors" - "helm.sh/helm/v3/internal/experimental/registry" "helm.sh/helm/v3/internal/fileutil" "helm.sh/helm/v3/internal/urlutil" "helm.sh/helm/v3/pkg/getter" "helm.sh/helm/v3/pkg/helmpath" "helm.sh/helm/v3/pkg/provenance" + "helm.sh/helm/v3/pkg/registry" "helm.sh/helm/v3/pkg/repo" ) @@ -103,7 +104,8 @@ func (c *ChartDownloader) DownloadTo(ref, version, dest string) (string, *proven name := filepath.Base(u.Path) if u.Scheme == registry.OCIScheme { - name = fmt.Sprintf("%s-%s.tgz", name, version) + idx := strings.LastIndexByte(name, ':') + name = fmt.Sprintf("%s-%s.tgz", name[:idx], name[idx+1:]) } destfile := filepath.Join(dest, name) @@ -139,12 +141,46 @@ func (c *ChartDownloader) DownloadTo(ref, version, dest string) (string, *proven return destfile, ver, nil } +func (c *ChartDownloader) getOciURI(ref, version string, u *url.URL) (*url.URL, error) { + var tag string + var err error + + // Evaluate whether an explicit version has been provided. Otherwise, determine version to use + _, errSemVer := semver.NewVersion(version) + if errSemVer == nil { + tag = version + } else { + // Retrieve list of repository tags + tags, err := c.RegistryClient.Tags(strings.TrimPrefix(ref, fmt.Sprintf("%s://", registry.OCIScheme))) + if err != nil { + return nil, err + } + if len(tags) == 0 { + return nil, errors.Errorf("Unable to locate any tags in provided repository: %s", ref) + } + + // Determine if version provided + // If empty, try to get the highest available tag + // If exact version, try to find it + // If semver constraint string, try to find a match + tag, err = registry.GetTagMatchingVersionOrConstraint(tags, version) + if err != nil { + return nil, err + } + } + + u.Path = fmt.Sprintf("%s:%s", u.Path, tag) + + return u, err +} + // ResolveChartVersion resolves a chart reference to a URL. // // It returns the URL and sets the ChartDownloader's Options that can fetch // the URL using the appropriate Getter. // -// A reference may be an HTTP URL, a 'reponame/chartname' reference, or a local path. +// A reference may be an HTTP URL, an oci reference URL, a 'reponame/chartname' +// reference, or a local path. // // A version is a SemVer string (1.2.3-beta.1+f334a6789). // @@ -159,6 +195,10 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, er return nil, errors.Errorf("invalid chart URL format: %s", ref) } + if registry.IsOCI(u.String()) { + return c.getOciURI(ref, version, u) + } + rf, err := loadRepoConfig(c.RepositoryConfig) if err != nil { return u, err diff --git a/vendor/helm.sh/helm/v3/pkg/downloader/manager.go b/vendor/helm.sh/helm/v3/pkg/downloader/manager.go index b0ade6c90..18b28dde1 100644 --- a/vendor/helm.sh/helm/v3/pkg/downloader/manager.go +++ b/vendor/helm.sh/helm/v3/pkg/downloader/manager.go @@ -34,7 +34,6 @@ import ( "github.com/pkg/errors" "sigs.k8s.io/yaml" - "helm.sh/helm/v3/internal/experimental/registry" "helm.sh/helm/v3/internal/resolver" "helm.sh/helm/v3/internal/third_party/dep/fs" "helm.sh/helm/v3/internal/urlutil" @@ -43,6 +42,7 @@ import ( "helm.sh/helm/v3/pkg/chartutil" "helm.sh/helm/v3/pkg/getter" "helm.sh/helm/v3/pkg/helmpath" + "helm.sh/helm/v3/pkg/registry" "helm.sh/helm/v3/pkg/repo" ) @@ -232,7 +232,7 @@ func (m *Manager) loadChartDir() (*chart.Chart, error) { // // This returns a lock file, which has all of the dependencies normalized to a specific version. func (m *Manager) resolve(req []*chart.Dependency, repoNames map[string]string) (*chart.Lock, error) { - res := resolver.New(m.ChartPath, m.RepositoryCache) + res := resolver.New(m.ChartPath, m.RepositoryCache, m.RegistryClient) return res.Resolve(req, repoNames) } @@ -332,6 +332,7 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error { Keyring: m.Keyring, RepositoryConfig: m.RepositoryConfig, RepositoryCache: m.RepositoryCache, + RegistryClient: m.RegistryClient, Getters: m.Getters, Options: []getter.Option{ getter.WithBasicAuth(username, password), @@ -342,12 +343,7 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error { } version := "" - if strings.HasPrefix(churl, "oci://") { - if !resolver.FeatureGateOCI.IsEnabled() { - return errors.Wrapf(resolver.FeatureGateOCI.Error(), - "the repository %s is an OCI registry", churl) - } - + if registry.IsOCI(churl) { churl, version, err = parseOCIRef(churl) if err != nil { return errors.Wrapf(err, "could not parse OCI reference") @@ -404,12 +400,12 @@ func parseOCIRef(chartRef string) (string, string, error) { func (m *Manager) safeMoveDeps(deps []*chart.Dependency, source, dest string) error { existsInSourceDirectory := map[string]bool{} isLocalDependency := map[string]bool{} - sourceFiles, err := ioutil.ReadDir(source) + sourceFiles, err := os.ReadDir(source) if err != nil { return err } // attempt to read destFiles; fail fast if we can't - destFiles, err := ioutil.ReadDir(dest) + destFiles, err := os.ReadDir(dest) if err != nil { return err } @@ -440,7 +436,7 @@ func (m *Manager) safeMoveDeps(deps []*chart.Dependency, source, dest string) er } fmt.Fprintln(m.Out, "Deleting outdated charts") - // find all files that exist in dest that do not exist in source; delete them (outdated dependendencies) + // find all files that exist in dest that do not exist in source; delete them (outdated dependencies) for _, file := range destFiles { if !file.IsDir() && !existsInSourceDirectory[file.Name()] { fname := filepath.Join(dest, file.Name()) @@ -477,7 +473,7 @@ func (m *Manager) hasAllRepos(deps []*chart.Dependency) error { Loop: for _, dd := range deps { // If repo is from local path or OCI, continue - if strings.HasPrefix(dd.Repository, "file://") || strings.HasPrefix(dd.Repository, "oci://") { + if strings.HasPrefix(dd.Repository, "file://") || registry.IsOCI(dd.Repository) { continue } @@ -578,8 +574,7 @@ func (m *Manager) resolveRepoNames(deps []*chart.Dependency) (map[string]string, missing := []string{} for _, dd := range deps { // Don't map the repository, we don't need to download chart from charts directory - // When OCI is used there is no Helm repository - if dd.Repository == "" || strings.HasPrefix(dd.Repository, "oci://") { + if dd.Repository == "" { continue } // if dep chart is from local path, verify the path is valid @@ -595,7 +590,7 @@ func (m *Manager) resolveRepoNames(deps []*chart.Dependency) (map[string]string, continue } - if strings.HasPrefix(dd.Repository, "oci://") { + if registry.IsOCI(dd.Repository) { reposMap[dd.Name] = dd.Repository continue } @@ -709,7 +704,7 @@ func (m *Manager) parallelRepoUpdate(repos []*repo.Entry) error { // // If it finds a URL that is "relative", it will prepend the repoURL. func (m *Manager) findChartURL(name, version, repoURL string, repos map[string]*repo.ChartRepository) (url, username, password string, insecureskiptlsverify, passcredentialsall bool, caFile, certFile, keyFile string, err error) { - if strings.HasPrefix(repoURL, "oci://") { + if registry.IsOCI(repoURL) { return fmt.Sprintf("%s/%s:%s", repoURL, name, version), "", "", false, false, "", "", "", nil } diff --git a/vendor/helm.sh/helm/v3/pkg/getter/getter.go b/vendor/helm.sh/helm/v3/pkg/getter/getter.go index 3a0567a87..653b032fe 100644 --- a/vendor/helm.sh/helm/v3/pkg/getter/getter.go +++ b/vendor/helm.sh/helm/v3/pkg/getter/getter.go @@ -18,12 +18,13 @@ package getter import ( "bytes" + "net/http" "time" "github.com/pkg/errors" - "helm.sh/helm/v3/internal/experimental/registry" "helm.sh/helm/v3/pkg/cli" + "helm.sh/helm/v3/pkg/registry" ) // options are generic parameters to be provided to the getter during instantiation. @@ -43,6 +44,7 @@ type options struct { version string registryClient *registry.Client timeout time.Duration + transport *http.Transport } // Option allows specifying various settings configurable by the user for overriding the defaults @@ -119,6 +121,13 @@ func WithUntar() Option { } } +// WithTransport sets the http.Transport to allow overwriting the HTTPGetter default. +func WithTransport(transport *http.Transport) Option { + return func(opts *options) { + opts.transport = transport + } +} + // Getter is an interface to support GET to the specified URL. type Getter interface { // Get file content by url string diff --git a/vendor/helm.sh/helm/v3/pkg/getter/httpgetter.go b/vendor/helm.sh/helm/v3/pkg/getter/httpgetter.go index 454eb6eb6..6fe1aa71f 100644 --- a/vendor/helm.sh/helm/v3/pkg/getter/httpgetter.go +++ b/vendor/helm.sh/helm/v3/pkg/getter/httpgetter.go @@ -21,6 +21,7 @@ import ( "io" "net/http" "net/url" + "sync" "github.com/pkg/errors" @@ -31,7 +32,9 @@ import ( // HTTPGetter is the default HTTP(/S) backend handler type HTTPGetter struct { - opts options + opts options + transport *http.Transport + once sync.Once } // Get performs a Get from repo.Getter and returns the body. @@ -106,10 +109,20 @@ func NewHTTPGetter(options ...Option) (Getter, error) { } func (g *HTTPGetter) httpClient() (*http.Client, error) { - transport := &http.Transport{ - DisableCompression: true, - Proxy: http.ProxyFromEnvironment, + if g.opts.transport != nil { + return &http.Client{ + Transport: g.opts.transport, + Timeout: g.opts.timeout, + }, nil } + + g.once.Do(func() { + g.transport = &http.Transport{ + DisableCompression: true, + Proxy: http.ProxyFromEnvironment, + } + }) + if (g.opts.certFile != "" && g.opts.keyFile != "") || g.opts.caFile != "" { tlsConf, err := tlsutil.NewClientTLS(g.opts.certFile, g.opts.keyFile, g.opts.caFile) if err != nil { @@ -123,21 +136,21 @@ func (g *HTTPGetter) httpClient() (*http.Client, error) { } tlsConf.ServerName = sni - transport.TLSClientConfig = tlsConf + g.transport.TLSClientConfig = tlsConf } if g.opts.insecureSkipVerifyTLS { - if transport.TLSClientConfig == nil { - transport.TLSClientConfig = &tls.Config{ + if g.transport.TLSClientConfig == nil { + g.transport.TLSClientConfig = &tls.Config{ InsecureSkipVerify: true, } } else { - transport.TLSClientConfig.InsecureSkipVerify = true + g.transport.TLSClientConfig.InsecureSkipVerify = true } } client := &http.Client{ - Transport: transport, + Transport: g.transport, Timeout: g.opts.timeout, } diff --git a/vendor/helm.sh/helm/v3/pkg/getter/ocigetter.go b/vendor/helm.sh/helm/v3/pkg/getter/ocigetter.go index 45c92749c..14f5cb3ec 100644 --- a/vendor/helm.sh/helm/v3/pkg/getter/ocigetter.go +++ b/vendor/helm.sh/helm/v3/pkg/getter/ocigetter.go @@ -20,7 +20,7 @@ import ( "fmt" "strings" - "helm.sh/helm/v3/internal/experimental/registry" + "helm.sh/helm/v3/pkg/registry" ) // OCIGetter is the default HTTP(/S) backend handler @@ -28,7 +28,7 @@ type OCIGetter struct { opts options } -//Get performs a Get from repo.Getter and returns the body. +// Get performs a Get from repo.Getter and returns the body. func (g *OCIGetter) Get(href string, options ...Option) (*bytes.Buffer, error) { for _, opt := range options { opt(&g.opts) @@ -50,10 +50,6 @@ func (g *OCIGetter) get(href string) (*bytes.Buffer, error) { registry.PullOptWithProv(true)) } - if version := g.opts.version; version != "" { - ref = fmt.Sprintf("%s:%s", ref, version) - } - result, err := client.Pull(ref, pullOpts...) if err != nil { return nil, err @@ -67,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 } diff --git a/vendor/helm.sh/helm/v3/pkg/helmpath/lazypath_darwin.go b/vendor/helm.sh/helm/v3/pkg/helmpath/lazypath_darwin.go index 80477abab..eba6dde15 100644 --- a/vendor/helm.sh/helm/v3/pkg/helmpath/lazypath_darwin.go +++ b/vendor/helm.sh/helm/v3/pkg/helmpath/lazypath_darwin.go @@ -12,7 +12,6 @@ // limitations under the License. //go:build darwin -// +build darwin package helmpath diff --git a/vendor/helm.sh/helm/v3/pkg/helmpath/lazypath_unix.go b/vendor/helm.sh/helm/v3/pkg/helmpath/lazypath_unix.go index a5afc1237..82fb4b6f1 100644 --- a/vendor/helm.sh/helm/v3/pkg/helmpath/lazypath_unix.go +++ b/vendor/helm.sh/helm/v3/pkg/helmpath/lazypath_unix.go @@ -12,7 +12,6 @@ // limitations under the License. //go:build !windows && !darwin -// +build !windows,!darwin package helmpath diff --git a/vendor/helm.sh/helm/v3/pkg/helmpath/lazypath_windows.go b/vendor/helm.sh/helm/v3/pkg/helmpath/lazypath_windows.go index 057a3af14..230aee2a9 100644 --- a/vendor/helm.sh/helm/v3/pkg/helmpath/lazypath_windows.go +++ b/vendor/helm.sh/helm/v3/pkg/helmpath/lazypath_windows.go @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -// +build windows +//go:build windows package helmpath diff --git a/vendor/helm.sh/helm/v3/pkg/kube/client.go b/vendor/helm.sh/helm/v3/pkg/kube/client.go index cc38243ac..38c8b93f2 100644 --- a/vendor/helm.sh/helm/v3/pkg/kube/client.go +++ b/vendor/helm.sh/helm/v3/pkg/kube/client.go @@ -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 } @@ -264,7 +275,7 @@ func (c *Client) Update(original, target ResourceList, force bool) (*Result, err } for _, info := range original.Difference(target) { - c.Log("Deleting %q in %s...", info.Name, info.Namespace) + c.Log("Deleting %s %q in namespace %s...", info.Mapping.GroupVersionKind.Kind, info.Name, info.Namespace) if err := info.Get(); err != nil { c.Log("Unable to get obj %q, err: %s", info.Name, err) @@ -498,7 +509,7 @@ func updateResource(c *Client, target *resource.Info, currentObj runtime.Object, } if patch == nil || string(patch) == "{}" { - c.Log("Looks like there are no changes for %s %q", target.Mapping.GroupVersionKind.Kind, target.Name) + c.Log("Looks like there are no changes for %s %q", kind, target.Name) // This needs to happen to make sure that Helm has the latest info from the API // Otherwise there will be no labels and other functions that use labels will panic if err := target.Get(); err != nil { @@ -507,6 +518,7 @@ func updateResource(c *Client, target *resource.Info, currentObj runtime.Object, return nil } // send patch to server + c.Log("Patch %s %q in namespace %s", kind, target.Name, target.Namespace) obj, err = helper.Patch(target.Namespace, target.Name, patchType, patch, nil) if err != nil { return errors.Wrapf(err, "cannot patch %q with kind %s", target.Name, kind) @@ -645,6 +657,9 @@ func (c *Client) WaitAndGetCompletedPodPhase(name string, timeout time.Duration) FieldSelector: fmt.Sprintf("metadata.name=%s", name), TimeoutSeconds: &to, }) + if err != nil { + return v1.PodUnknown, err + } for event := range watcher.ResultChan() { p, ok := event.Object.(*v1.Pod) diff --git a/vendor/helm.sh/helm/v3/pkg/kube/factory.go b/vendor/helm.sh/helm/v3/pkg/kube/factory.go index f47f9d9f6..fdba8cf8f 100644 --- a/vendor/helm.sh/helm/v3/pkg/kube/factory.go +++ b/vendor/helm.sh/helm/v3/pkg/kube/factory.go @@ -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 } diff --git a/vendor/helm.sh/helm/v3/pkg/kube/ready.go b/vendor/helm.sh/helm/v3/pkg/kube/ready.go index 106c0be51..0554c1729 100644 --- a/vendor/helm.sh/helm/v3/pkg/kube/ready.go +++ b/vendor/helm.sh/helm/v3/pkg/kube/ready.go @@ -353,9 +353,16 @@ func (c *ReadyChecker) crdReady(crd apiextv1.CustomResourceDefinition) bool { 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 @@ -377,7 +384,7 @@ func (c *ReadyChecker) statefulSetReady(sts *appsv1.StatefulSet) bool { expectedReplicas := replicas - partition // Make sure all the updated pods have been scheduled - if int(sts.Status.UpdatedReplicas) != expectedReplicas { + if int(sts.Status.UpdatedReplicas) < expectedReplicas { c.log("StatefulSet is not ready: %s/%s. %d out of %d expected pods have been scheduled", sts.Namespace, sts.Name, sts.Status.UpdatedReplicas, expectedReplicas) return false } @@ -386,6 +393,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 } diff --git a/vendor/helm.sh/helm/v3/pkg/lint/rules/template.go b/vendor/helm.sh/helm/v3/pkg/lint/rules/template.go index b4bfe33e2..61425f92e 100644 --- a/vendor/helm.sh/helm/v3/pkg/lint/rules/template.go +++ b/vendor/helm.sh/helm/v3/pkg/lint/rules/template.go @@ -70,6 +70,12 @@ func Templates(linter *support.Linter, values map[string]interface{}, namespace Namespace: namespace, } + // lint ignores import-values + // See https://github.com/helm/helm/issues/9658 + if err := chartutil.ProcessDependencies(chart, values); err != nil { + return + } + cvals, err := chartutil.CoalesceValues(chart, values) if err != nil { return diff --git a/vendor/helm.sh/helm/v3/pkg/postrender/exec.go b/vendor/helm.sh/helm/v3/pkg/postrender/exec.go index 1de70b024..167e737d6 100644 --- a/vendor/helm.sh/helm/v3/pkg/postrender/exec.go +++ b/vendor/helm.sh/helm/v3/pkg/postrender/exec.go @@ -27,23 +27,24 @@ import ( type execRender struct { binaryPath string + args []string } // NewExec returns a PostRenderer implementation that calls the provided binary. // It returns an error if the binary cannot be found. If the path does not // contain any separators, it will search in $PATH, otherwise it will resolve // any relative paths to a fully qualified path -func NewExec(binaryPath string) (PostRenderer, error) { +func NewExec(binaryPath string, args ...string) (PostRenderer, error) { fullPath, err := getFullPath(binaryPath) if err != nil { return nil, err } - return &execRender{fullPath}, nil + return &execRender{fullPath, args}, nil } // Run the configured binary for the post render func (p *execRender) Run(renderedManifests *bytes.Buffer) (*bytes.Buffer, error) { - cmd := exec.Command(p.binaryPath) + cmd := exec.Command(p.binaryPath, p.args...) stdin, err := cmd.StdinPipe() if err != nil { return nil, err diff --git a/vendor/helm.sh/helm/v3/pkg/provenance/sign.go b/vendor/helm.sh/helm/v3/pkg/provenance/sign.go index 5d16779f1..c41f90c61 100644 --- a/vendor/helm.sh/helm/v3/pkg/provenance/sign.go +++ b/vendor/helm.sh/helm/v3/pkg/provenance/sign.go @@ -26,9 +26,9 @@ import ( "strings" "github.com/pkg/errors" - "golang.org/x/crypto/openpgp" - "golang.org/x/crypto/openpgp/clearsign" - "golang.org/x/crypto/openpgp/packet" + "golang.org/x/crypto/openpgp" //nolint + "golang.org/x/crypto/openpgp/clearsign" //nolint + "golang.org/x/crypto/openpgp/packet" //nolint "sigs.k8s.io/yaml" hapi "helm.sh/helm/v3/pkg/chart" @@ -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. diff --git a/vendor/helm.sh/helm/v3/pkg/gates/gates.go b/vendor/helm.sh/helm/v3/pkg/pusher/doc.go index 69559219e..df89ab112 100644 --- a/vendor/helm.sh/helm/v3/pkg/gates/gates.go +++ b/vendor/helm.sh/helm/v3/pkg/pusher/doc.go @@ -13,26 +13,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -package gates - -import ( - "fmt" - "os" -) - -// Gate is the name of the feature gate. -type Gate string - -// String returns the string representation of this feature gate. -func (g Gate) String() string { - return string(g) -} - -// IsEnabled determines whether a certain feature gate is enabled. -func (g Gate) IsEnabled() bool { - return os.Getenv(string(g)) != "" -} - -func (g Gate) Error() error { - return fmt.Errorf("this feature has been marked as experimental and is not enabled by default. Please set %s=1 in your environment to use this feature", g.String()) -} +/* +Package pusher provides a generalized tool for uploading data by scheme. +This provides a method by which the plugin system can load arbitrary protocol +handlers based upon a URL scheme. +*/ +package pusher diff --git a/vendor/helm.sh/helm/v3/pkg/pusher/ocipusher.go b/vendor/helm.sh/helm/v3/pkg/pusher/ocipusher.go new file mode 100644 index 000000000..7c90e85a4 --- /dev/null +++ b/vendor/helm.sh/helm/v3/pkg/pusher/ocipusher.go @@ -0,0 +1,106 @@ +/* +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 pusher + +import ( + "fmt" + "io/ioutil" + "os" + "path" + "strings" + + "github.com/pkg/errors" + + "helm.sh/helm/v3/pkg/chart/loader" + "helm.sh/helm/v3/pkg/registry" +) + +// OCIPusher is the default OCI backend handler +type OCIPusher struct { + opts options +} + +// Push performs a Push from repo.Pusher. +func (pusher *OCIPusher) Push(chartRef, href string, options ...Option) error { + for _, opt := range options { + opt(&pusher.opts) + } + return pusher.push(chartRef, href) +} + +func (pusher *OCIPusher) push(chartRef, href string) error { + stat, err := os.Stat(chartRef) + if err != nil { + if os.IsNotExist(err) { + return errors.Errorf("%s: no such file", chartRef) + } + return err + } + if stat.IsDir() { + return errors.New("cannot push directory, must provide chart archive (.tgz)") + } + + meta, err := loader.Load(chartRef) + if err != nil { + return err + } + + client := pusher.opts.registryClient + + chartBytes, err := ioutil.ReadFile(chartRef) + if err != nil { + return err + } + + var pushOpts []registry.PushOption + provRef := fmt.Sprintf("%s.prov", chartRef) + if _, err := os.Stat(provRef); err == nil { + provBytes, err := ioutil.ReadFile(provRef) + if err != nil { + return err + } + pushOpts = append(pushOpts, registry.PushOptProvData(provBytes)) + } + + ref := fmt.Sprintf("%s:%s", + path.Join(strings.TrimPrefix(href, fmt.Sprintf("%s://", registry.OCIScheme)), meta.Metadata.Name), + meta.Metadata.Version) + + _, err = client.Push(chartBytes, ref, pushOpts...) + return err +} + +// NewOCIPusher constructs a valid OCI client as a Pusher +func NewOCIPusher(ops ...Option) (Pusher, error) { + registryClient, err := registry.NewClient( + registry.ClientOptEnableCache(true), + ) + if err != nil { + return nil, err + } + + client := OCIPusher{ + opts: options{ + registryClient: registryClient, + }, + } + + for _, opt := range ops { + opt(&client.opts) + } + + return &client, nil +} diff --git a/vendor/helm.sh/helm/v3/pkg/pusher/pusher.go b/vendor/helm.sh/helm/v3/pkg/pusher/pusher.go new file mode 100644 index 000000000..30c6af97c --- /dev/null +++ b/vendor/helm.sh/helm/v3/pkg/pusher/pusher.go @@ -0,0 +1,95 @@ +/* +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 pusher + +import ( + "github.com/pkg/errors" + + "helm.sh/helm/v3/pkg/cli" + "helm.sh/helm/v3/pkg/registry" +) + +// options are generic parameters to be provided to the pusher during instantiation. +// +// Pushers may or may not ignore these parameters as they are passed in. +type options struct { + registryClient *registry.Client +} + +// Option allows specifying various settings configurable by the user for overriding the defaults +// used when performing Push operations with the Pusher. +type Option func(*options) + +// WithRegistryClient sets the registryClient option. +func WithRegistryClient(client *registry.Client) Option { + return func(opts *options) { + opts.registryClient = client + } +} + +// Pusher is an interface to support upload to the specified URL. +type Pusher interface { + // Push file content by url string + Push(chartRef, url string, options ...Option) error +} + +// Constructor is the function for every pusher which creates a specific instance +// according to the configuration +type Constructor func(options ...Option) (Pusher, error) + +// Provider represents any pusher and the schemes that it supports. +type Provider struct { + Schemes []string + New Constructor +} + +// Provides returns true if the given scheme is supported by this Provider. +func (p Provider) Provides(scheme string) bool { + for _, i := range p.Schemes { + if i == scheme { + return true + } + } + return false +} + +// Providers is a collection of Provider objects. +type Providers []Provider + +// ByScheme returns a Provider that handles the given scheme. +// +// If no provider handles this scheme, this will return an error. +func (p Providers) ByScheme(scheme string) (Pusher, error) { + for _, pp := range p { + if pp.Provides(scheme) { + return pp.New() + } + } + return nil, errors.Errorf("scheme %q not supported", scheme) +} + +var ociProvider = Provider{ + Schemes: []string{registry.OCIScheme}, + New: NewOCIPusher, +} + +// All finds all of the registered pushers as a list of Provider instances. +// Currently, just the built-in pushers are collected. +func All(settings *cli.EnvSettings) Providers { + result := Providers{ociProvider} + return result +} diff --git a/vendor/helm.sh/helm/v3/internal/experimental/registry/client.go b/vendor/helm.sh/helm/v3/pkg/registry/client.go index cc9e1fe79..c1004f956 100644 --- a/vendor/helm.sh/helm/v3/internal/experimental/registry/client.go +++ b/vendor/helm.sh/helm/v3/pkg/registry/client.go @@ -14,16 +14,19 @@ See the License for the specific language governing permissions and limitations under the License. */ -package registry // import "helm.sh/helm/v3/internal/experimental/registry" +package registry // import "helm.sh/helm/v3/pkg/registry" import ( + "context" "encoding/json" "fmt" "io" "io/ioutil" "net/http" + "sort" "strings" + "github.com/Masterminds/semver/v3" "github.com/containerd/containerd/remotes" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" @@ -31,21 +34,33 @@ import ( dockerauth "oras.land/oras-go/pkg/auth/docker" "oras.land/oras-go/pkg/content" "oras.land/oras-go/pkg/oras" + "oras.land/oras-go/pkg/registry" + registryremote "oras.land/oras-go/pkg/registry/remote" + registryauth "oras.land/oras-go/pkg/registry/remote/auth" "helm.sh/helm/v3/internal/version" "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/helmpath" ) +// See https://github.com/helm/helm/issues/10166 +const registryUnderscoreMessage = ` +OCI artifact references (e.g. tags) do not support the plus sign (+). To support +storing semantic versions, Helm adopts the convention of changing plus (+) to +an underscore (_) in chart version tags when pushing to a registry and back to +a plus (+) when pulling from a registry.` + type ( // Client works with OCI-compliant registries Client struct { - debug bool + debug bool + enableCache bool // path to repository config file e.g. ~/.docker/config.json - credentialsFile string - out io.Writer - authorizer auth.Client - resolver remotes.Resolver + credentialsFile string + out io.Writer + authorizer auth.Client + registryAuthorizer *registryauth.Client + resolver remotes.Resolver } // ClientOption allows specifying various settings configurable by the user for overriding the defaults @@ -65,7 +80,7 @@ func NewClient(options ...ClientOption) (*Client, error) { client.credentialsFile = helmpath.ConfigPath(CredentialsFileBasename) } if client.authorizer == nil { - authClient, err := dockerauth.NewClient(client.credentialsFile) + authClient, err := dockerauth.NewClientWithDockerFallback(client.credentialsFile) if err != nil { return nil, err } @@ -81,6 +96,45 @@ 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 = ®istryauth.Client{ + Header: http.Header{ + "User-Agent": {version.GetUserAgent()}, + }, + Cache: cache, + Credential: func(ctx context.Context, reg string) (registryauth.Credential, error) { + dockerClient, ok := client.authorizer.(*dockerauth.Client) + if !ok { + return registryauth.EmptyCredential, errors.New("unable to obtain docker client") + } + + username, password, err := dockerClient.Credential(reg) + if err != nil { + return registryauth.EmptyCredential, errors.New("unable to retrieve credentials") + } + + // A blank returned username and password value is a bearer token + if username == "" && password != "" { + return registryauth.Credential{ + RefreshToken: password, + }, nil + } + + return registryauth.Credential{ + Username: username, + Password: password, + }, nil + + }, + } + + } return client, nil } @@ -91,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) { @@ -207,6 +268,11 @@ type ( // Pull downloads a chart from a registry func (c *Client) Pull(ref string, options ...PullOption) (*PullResult, error) { + parsedRef, err := parseReference(ref) + if err != nil { + return nil, err + } + operation := &pullOperation{ withChart: true, // By default, always download the chart layer } @@ -217,7 +283,7 @@ func (c *Client) Pull(ref string, options ...PullOption) (*PullResult, error) { return nil, errors.New( "must specify at least one layer to pull (chart/prov)") } - store := content.NewMemoryStore() + memoryStore := content.NewMemory() allowedMediaTypes := []string{ ConfigMediaType, } @@ -232,17 +298,27 @@ func (c *Client) Pull(ref string, options ...PullOption) (*PullResult, error) { } allowedMediaTypes = append(allowedMediaTypes, ProvLayerMediaType) } - manifest, descriptors, err := oras.Pull(ctx(c.out, c.debug), c.resolver, ref, store, + + var descriptors, layers []ocispec.Descriptor + registryStore := content.Registry{Resolver: c.resolver} + + manifest, err := oras.Copy(ctx(c.out, c.debug), registryStore, parsedRef.String(), memoryStore, "", oras.WithPullEmptyNameAllowed(), - oras.WithAllowedMediaTypes(allowedMediaTypes)) + oras.WithAllowedMediaTypes(allowedMediaTypes), + oras.WithLayerDescriptors(func(l []ocispec.Descriptor) { + layers = l + })) if err != nil { return nil, err } + + descriptors = append(descriptors, manifest) + descriptors = append(descriptors, layers...) + numDescriptors := len(descriptors) if numDescriptors < minNumDescriptors { - return nil, errors.New( - fmt.Sprintf("manifest does not contain minimum number of descriptors (%d), descriptors found: %d", - minNumDescriptors, numDescriptors)) + return nil, fmt.Errorf("manifest does not contain minimum number of descriptors (%d), descriptors found: %d", + minNumDescriptors, numDescriptors) } var configDescriptor *ocispec.Descriptor var chartDescriptor *ocispec.Descriptor @@ -262,22 +338,19 @@ func (c *Client) Pull(ref string, options ...PullOption) (*PullResult, error) { } } if configDescriptor == nil { - return nil, errors.New( - fmt.Sprintf("could not load config with mediatype %s", ConfigMediaType)) + return nil, fmt.Errorf("could not load config with mediatype %s", ConfigMediaType) } if operation.withChart && chartDescriptor == nil { - return nil, errors.New( - fmt.Sprintf("manifest does not contain a layer with mediatype %s", - ChartLayerMediaType)) + return nil, fmt.Errorf("manifest does not contain a layer with mediatype %s", + ChartLayerMediaType) } var provMissing bool if operation.withProv && provDescriptor == nil { if operation.ignoreMissingProv { provMissing = true } else { - return nil, errors.New( - fmt.Sprintf("manifest does not contain a layer with mediatype %s", - ProvLayerMediaType)) + return nil, fmt.Errorf("manifest does not contain a layer with mediatype %s", + ProvLayerMediaType) } } result := &PullResult{ @@ -291,10 +364,10 @@ func (c *Client) Pull(ref string, options ...PullOption) (*PullResult, error) { }, Chart: &descriptorPullSummaryWithMeta{}, Prov: &descriptorPullSummary{}, - Ref: ref, + Ref: parsedRef.String(), } var getManifestErr error - if _, manifestData, ok := store.Get(manifest); !ok { + if _, manifestData, ok := memoryStore.Get(manifest); !ok { getManifestErr = errors.Errorf("Unable to retrieve blob with digest %s", manifest.Digest) } else { result.Manifest.Data = manifestData @@ -303,7 +376,7 @@ func (c *Client) Pull(ref string, options ...PullOption) (*PullResult, error) { return nil, getManifestErr } var getConfigDescriptorErr error - if _, configData, ok := store.Get(*configDescriptor); !ok { + if _, configData, ok := memoryStore.Get(*configDescriptor); !ok { getConfigDescriptorErr = errors.Errorf("Unable to retrieve blob with digest %s", configDescriptor.Digest) } else { result.Config.Data = configData @@ -318,7 +391,7 @@ func (c *Client) Pull(ref string, options ...PullOption) (*PullResult, error) { } if operation.withChart { var getChartDescriptorErr error - if _, chartData, ok := store.Get(*chartDescriptor); !ok { + if _, chartData, ok := memoryStore.Get(*chartDescriptor); !ok { getChartDescriptorErr = errors.Errorf("Unable to retrieve blob with digest %s", chartDescriptor.Digest) } else { result.Chart.Data = chartData @@ -331,7 +404,7 @@ func (c *Client) Pull(ref string, options ...PullOption) (*PullResult, error) { } if operation.withProv && !provMissing { var getProvDescriptorErr error - if _, provData, ok := store.Get(*provDescriptor); !ok { + if _, provData, ok := memoryStore.Get(*provDescriptor); !ok { getProvDescriptorErr = errors.Errorf("Unable to retrieve blob with digest %s", provDescriptor.Digest) } else { result.Prov.Data = provData @@ -342,8 +415,15 @@ func (c *Client) Pull(ref string, options ...PullOption) (*PullResult, error) { return nil, getProvDescriptorErr } } + fmt.Fprintf(c.out, "Pulled: %s\n", result.Ref) fmt.Fprintf(c.out, "Digest: %s\n", result.Manifest.Digest) + + if strings.Contains(result.Ref, "_") { + fmt.Fprintf(c.out, "%s contains an underscore.\n", result.Ref) + fmt.Fprint(c.out, registryUnderscoreMessage+"\n") + } + return result, nil } @@ -399,6 +479,11 @@ type ( // Push uploads a chart to a registry. func (c *Client) Push(data []byte, ref string, options ...PushOption) (*PushResult, error) { + parsedRef, err := parseReference(ref) + if err != nil { + return nil, err + } + operation := &pushOperation{ strictMode: true, // By default, enable strict mode } @@ -415,21 +500,45 @@ func (c *Client) Push(data []byte, ref string, options ...PushOption) (*PushResu "strict mode enabled, ref basename and tag must match the chart name and version") } } - store := content.NewMemoryStore() - chartDescriptor := store.Add("", ChartLayerMediaType, data) + memoryStore := content.NewMemory() + chartDescriptor, err := memoryStore.Add("", ChartLayerMediaType, data) + if err != nil { + return nil, err + } + configData, err := json.Marshal(meta) if err != nil { return nil, err } - configDescriptor := store.Add("", ConfigMediaType, configData) + + configDescriptor, err := memoryStore.Add("", ConfigMediaType, configData) + if err != nil { + return nil, err + } + descriptors := []ocispec.Descriptor{chartDescriptor} var provDescriptor ocispec.Descriptor if operation.provData != nil { - provDescriptor = store.Add("", ProvLayerMediaType, operation.provData) + provDescriptor, err = memoryStore.Add("", ProvLayerMediaType, operation.provData) + if err != nil { + return nil, err + } + descriptors = append(descriptors, provDescriptor) } - manifest, err := oras.Push(ctx(c.out, c.debug), c.resolver, ref, store, descriptors, - oras.WithConfig(configDescriptor), oras.WithNameValidation(nil)) + + manifestData, manifest, err := content.GenerateManifest(&configDescriptor, nil, descriptors...) + if err != nil { + return nil, err + } + + if err := memoryStore.StoreManifest(parsedRef.String(), manifest, manifestData); err != nil { + return nil, err + } + + registryStore := content.Registry{Resolver: c.resolver} + _, err = oras.Copy(ctx(c.out, c.debug), memoryStore, parsedRef.String(), registryStore, "", + oras.WithNameValidation(nil)) if err != nil { return nil, err } @@ -449,7 +558,7 @@ func (c *Client) Push(data []byte, ref string, options ...PushOption) (*PushResu }, Chart: chartSummary, Prov: &descriptorPushSummary{}, // prevent nil references - Ref: ref, + Ref: parsedRef.String(), } if operation.provData != nil { result.Prov = &descriptorPushSummary{ @@ -459,6 +568,11 @@ func (c *Client) Push(data []byte, ref string, options ...PushOption) (*PushResu } fmt.Fprintf(c.out, "Pushed: %s\n", result.Ref) fmt.Fprintf(c.out, "Digest: %s\n", result.Manifest.Digest) + if strings.Contains(parsedRef.Reference, "_") { + fmt.Fprintf(c.out, "%s contains an underscore.\n", result.Ref) + fmt.Fprint(c.out, registryUnderscoreMessage+"\n") + } + return result, err } @@ -475,3 +589,55 @@ func PushOptStrictMode(strictMode bool) PushOption { operation.strictMode = strictMode } } + +// Tags provides a sorted list all semver compliant tags for a given repository +func (c *Client) Tags(ref string) ([]string, error) { + parsedReference, err := registry.ParseReference(ref) + if err != nil { + return nil, err + } + + repository := registryremote.Repository{ + Reference: parsedReference, + Client: c.registryAuthorizer, + } + + var registryTags []string + + for { + registryTags, err = registry.Tags(ctx(c.out, c.debug), &repository) + if err != nil { + // Fallback to http based request + if !repository.PlainHTTP && strings.Contains(err.Error(), "server gave HTTP response") { + repository.PlainHTTP = true + continue + } + return nil, err + } + + break + + } + + var tagVersions []*semver.Version + for _, tag := range registryTags { + // Change underscore (_) back to plus (+) for Helm + // See https://github.com/helm/helm/issues/10166 + tagVersion, err := semver.StrictNewVersion(strings.ReplaceAll(tag, "_", "+")) + if err == nil { + tagVersions = append(tagVersions, tagVersion) + } + } + + // Sort the collection + sort.Sort(sort.Reverse(semver.Collection(tagVersions))) + + tags := make([]string, len(tagVersions)) + + for iTv, tv := range tagVersions { + tags[iTv] = tv.String() + } + + return tags, nil + +} diff --git a/vendor/helm.sh/helm/v3/internal/experimental/registry/constants.go b/vendor/helm.sh/helm/v3/pkg/registry/constants.go index 876e4dc13..570b6f0d3 100644 --- a/vendor/helm.sh/helm/v3/internal/experimental/registry/constants.go +++ b/vendor/helm.sh/helm/v3/pkg/registry/constants.go @@ -14,14 +14,14 @@ See the License for the specific language governing permissions and limitations under the License. */ -package registry // import "helm.sh/helm/v3/internal/experimental/registry" +package registry // import "helm.sh/helm/v3/pkg/registry" const ( // OCIScheme is the URL scheme for OCI-based requests OCIScheme = "oci" // CredentialsFileBasename is the filename for auth credentials file - CredentialsFileBasename = "registry.json" + CredentialsFileBasename = "registry/config.json" // ConfigMediaType is the reserved media type for the Helm chart manifest config ConfigMediaType = "application/vnd.cncf.helm.config.v1+json" diff --git a/vendor/helm.sh/helm/v3/pkg/registry/util.go b/vendor/helm.sh/helm/v3/pkg/registry/util.go new file mode 100644 index 000000000..47eed267f --- /dev/null +++ b/vendor/helm.sh/helm/v3/pkg/registry/util.go @@ -0,0 +1,131 @@ +/* +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 registry // import "helm.sh/helm/v3/pkg/registry" + +import ( + "bytes" + "context" + "fmt" + "io" + "strings" + + "github.com/Masterminds/semver/v3" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + orascontext "oras.land/oras-go/pkg/context" + "oras.land/oras-go/pkg/registry" + + "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/chart/loader" +) + +// IsOCI determines whether or not a URL is to be treated as an OCI URL +func IsOCI(url string) bool { + return strings.HasPrefix(url, fmt.Sprintf("%s://", OCIScheme)) +} + +// ContainsTag determines whether a tag is found in a provided list of tags +func ContainsTag(tags []string, tag string) bool { + for _, t := range tags { + if tag == t { + return true + } + } + return false +} + +func GetTagMatchingVersionOrConstraint(tags []string, versionString string) (string, error) { + var constraint *semver.Constraints + if versionString == "" { + // If string is empty, set wildcard constraint + constraint, _ = semver.NewConstraint("*") + } else { + // when customer input exact version, check whether have exact match + // one first + for _, v := range tags { + if versionString == v { + return v, nil + } + } + + // Otherwise set constraint to the string given + var err error + constraint, err = semver.NewConstraint(versionString) + if err != nil { + return "", err + } + } + + // Otherwise try to find the first available version matching the string, + // in case it is a constraint + for _, v := range tags { + test, err := semver.NewVersion(v) + if err != nil { + continue + } + if constraint.Check(test) { + return v, nil + } + } + + return "", errors.Errorf("Could not locate a version matching provided version string %s", versionString) +} + +// extractChartMeta is used to extract a chart metadata from a byte array +func extractChartMeta(chartData []byte) (*chart.Metadata, error) { + ch, err := loader.LoadArchive(bytes.NewReader(chartData)) + if err != nil { + return nil, err + } + return ch.Metadata, nil +} + +// ctx retrieves a fresh context. +// disable verbose logging coming from ORAS (unless debug is enabled) +func ctx(out io.Writer, debug bool) context.Context { + if !debug { + return orascontext.Background() + } + ctx := orascontext.WithLoggerFromWriter(context.Background(), out) + orascontext.GetLogger(ctx).Logger.SetLevel(logrus.DebugLevel) + return ctx +} + +// parseReference will parse and validate the reference, and clean tags when +// applicable tags are only cleaned when plus (+) signs are present, and are +// converted to underscores (_) before pushing +// See https://github.com/helm/helm/issues/10166 +func parseReference(raw string) (registry.Reference, error) { + // The sole possible reference modification is replacing plus (+) signs + // present in tags with underscores (_). To do this properly, we first + // need to identify a tag, and then pass it on to the reference parser + // NOTE: Passing immediately to the reference parser will fail since (+) + // signs are an invalid tag character, and simply replacing all plus (+) + // occurrences could invalidate other portions of the URI + parts := strings.Split(raw, ":") + if len(parts) > 1 && !strings.Contains(parts[len(parts)-1], "/") { + tag := parts[len(parts)-1] + + if tag != "" { + // Replace any plus (+) signs with known underscore (_) conversion + newTag := strings.ReplaceAll(tag, "+", "_") + raw = strings.ReplaceAll(raw, tag, newTag) + } + } + + return registry.ParseReference(raw) +} diff --git a/vendor/helm.sh/helm/v3/pkg/releaseutil/kind_sorter.go b/vendor/helm.sh/helm/v3/pkg/releaseutil/kind_sorter.go index a340dfc29..1d1874cfa 100644 --- a/vendor/helm.sh/helm/v3/pkg/releaseutil/kind_sorter.go +++ b/vendor/helm.sh/helm/v3/pkg/releaseutil/kind_sorter.go @@ -61,6 +61,7 @@ var InstallOrder KindSortOrder = []string{ "StatefulSet", "Job", "CronJob", + "IngressClass", "Ingress", "APIService", } @@ -71,6 +72,7 @@ var InstallOrder KindSortOrder = []string{ var UninstallOrder KindSortOrder = []string{ "APIService", "Ingress", + "IngressClass", "Service", "CronJob", "Job", diff --git a/vendor/helm.sh/helm/v3/pkg/storage/driver/util.go b/vendor/helm.sh/helm/v3/pkg/storage/driver/util.go index e5b846163..b5908e508 100644 --- a/vendor/helm.sh/helm/v3/pkg/storage/driver/util.go +++ b/vendor/helm.sh/helm/v3/pkg/storage/driver/util.go @@ -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 diff --git a/vendor/helm.sh/helm/v3/pkg/storage/storage.go b/vendor/helm.sh/helm/v3/pkg/storage/storage.go index 370fec4b4..0a18b34a0 100644 --- a/vendor/helm.sh/helm/v3/pkg/storage/storage.go +++ b/vendor/helm.sh/helm/v3/pkg/storage/storage.go @@ -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 } diff --git a/vendor/helm.sh/helm/v3/pkg/strvals/parser.go b/vendor/helm.sh/helm/v3/pkg/strvals/parser.go index 457b99f94..d8c73d03c 100644 --- a/vendor/helm.sh/helm/v3/pkg/strvals/parser.go +++ b/vendor/helm.sh/helm/v3/pkg/strvals/parser.go @@ -29,6 +29,10 @@ import ( // ErrNotList indicates that a non-list was treated as a list. var ErrNotList = errors.New("not a list") +// MaxIndex is the maximum index that will be allowed by setIndex. +// The default value 65536 = 1024 * 64 +var MaxIndex = 65536 + // ToYAML takes a string of arguments and converts to a YAML document. func ToYAML(s string) (string, error) { m, err := Parse(s) @@ -249,6 +253,9 @@ func setIndex(list []interface{}, index int, val interface{}) (l2 []interface{}, if index < 0 { return list, fmt.Errorf("negative %d index not allowed", index) } + if index > MaxIndex { + return list, fmt.Errorf("index of %d is greater than maximum supported index of %d", index, MaxIndex) + } if len(list) <= index { newlist := make([]interface{}, index+1) copy(newlist, list) diff --git a/vendor/helm.sh/helm/v3/pkg/uploader/chart_uploader.go b/vendor/helm.sh/helm/v3/pkg/uploader/chart_uploader.go new file mode 100644 index 000000000..d7e940406 --- /dev/null +++ b/vendor/helm.sh/helm/v3/pkg/uploader/chart_uploader.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 uploader + +import ( + "fmt" + "io" + "net/url" + + "github.com/pkg/errors" + + "helm.sh/helm/v3/pkg/pusher" + "helm.sh/helm/v3/pkg/registry" +) + +// ChartUploader handles uploading a chart. +type ChartUploader struct { + // Out is the location to write warning and info messages. + Out io.Writer + // Pusher collection for the operation + Pushers pusher.Providers + // Options provide parameters to be passed along to the Pusher being initialized. + Options []pusher.Option + // RegistryClient is a client for interacting with registries. + RegistryClient *registry.Client +} + +// UploadTo uploads a chart. Depending on the settings, it may also upload a provenance file. +func (c *ChartUploader) UploadTo(ref, remote string) error { + u, err := url.Parse(remote) + if err != nil { + return errors.Errorf("invalid chart URL format: %s", remote) + } + + if u.Scheme == "" { + return fmt.Errorf("scheme prefix missing from remote (e.g. \"%s://\")", registry.OCIScheme) + } + + p, err := c.Pushers.ByScheme(u.Scheme) + if err != nil { + return err + } + + return p.Push(ref, u.String(), c.Options...) +} diff --git a/vendor/helm.sh/helm/v3/pkg/gates/doc.go b/vendor/helm.sh/helm/v3/pkg/uploader/doc.go index 762fdb8c6..45eacbbf5 100644 --- a/vendor/helm.sh/helm/v3/pkg/gates/doc.go +++ b/vendor/helm.sh/helm/v3/pkg/uploader/doc.go @@ -13,8 +13,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -/*Package gates provides a general tool for working with experimental feature gates. +/*Package uploader provides a library for uploading charts. -This provides convenience methods where the user can determine if certain experimental features are enabled. +This package contains tools for uploading charts to registries. */ -package gates +package uploader |
