Merge branch 'main' into feat/status

pull/10336/head
Carlos Rodríguez Hernández 4 years ago committed by GitHub
commit 675c7bd366
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -5,7 +5,7 @@ jobs:
build:
working_directory: ~/helm.sh/helm
docker:
- image: circleci/golang:1.16
- image: circleci/golang:1.17
auth:
username: $DOCKER_USER
@ -13,7 +13,7 @@ jobs:
environment:
GOCACHE: "/tmp/go/cache"
GOLANGCI_LINT_VERSION: "1.36.0"
GOLANGCI_LINT_VERSION: "1.43.0"
steps:
- checkout
@ -26,6 +26,9 @@ jobs:
- run:
name: test
command: make test-coverage
- run:
name: test build
command: make
- deploy:
name: deploy
command: .circleci/deploy.sh

@ -12,7 +12,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: '1.16'
go-version: '1.17'
- 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.36.0'
GOLANGCI_LINT_SHA256: '9b8856b3a1c9bfbcf3a06b78e94611763b79abd9751c245246787cd3bf0e78a5'
GOLANGCI_LINT_VERSION: '1.43.0'
GOLANGCI_LINT_SHA256: 'f3515cebec926257da703ba0a2b169e4a322c11dc31a8b4656b50a43e48877f4'
- name: Test style
run: make test-style
- name: Run unit tests

@ -8,12 +8,12 @@ linters:
- dupl
- gofmt
- goimports
- golint
- gosimple
- govet
- ineffassign
- misspell
- nakedret
- revive
- structcheck
- unused
- varcheck

@ -5,18 +5,20 @@ maintainers:
- jdolitsky
- marckhouzam
- mattfarina
- prydonius
- scottrigby
- SlickNik
- technosophos
triage:
- joejulian
- yxxhero
- zonggen
emeritus:
- fibonacci1729
- jascott1
- michelleN
- migmartri
- nebril
- prydonius
- rimusz
- seh
- thomastaylor312

@ -47,10 +47,10 @@ func checkFileCompletion(t *testing.T, cmdName string, shouldBePerformed bool) {
}
if !strings.Contains(out, "ShellCompDirectiveNoFileComp") != shouldBePerformed {
if shouldBePerformed {
t.Error(fmt.Sprintf("Unexpected directive ShellCompDirectiveNoFileComp when completing '%s'", cmdName))
t.Errorf("Unexpected directive ShellCompDirectiveNoFileComp when completing '%s'", cmdName)
} else {
t.Error(fmt.Sprintf("Did not receive directive ShellCompDirectiveNoFileComp when completing '%s'", cmdName))
t.Errorf("Did not receive directive ShellCompDirectiveNoFileComp when completing '%s'", cmdName)
}
t.Log(out)
}

@ -50,11 +50,6 @@ func TestDependencyBuildCmd(t *testing.T) {
}
ociSrv.Run(t, repotest.WithDependingChart(c))
err = os.Setenv("HELM_EXPERIMENTAL_OCI", "1")
if err != nil {
t.Fatal("failed to set environment variable enabling OCI support")
}
dir := func(p ...string) string {
return filepath.Join(append([]string{srv.Root()}, p...)...)
}

@ -51,11 +51,6 @@ func TestDependencyUpdateCmd(t *testing.T) {
}
ociSrv.Run(t, repotest.WithDependingChart(c))
err = os.Setenv("HELM_EXPERIMENTAL_OCI", "1")
if err != nil {
t.Fatal("failed to set environment variable enabling OCI support")
}
if err := srv.LinkIndices(); err != nil {
t.Fatal(err)
}

@ -69,14 +69,7 @@ func newDocsCmd(out io.Writer) *cobra.Command {
f.BoolVar(&o.generateHeaders, "generate-headers", false, "generate standard headers for markdown files")
cmd.RegisterFlagCompletionFunc("type", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
types := []string{"bash", "man", "markdown"}
var comps []string
for _, t := range types {
if strings.HasPrefix(t, toComplete) {
comps = append(comps, t)
}
}
return comps, cobra.ShellCompDirectiveNoFileComp
return []string{"bash", "man", "markdown"}, cobra.ShellCompDirectiveNoFileComp
})
return cmd

@ -26,9 +26,9 @@ func TestDocsTypeFlagCompletion(t *testing.T) {
cmd: "__complete docs --type ''",
golden: "output/docs-type-comp.txt",
}, {
name: "completion for docs --type",
name: "completion for docs --type, no filter",
cmd: "__complete docs --type mar",
golden: "output/docs-type-filtered-comp.txt",
golden: "output/docs-type-comp.txt",
}}
runTestCmd(t, tests)
}

@ -36,8 +36,11 @@ import (
"helm.sh/helm/v3/pkg/repo"
)
const outputFlag = "output"
const postRenderFlag = "post-renderer"
const (
outputFlag = "output"
postRenderFlag = "post-renderer"
postRenderArgsFlag = "post-renderer-args"
)
func addValueOptionsFlags(f *pflag.FlagSet, v *values.Options) {
f.StringSliceVarP(&v.ValueFiles, "values", "f", []string{}, "specify values in a YAML file or a URL (can specify multiple)")
@ -69,10 +72,8 @@ func bindOutputFlag(cmd *cobra.Command, varRef *output.Format) {
err := cmd.RegisterFlagCompletionFunc(outputFlag, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
var formatNames []string
for format, desc := range output.FormatsWithDesc() {
if strings.HasPrefix(format, toComplete) {
formatNames = append(formatNames, fmt.Sprintf("%s\t%s", format, desc))
}
}
// Sort the results to get a deterministic order for the tests
sort.Strings(formatNames)
@ -112,33 +113,85 @@ func (o *outputValue) Set(s string) error {
}
func bindPostRenderFlag(cmd *cobra.Command, varRef *postrender.PostRenderer) {
cmd.Flags().Var(&postRenderer{varRef}, postRenderFlag, "the path to an executable to be used for post rendering. If it exists in $PATH, the binary will be used, otherwise it will try to look for the executable at the given path")
p := &postRendererOptions{varRef, "", []string{}}
cmd.Flags().Var(&postRendererString{p}, postRenderFlag, "the path to an executable to be used for post rendering. If it exists in $PATH, the binary will be used, otherwise it will try to look for the executable at the given path")
cmd.Flags().Var(&postRendererArgsSlice{p}, postRenderArgsFlag, "an argument to the post-renderer (can specify multiple)")
}
type postRenderer struct {
type postRendererOptions struct {
renderer *postrender.PostRenderer
binaryPath string
args []string
}
type postRendererString struct {
options *postRendererOptions
}
func (p postRenderer) String() string {
return "exec"
func (p *postRendererString) String() string {
return p.options.binaryPath
}
func (p postRenderer) Type() string {
return "postrenderer"
func (p *postRendererString) Type() string {
return "postRendererString"
}
func (p postRenderer) Set(s string) error {
if s == "" {
func (p *postRendererString) Set(val string) error {
if val == "" {
return nil
}
pr, err := postrender.NewExec(s)
p.options.binaryPath = val
pr, err := postrender.NewExec(p.options.binaryPath, p.options.args...)
if err != nil {
return err
}
*p.renderer = pr
*p.options.renderer = pr
return nil
}
type postRendererArgsSlice struct {
options *postRendererOptions
}
func (p *postRendererArgsSlice) String() string {
return "[" + strings.Join(p.options.args, ",") + "]"
}
func (p *postRendererArgsSlice) Type() string {
return "postRendererArgsSlice"
}
func (p *postRendererArgsSlice) Set(val string) error {
// a post-renderer defined by a user may accept empty arguments
p.options.args = append(p.options.args, val)
if p.options.binaryPath == "" {
return nil
}
// overwrite if already create PostRenderer by `post-renderer` flags
pr, err := postrender.NewExec(p.options.binaryPath, p.options.args...)
if err != nil {
return err
}
*p.options.renderer = pr
return nil
}
func (p *postRendererArgsSlice) Append(val string) error {
p.options.args = append(p.options.args, val)
return nil
}
func (p *postRendererArgsSlice) Replace(val []string) error {
p.options.args = val
return nil
}
func (p *postRendererArgsSlice) GetSlice() []string {
return p.options.args
}
func compVersionFlag(chartRef string, toComplete string) ([]string, cobra.ShellCompDirective) {
chartInfo := strings.Split(chartRef, "/")
if len(chartInfo) != 2 {
@ -153,8 +206,6 @@ func compVersionFlag(chartRef string, toComplete string) ([]string, cobra.ShellC
var versions []string
if indexFile, err := repo.LoadIndexFile(path); err == nil {
for _, details := range indexFile.Entries[chartName] {
version := details.Metadata.Version
if strings.HasPrefix(version, toComplete) {
appVersion := details.Metadata.AppVersion
appVersionDesc := ""
if appVersion != "" {
@ -169,8 +220,7 @@ func compVersionFlag(chartRef string, toComplete string) ([]string, cobra.ShellC
if details.Metadata.Deprecated {
deprecated = "(deprecated)"
}
versions = append(versions, fmt.Sprintf("%s\t%s%s%s", version, appVersionDesc, createdDesc, deprecated))
}
versions = append(versions, fmt.Sprintf("%s\t%s%s%s", details.Metadata.Version, appVersionDesc, createdDesc, deprecated))
}
}

@ -83,6 +83,13 @@ func outputFlagCompletionTest(t *testing.T, cmdName string) {
rels: releasesMockWithStatus(&release.Info{
Status: release.StatusDeployed,
}),
}, {
name: "completion for output flag, no filter",
cmd: fmt.Sprintf("__complete %s --output jso", cmdName),
golden: "output/output-comp.txt",
rels: releasesMockWithStatus(&release.Info{
Status: release.StatusDeployed,
}),
}}
runTestCmd(t, tests)
}

@ -31,16 +31,12 @@ import (
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/cli"
"helm.sh/helm/v3/pkg/gates"
"helm.sh/helm/v3/pkg/kube"
kubefake "helm.sh/helm/v3/pkg/kube/fake"
"helm.sh/helm/v3/pkg/release"
"helm.sh/helm/v3/pkg/storage/driver"
)
// FeatureGateOCI is the feature gate for checking if `helm chart` and `helm registry` commands should work
const FeatureGateOCI = gates.Gate("HELM_EXPERIMENTAL_OCI")
var settings = cli.New()
func init() {
@ -95,15 +91,6 @@ func main() {
}
}
func checkOCIFeatureGate() func(_ *cobra.Command, _ []string) error {
return func(_ *cobra.Command, _ []string) error {
if !FeatureGateOCI.IsEnabled() {
return FeatureGateOCI.Error()
}
return nil
}
}
// This function loads releases into the memory storage if the
// environment variable is properly set.
func loadReleasesInMemory(actionConfig *action.Configuration) {

@ -20,7 +20,6 @@ import (
"fmt"
"io"
"strconv"
"strings"
"time"
"github.com/gosuri/uitable"
@ -191,12 +190,9 @@ func compListRevisions(toComplete string, cfg *action.Configuration, releaseName
var revisions []string
if hist, err := client.Run(releaseName); err == nil {
for _, release := range hist {
version := strconv.Itoa(release.Version)
if strings.HasPrefix(version, toComplete) {
appVersion := fmt.Sprintf("App: %s", release.Chart.Metadata.AppVersion)
chartDesc := fmt.Sprintf("Chart: %s-%s", release.Chart.Metadata.Name, release.Chart.Metadata.Version)
revisions = append(revisions, fmt.Sprintf("%s\t%s, %s", version, appVersion, chartDesc))
}
revisions = append(revisions, fmt.Sprintf("%s\t%s, %s", strconv.Itoa(release.Version), appVersion, chartDesc))
}
return revisions, cobra.ShellCompDirectiveNoFileComp
}

@ -95,6 +95,11 @@ func revisionFlagCompletionTest(t *testing.T, cmdName string) {
cmd: fmt.Sprintf("__complete %s musketeers --revision ''", cmdName),
rels: releases,
golden: "output/revision-comp.txt",
}, {
name: "completion for revision flag, no filter",
cmd: fmt.Sprintf("__complete %s musketeers --revision 1", cmdName),
rels: releases,
golden: "output/revision-comp.txt",
}, {
name: "completion for revision flag with too few args",
cmd: fmt.Sprintf("__complete %s --revision ''", cmdName),

@ -49,9 +49,9 @@ a path to an unpacked chart directory or a URL.
To override values in a chart, use either the '--values' flag and pass in a file
or use the '--set' flag and pass configuration from the command line, to force
a string value use '--set-string'. In case a value is large and therefore
you want not to use neither '--values' nor '--set', use '--set-file' to read the
single large value from file.
a string value use '--set-string'. You can use '--set-file' to set individual
values from a file when the value itself is too long for the command line
or is dynamically generated.
$ helm install -f myvalues.yaml myredis ./redis
@ -187,10 +187,6 @@ func runInstall(args []string, client *action.Install, valueOpts *values.Options
}
client.ReleaseName = name
if err := checkOCI(chart); err != nil {
return nil, err
}
cp, err := client.ChartPathOptions.LocateChart(chart, settings)
if err != nil {
return nil, err
@ -223,6 +219,7 @@ func runInstall(args []string, client *action.Install, valueOpts *values.Options
// As of Helm 2.4.0, this is treated as a stopping condition:
// https://github.com/helm/helm/issues/2209
if err := action.CheckDependencies(chartRequested, req); err != nil {
err = errors.Wrap(err, "An error occurred while checking for chart dependencies. You may need to run `helm dependency build` to fetch missing dependencies")
if client.DependencyUpdate {
man := &downloader.Manager{
Out: out,
@ -253,8 +250,10 @@ func runInstall(args []string, client *action.Install, valueOpts *values.Options
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
// Handle SIGTERM
cSignal := make(chan os.Signal)
// Set up channel on which to send signal notifications.
// We must use a buffered channel or risk missing the signal
// if we're not ready to receive when the signal is sent.
cSignal := make(chan os.Signal, 2)
signal.Notify(cSignal, os.Interrupt, syscall.SIGTERM)
go func() {
<-cSignal

@ -123,7 +123,7 @@ func TestInstall(t *testing.T) {
// Install, using the name-template
{
name: "install with name-template",
cmd: "install testdata/testcharts/empty --name-template '{{upper \"foobar\"}}'",
cmd: "install testdata/testcharts/empty --name-template '{{ \"foobar\"}}'",
golden: "output/install-name-template.txt",
},
// Install, perform chart verification along the way.
@ -275,6 +275,10 @@ func TestInstallVersionCompletion(t *testing.T) {
name: "completion for install version flag with generate-name",
cmd: fmt.Sprintf("%s __complete install --generate-name testing/alpine --version ''", repoSetup),
golden: "output/version-comp.txt",
}, {
name: "completion for install version flag, no filter",
cmd: fmt.Sprintf("%s __complete install releasename testing/alpine --version 0.3", repoSetup),
golden: "output/version-comp.txt",
}, {
name: "completion for install version flag too few args",
cmd: fmt.Sprintf("%s __complete install testing/alpine --version ''", repoSetup),

@ -157,8 +157,8 @@ func newReleaseListWriter(releases []*release.Release, timeFormat string) *relea
Namespace: r.Namespace,
Revision: strconv.Itoa(r.Version),
Status: r.Info.Status.String(),
Chart: fmt.Sprintf("%s-%s", r.Chart.Metadata.Name, r.Chart.Metadata.Version),
AppVersion: r.Chart.Metadata.AppVersion,
Chart: formatChartname(r.Chart),
AppVersion: formatAppVersion(r.Chart),
}
t := "-"
@ -224,7 +224,14 @@ func compListReleases(toComplete string, ignoredReleaseNames []string, cfg *acti
client := action.NewList(cfg)
client.All = true
client.Limit = 0
client.Filter = fmt.Sprintf("^%s", toComplete)
// Do not filter so as to get the entire list of releases.
// This will allow zsh and fish to match completion choices
// on other criteria then prefix. For example:
// helm status ingress<TAB>
// can match
// helm status nginx-ingress
//
// client.Filter = fmt.Sprintf("^%s", toComplete)
client.SetStateMask()
releases, err := client.Run()

@ -313,7 +313,7 @@ func loadFile(path string) (*pluginCommand, error) {
cmds := new(pluginCommand)
b, err := ioutil.ReadFile(path)
if err != nil {
return cmds, errors.New(fmt.Sprintf("File (%s) not provided by plugin. No plugin auto-completion possible.", path))
return cmds, fmt.Errorf("file (%s) not provided by plugin. No plugin auto-completion possible", path)
}
err = yaml.Unmarshal(b, cmds)

@ -18,7 +18,6 @@ package main
import (
"fmt"
"io"
"strings"
"github.com/gosuri/uitable"
"github.com/spf13/cobra"
@ -82,10 +81,8 @@ func compListPlugins(toComplete string, ignoredPluginNames []string) []string {
if err == nil && len(plugins) > 0 {
filteredPlugins := filterPlugins(plugins, ignoredPluginNames)
for _, p := range filteredPlugins {
if strings.HasPrefix(p.Metadata.Name, toComplete) {
pNames = append(pNames, fmt.Sprintf("%s\t%s", p.Metadata.Name, p.Metadata.Usage))
}
}
}
return pNames
}

@ -307,6 +307,11 @@ func TestPluginCmdsCompletion(t *testing.T) {
cmd: "__complete plugin update ''",
golden: "output/plugin_list_comp.txt",
rels: []*release.Release{},
}, {
name: "completion for plugin update, no filter",
cmd: "__complete plugin update full",
golden: "output/plugin_list_comp.txt",
rels: []*release.Release{},
}, {
name: "completion for plugin update repetition",
cmd: "__complete plugin update args ''",
@ -317,6 +322,11 @@ func TestPluginCmdsCompletion(t *testing.T) {
cmd: "__complete plugin uninstall ''",
golden: "output/plugin_list_comp.txt",
rels: []*release.Release{},
}, {
name: "completion for plugin uninstall, no filter",
cmd: "__complete plugin uninstall full",
golden: "output/plugin_list_comp.txt",
rels: []*release.Release{},
}, {
name: "completion for plugin uninstall repetition",
cmd: "__complete plugin uninstall args ''",

@ -64,10 +64,6 @@ func newPullCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
client.Version = ">0.0.0-0"
}
if err := checkOCI(args[0]); err != nil {
return err
}
for i := 0; i < len(args); i++ {
output, err := client.Run(args[i])
if err != nil {

@ -34,7 +34,6 @@ func TestPullCmd(t *testing.T) {
}
defer srv.Stop()
os.Setenv("HELM_EXPERIMENTAL_OCI", "1")
ociSrv, err := repotest.NewOCIServer(t, srv.Root())
if err != nil {
t.Fatal(err)
@ -371,6 +370,10 @@ func TestPullVersionCompletion(t *testing.T) {
name: "completion for pull version flag",
cmd: fmt.Sprintf("%s __complete pull testing/alpine --version ''", repoSetup),
golden: "output/version-comp.txt",
}, {
name: "completion for pull version flag, no filter",
cmd: fmt.Sprintf("%s __complete pull testing/alpine --version 0.3", repoSetup),
golden: "output/version-comp.txt",
}, {
name: "completion for pull version flag too few args",
cmd: fmt.Sprintf("%s __complete pull --version ''", repoSetup),

@ -23,8 +23,8 @@ import (
"github.com/spf13/cobra"
"helm.sh/helm/v3/cmd/helm/require"
experimental "helm.sh/helm/v3/internal/experimental/action"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/pusher"
)
const pushDesc = `
@ -35,15 +35,30 @@ it will also be uploaded.
`
func newPushCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
client := experimental.NewPushWithOpts(experimental.WithPushConfig(cfg))
client := action.NewPushWithOpts(action.WithPushConfig(cfg))
cmd := &cobra.Command{
Use: "push [chart] [remote]",
Short: "push a chart to remote",
Long: pushDesc,
Hidden: !FeatureGateOCI.IsEnabled(),
PersistentPreRunE: checkOCIFeatureGate(),
Args: require.MinimumNArgs(2),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) == 0 {
// Do file completion for the chart file to push
return nil, cobra.ShellCompDirectiveDefault
}
if len(args) == 1 {
providers := []pusher.Provider(pusher.All(settings))
var comps []string
for _, p := range providers {
for _, scheme := range p.Schemes {
comps = append(comps, fmt.Sprintf("%s://", scheme))
}
}
return comps, cobra.ShellCompDirectiveNoFileComp | cobra.ShellCompDirectiveNoSpace
}
return nil, cobra.ShellCompDirectiveNoFileComp
},
RunE: func(cmd *cobra.Command, args []string) error {
chartRef := args[0]
remote := args[1]

@ -0,0 +1,27 @@
/*
Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"testing"
)
func TestPushFileCompletion(t *testing.T) {
checkFileCompletion(t, "push", true)
checkFileCompletion(t, "push package.tgz", false)
checkFileCompletion(t, "push package.tgz oci://localhost:5000", false)
}

@ -32,8 +32,6 @@ func newRegistryCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
Use: "registry",
Short: "login to or logout from a registry",
Long: registryHelp,
Hidden: !FeatureGateOCI.IsEnabled(),
PersistentPreRunE: checkOCIFeatureGate(),
}
cmd.AddCommand(
newRegistryLoginCmd(cfg, out),

@ -25,11 +25,10 @@ import (
"os"
"strings"
"github.com/docker/docker/pkg/term"
"github.com/docker/docker/pkg/term" //nolint
"github.com/spf13/cobra"
"helm.sh/helm/v3/cmd/helm/require"
experimental "helm.sh/helm/v3/internal/experimental/action"
"helm.sh/helm/v3/pkg/action"
)
@ -46,7 +45,7 @@ func newRegistryLoginCmd(cfg *action.Configuration, out io.Writer) *cobra.Comman
Short: "login to a registry",
Long: registryLoginDesc,
Args: require.MinimumNArgs(1),
Hidden: !FeatureGateOCI.IsEnabled(),
ValidArgsFunction: noCompletions,
RunE: func(cmd *cobra.Command, args []string) error {
hostname := args[0]
@ -55,7 +54,7 @@ func newRegistryLoginCmd(cfg *action.Configuration, out io.Writer) *cobra.Comman
return err
}
return experimental.NewRegistryLogin(cfg).Run(out, hostname, username, password, insecureOpt)
return action.NewRegistryLogin(cfg).Run(out, hostname, username, password, insecureOpt)
},
}

@ -0,0 +1,25 @@
/*
Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"testing"
)
func TestRegistryLoginFileCompletion(t *testing.T) {
checkFileCompletion(t, "registry login", false)
}

@ -22,7 +22,6 @@ import (
"github.com/spf13/cobra"
"helm.sh/helm/v3/cmd/helm/require"
experimental "helm.sh/helm/v3/internal/experimental/action"
"helm.sh/helm/v3/pkg/action"
)
@ -36,10 +35,10 @@ func newRegistryLogoutCmd(cfg *action.Configuration, out io.Writer) *cobra.Comma
Short: "logout from a registry",
Long: registryLogoutDesc,
Args: require.MinimumNArgs(1),
Hidden: !FeatureGateOCI.IsEnabled(),
ValidArgsFunction: noCompletions,
RunE: func(cmd *cobra.Command, args []string) error {
hostname := args[0]
return experimental.NewRegistryLogout(cfg).Run(out, hostname)
return action.NewRegistryLogout(cfg).Run(out, hostname)
},
}
}

@ -0,0 +1,25 @@
/*
Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"testing"
)
func TestRegistryLogoutFileCompletion(t *testing.T) {
checkFileCompletion(t, "registry logout", false)
}

@ -19,7 +19,6 @@ package main
import (
"fmt"
"io"
"strings"
"github.com/gosuri/uitable"
"github.com/pkg/errors"
@ -131,10 +130,8 @@ func compListRepos(prefix string, ignoredRepoNames []string) []string {
if err == nil && len(f.Repositories) > 0 {
filteredRepos := filterRepos(f.Repositories, ignoredRepoNames)
for _, repo := range filteredRepos {
if strings.HasPrefix(repo.Name, prefix) {
rNames = append(rNames, fmt.Sprintf("%s\t%s", repo.Name, repo.URL))
}
}
}
return rNames
}

@ -197,6 +197,10 @@ func TestRepoRemoveCompletion(t *testing.T) {
name: "completion for repo remove",
cmd: fmt.Sprintf("%s __completeNoDesc repo remove ''", repoSetup),
golden: "output/repo_list_comp.txt",
}, {
name: "completion for repo remove, no filter",
cmd: fmt.Sprintf("%s __completeNoDesc repo remove fo", repoSetup),
golden: "output/repo_list_comp.txt",
}, {
name: "completion for repo remove repetition",
cmd: fmt.Sprintf("%s __completeNoDesc repo remove foo ''", repoSetup),

@ -133,8 +133,8 @@ func updateCharts(repos []*repo.ChartRepository, out io.Writer, failOnRepoUpdate
wg.Wait()
if len(repoFailList) > 0 && failOnRepoUpdateFail {
return errors.New(fmt.Sprintf("Failed to update the following repositories: %s",
repoFailList))
return fmt.Errorf("Failed to update the following repositories: %s",
repoFailList)
}
fmt.Fprintln(out, "Update Complete. ⎈Happy Helming!⎈")

@ -29,8 +29,8 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/clientcmd"
"helm.sh/helm/v3/internal/experimental/registry"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/registry"
"helm.sh/helm/v3/pkg/repo"
)
@ -106,10 +106,8 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string
nsNames := []string{}
if namespaces, err := client.CoreV1().Namespaces().List(context.Background(), metav1.ListOptions{TimeoutSeconds: &to}); err == nil {
for _, ns := range namespaces.Items {
if strings.HasPrefix(ns.Name, toComplete) {
nsNames = append(nsNames, ns.Name)
}
}
return nsNames, cobra.ShellCompDirectiveNoFileComp
}
}
@ -133,10 +131,8 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string
&clientcmd.ConfigOverrides{}).RawConfig(); err == nil {
comps := []string{}
for name, context := range config.Contexts {
if strings.HasPrefix(name, toComplete) {
comps = append(comps, fmt.Sprintf("%s\t%s", name, context.Cluster))
}
}
return comps, cobra.ShellCompDirectiveNoFileComp
}
return nil, cobra.ShellCompDirectiveNoFileComp
@ -169,7 +165,7 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string
newCreateCmd(out),
newDependencyCmd(actionConfig, out),
newPullCmd(actionConfig, out),
newShowCmd(out),
newShowCmd(actionConfig, out),
newLintCmd(out),
newPackageCmd(out),
newRepoCmd(out),
@ -197,7 +193,6 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string
newDocsCmd(out),
)
// Add *experimental* subcommands
cmd.AddCommand(
newRegistryCmd(actionConfig, out),
newPushCmd(actionConfig, out),
@ -262,12 +257,3 @@ func checkForExpiredRepos(repofile string) {
}
}
// When dealing with OCI-based charts, ensure that the user has
// enabled the experimental feature gate prior to continuing
func checkOCI(ref string) error {
if registry.IsOCI(ref) && !FeatureGateOCI.IsEnabled() {
return FeatureGateOCI.Error()
}
return nil
}

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows
/*

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows
/*

@ -53,6 +53,7 @@ type searchHubOptions struct {
searchEndpoint string
maxColWidth uint
outputFormat output.Format
listRepoURL bool
}
func newSearchHubCmd(out io.Writer) *cobra.Command {
@ -70,6 +71,8 @@ func newSearchHubCmd(out io.Writer) *cobra.Command {
f := cmd.Flags()
f.StringVar(&o.searchEndpoint, "endpoint", "https://hub.helm.sh", "Hub instance to query for charts")
f.UintVar(&o.maxColWidth, "max-col-width", 50, "maximum column width for output table")
f.BoolVar(&o.listRepoURL, "list-repo-url", false, "print charts repository URL")
bindOutputFlag(cmd, &o.outputFormat)
return cmd
@ -88,7 +91,12 @@ func (o *searchHubOptions) run(out io.Writer, args []string) error {
return fmt.Errorf("unable to perform search against %q", o.searchEndpoint)
}
return o.outputFormat.Write(out, newHubSearchWriter(results, o.searchEndpoint, o.maxColWidth))
return o.outputFormat.Write(out, newHubSearchWriter(results, o.searchEndpoint, o.maxColWidth, o.listRepoURL))
}
type hubChartRepo struct {
URL string `json:"url"`
Name string `json:"name"`
}
type hubChartElement struct {
@ -96,14 +104,16 @@ type hubChartElement struct {
Version string `json:"version"`
AppVersion string `json:"app_version"`
Description string `json:"description"`
Repository hubChartRepo `json:"repository"`
}
type hubSearchWriter struct {
elements []hubChartElement
columnWidth uint
listRepoURL bool
}
func newHubSearchWriter(results []monocular.SearchResult, endpoint string, columnWidth uint) *hubSearchWriter {
func newHubSearchWriter(results []monocular.SearchResult, endpoint string, columnWidth uint, listRepoURL bool) *hubSearchWriter {
var elements []hubChartElement
for _, r := range results {
// Backwards compatibility for Monocular
@ -114,9 +124,9 @@ func newHubSearchWriter(results []monocular.SearchResult, endpoint string, colum
url = r.ArtifactHub.PackageURL
}
elements = append(elements, hubChartElement{url, r.Relationships.LatestChartVersion.Data.Version, r.Relationships.LatestChartVersion.Data.AppVersion, r.Attributes.Description})
elements = append(elements, hubChartElement{url, r.Relationships.LatestChartVersion.Data.Version, r.Relationships.LatestChartVersion.Data.AppVersion, r.Attributes.Description, hubChartRepo{URL: r.Attributes.Repo.URL, Name: r.Attributes.Repo.Name}})
}
return &hubSearchWriter{elements, columnWidth}
return &hubSearchWriter{elements, columnWidth, listRepoURL}
}
func (h *hubSearchWriter) WriteTable(out io.Writer) error {
@ -129,10 +139,20 @@ func (h *hubSearchWriter) WriteTable(out io.Writer) error {
}
table := uitable.New()
table.MaxColWidth = h.columnWidth
if h.listRepoURL {
table.AddRow("URL", "CHART VERSION", "APP VERSION", "DESCRIPTION", "REPO URL")
} else {
table.AddRow("URL", "CHART VERSION", "APP VERSION", "DESCRIPTION")
}
for _, r := range h.elements {
if h.listRepoURL {
table.AddRow(r.URL, r.Version, r.AppVersion, r.Description, r.Repository.URL)
} else {
table.AddRow(r.URL, r.Version, r.AppVersion, r.Description)
}
}
return output.EncodeTable(out, table)
}
@ -149,7 +169,7 @@ func (h *hubSearchWriter) encodeByFormat(out io.Writer, format output.Format) er
chartList := make([]hubChartElement, 0, len(h.elements))
for _, r := range h.elements {
chartList = append(chartList, hubChartElement{r.URL, r.Version, r.AppVersion, r.Description})
chartList = append(chartList, hubChartElement{r.URL, r.Version, r.AppVersion, r.Description, r.Repository})
}
switch format {

@ -33,6 +33,8 @@ func TestSearchHubCmd(t *testing.T) {
defer ts.Close()
// The expected output has the URL to the mocked search service in it
// Trailing spaces are necessary to preserve in "expected" as the uitable package adds
// them during printing.
var expected = fmt.Sprintf(`URL CHART VERSION APP VERSION DESCRIPTION
%s/charts/stable/phpmyadmin 3.0.0 4.9.0-1 phpMyAdmin is an mysql administration frontend
%s/charts/bitnami/phpmyadmin 3.0.0 4.9.0-1 phpMyAdmin is an mysql administration frontend
@ -51,6 +53,36 @@ func TestSearchHubCmd(t *testing.T) {
}
}
func TestSearchHubListRepoCmd(t *testing.T) {
// Setup a mock search service
var searchResult = `{"data":[{"id":"stable/phpmyadmin","type":"chart","attributes":{"name":"phpmyadmin","repo":{"name":"stable","url":"https://charts.helm.sh/stable"},"description":"phpMyAdmin is an mysql administration frontend","home":"https://www.phpmyadmin.net/","keywords":["mariadb","mysql","phpmyadmin"],"maintainers":[{"name":"Bitnami","email":"containers@bitnami.com"}],"sources":["https://github.com/bitnami/bitnami-docker-phpmyadmin"],"icon":""},"links":{"self":"/v1/charts/stable/phpmyadmin"},"relationships":{"latestChartVersion":{"data":{"version":"3.0.0","app_version":"4.9.0-1","created":"2019-08-08T17:57:31.38Z","digest":"119c499251bffd4b06ff0cd5ac98c2ce32231f84899fb4825be6c2d90971c742","urls":["https://charts.helm.sh/stable/phpmyadmin-3.0.0.tgz"],"readme":"/v1/assets/stable/phpmyadmin/versions/3.0.0/README.md","values":"/v1/assets/stable/phpmyadmin/versions/3.0.0/values.yaml"},"links":{"self":"/v1/charts/stable/phpmyadmin/versions/3.0.0"}}}},{"id":"bitnami/phpmyadmin","type":"chart","attributes":{"name":"phpmyadmin","repo":{"name":"bitnami","url":"https://charts.bitnami.com"},"description":"phpMyAdmin is an mysql administration frontend","home":"https://www.phpmyadmin.net/","keywords":["mariadb","mysql","phpmyadmin"],"maintainers":[{"name":"Bitnami","email":"containers@bitnami.com"}],"sources":["https://github.com/bitnami/bitnami-docker-phpmyadmin"],"icon":""},"links":{"self":"/v1/charts/bitnami/phpmyadmin"},"relationships":{"latestChartVersion":{"data":{"version":"3.0.0","app_version":"4.9.0-1","created":"2019-08-08T18:34:13.341Z","digest":"66d77cf6d8c2b52c488d0a294cd4996bd5bad8dc41d3829c394498fb401c008a","urls":["https://charts.bitnami.com/bitnami/phpmyadmin-3.0.0.tgz"],"readme":"/v1/assets/bitnami/phpmyadmin/versions/3.0.0/README.md","values":"/v1/assets/bitnami/phpmyadmin/versions/3.0.0/values.yaml"},"links":{"self":"/v1/charts/bitnami/phpmyadmin/versions/3.0.0"}}}}]}`
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, searchResult)
}))
defer ts.Close()
// The expected output has the URL to the mocked search service in it
// Trailing spaces are necessary to preserve in "expected" as the uitable package adds
// them during printing.
var expected = fmt.Sprintf(`URL CHART VERSION APP VERSION DESCRIPTION REPO URL
%s/charts/stable/phpmyadmin 3.0.0 4.9.0-1 phpMyAdmin is an mysql administration frontend https://charts.helm.sh/stable
%s/charts/bitnami/phpmyadmin 3.0.0 4.9.0-1 phpMyAdmin is an mysql administration frontend https://charts.bitnami.com
`, ts.URL, ts.URL)
testcmd := "search hub --list-repo-url --endpoint " + ts.URL + " maria"
storage := storageFixture()
_, out, err := executeActionCommandC(storage, testcmd)
if err != nil {
t.Errorf("unexpected error, %s", err)
}
if out != expected {
t.Error("expected and actual output did not match")
t.Log(out)
t.Log(expected)
}
}
func TestSearchHubOutputCompletion(t *testing.T) {
outputFlagCompletionTest(t, "search hub")
}

@ -312,8 +312,9 @@ func compListCharts(toComplete string, includeFiles bool) ([]string, cobra.Shell
}
repoWithSlash := fmt.Sprintf("%s/", repo)
if strings.HasPrefix(toComplete, repoWithSlash) {
// Must complete with charts within the specified repo
completions = append(completions, compListChartsOfRepo(repo, toComplete)...)
// Must complete with charts within the specified repo.
// Don't filter on toComplete to allow for shell fuzzy matching
completions = append(completions, compListChartsOfRepo(repo, "")...)
noSpace = false
break
} else if strings.HasPrefix(repo, toComplete) {
@ -325,7 +326,7 @@ func compListCharts(toComplete string, includeFiles bool) ([]string, cobra.Shell
cobra.CompDebugln(fmt.Sprintf("Completions after repos: %v", completions), settings.Debug)
// Now handle completions for url prefixes
for _, url := range []string{"https://\tChart URL prefix", "http://\tChart URL prefix", "file://\tChart local URL prefix"} {
for _, url := range []string{"oci://\tChart OCI prefix", "https://\tChart URL prefix", "http://\tChart URL prefix", "file://\tChart local URL prefix"} {
if strings.HasPrefix(toComplete, url) {
// The user already put in the full url prefix; we don't have
// anything to add, but make sure the shell does not default

@ -53,11 +53,11 @@ of the README file
const showCRDsDesc = `
This command inspects a chart (directory, file, or URL) and displays the contents
of the CustomResourceDefintion files
of the CustomResourceDefinition files
`
func newShowCmd(out io.Writer) *cobra.Command {
client := action.NewShow(action.ShowAll)
func newShowCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
client := action.NewShowWithConfig(action.ShowAll, cfg)
showCommand := &cobra.Command{
Use: "show",
@ -198,10 +198,6 @@ func runShow(args []string, client *action.Show) (string, error) {
client.Version = ">0.0.0-0"
}
if err := checkOCI(args[0]); err != nil {
return "", err
}
cp, err := client.ChartPathOptions.LocateChart(args[0], settings)
if err != nil {
return "", err

@ -98,6 +98,10 @@ func TestShowVersionCompletion(t *testing.T) {
name: "completion for show version flag",
cmd: fmt.Sprintf("%s __complete show chart testing/alpine --version ''", repoSetup),
golden: "output/version-comp.txt",
}, {
name: "completion for show version flag, no filter",
cmd: fmt.Sprintf("%s __complete show chart testing/alpine --version 0.3", repoSetup),
golden: "output/version-comp.txt",
}, {
name: "completion for show version flag too few args",
cmd: fmt.Sprintf("%s __complete show chart --version ''", repoSetup),

@ -74,7 +74,7 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
}
client.DryRun = true
client.ReleaseName = "RELEASE-NAME"
client.ReleaseName = "release-name"
client.Replace = true // Skip the name check
client.ClientOnly = !validate
client.APIVersions = chartutil.VersionSet(extraAPIs)

@ -43,7 +43,7 @@ func TestTemplateCmd(t *testing.T) {
},
{
name: "check name template",
cmd: fmt.Sprintf(`template '%s' --name-template='foobar-{{ b64enc "abc" }}-baz'`, chartPath),
cmd: fmt.Sprintf(`template '%s' --name-template='foobar-{{ b64enc "abc" | lower }}-baz'`, chartPath),
golden: "output/template-name-template.txt",
},
{

@ -1,3 +0,0 @@
markdown
:4
Completion ended with directive: ShellCompDirectiveNoFileComp

@ -1,5 +1,4 @@
RELEASE NAME: FOOBAR
CHART: empty-0.1.0
NAME: foobar
LAST DEPLOYED: Fri Sep 2 22:04:05 1977
NAMESPACE: default
STATUS: deployed

@ -1,4 +1,5 @@
aramis Aramis-chart-0.0.0 -> uninstalled
athos Athos-chart-1.2.3 -> deployed
porthos Porthos-chart-111.222.333 -> failed
:4
Completion ended with directive: ShellCompDirectiveNoFileComp

@ -7,7 +7,7 @@ metadata:
app: chart-with-template-lib-archive-dep
chart: chart-with-template-lib-archive-dep-0.1.0
heritage: Helm
release: RELEASE-NAME
release: release-name
name: release-name-chart-with-template-lib-archive-dep
spec:
ports:
@ -16,30 +16,30 @@ spec:
targetPort: http
selector:
app: chart-with-template-lib-archive-dep
release: RELEASE-NAME
release: release-name
type: ClusterIP
---
# Source: chart-with-template-lib-archive-dep/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: RELEASE-NAME-chart-with-template-lib-archive-dep
name: release-name-chart-with-template-lib-archive-dep
labels:
app: chart-with-template-lib-archive-dep
chart: chart-with-template-lib-archive-dep-0.1.0
release: RELEASE-NAME
release: release-name
heritage: Helm
spec:
replicas: 1
selector:
matchLabels:
app: chart-with-template-lib-archive-dep
release: RELEASE-NAME
release: release-name
template:
metadata:
labels:
app: chart-with-template-lib-archive-dep
release: RELEASE-NAME
release: release-name
spec:
containers:
- name: chart-with-template-lib-archive-dep

@ -7,7 +7,7 @@ metadata:
app: chart-with-template-lib-dep
chart: chart-with-template-lib-dep-0.1.0
heritage: Helm
release: RELEASE-NAME
release: release-name
name: release-name-chart-with-template-lib-dep
spec:
ports:
@ -16,30 +16,30 @@ spec:
targetPort: http
selector:
app: chart-with-template-lib-dep
release: RELEASE-NAME
release: release-name
type: ClusterIP
---
# Source: chart-with-template-lib-dep/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: RELEASE-NAME-chart-with-template-lib-dep
name: release-name-chart-with-template-lib-dep
labels:
app: chart-with-template-lib-dep
chart: chart-with-template-lib-dep-0.1.0
release: RELEASE-NAME
release: release-name
heritage: Helm
spec:
replicas: 1
selector:
matchLabels:
app: chart-with-template-lib-dep
release: RELEASE-NAME
release: release-name
template:
metadata:
labels:
app: chart-with-template-lib-dep
release: RELEASE-NAME
release: release-name
spec:
containers:
- name: chart-with-template-lib-dep

@ -70,7 +70,7 @@ metadata:
name: subchart
labels:
helm.sh/chart: "subchart-0.1.0"
app.kubernetes.io/instance: "foobar-YWJj-baz"
app.kubernetes.io/instance: "foobar-ywjj-baz"
kube-version/major: "1"
kube-version/minor: "20"
kube-version/version: "v1.20.0"
@ -88,7 +88,7 @@ spec:
apiVersion: v1
kind: ConfigMap
metadata:
name: "foobar-YWJj-baz-testconfig"
name: "foobar-ywjj-baz-testconfig"
annotations:
"helm.sh/hook": test
data:
@ -98,7 +98,7 @@ data:
apiVersion: v1
kind: Pod
metadata:
name: "foobar-YWJj-baz-test"
name: "foobar-ywjj-baz-test"
annotations:
"helm.sh/hook": test
spec:
@ -107,7 +107,7 @@ spec:
image: "alpine:latest"
envFrom:
- configMapRef:
name: "foobar-YWJj-baz-testconfig"
name: "foobar-ywjj-baz-testconfig"
command:
- echo
- "$message"

@ -70,7 +70,7 @@ metadata:
name: subchart
labels:
helm.sh/chart: "subchart-0.1.0"
app.kubernetes.io/instance: "RELEASE-NAME"
app.kubernetes.io/instance: "release-name"
kube-version/major: "1"
kube-version/minor: "20"
kube-version/version: "v1.20.0"
@ -88,7 +88,7 @@ spec:
apiVersion: v1
kind: ConfigMap
metadata:
name: "RELEASE-NAME-testconfig"
name: "release-name-testconfig"
annotations:
"helm.sh/hook": test
data:
@ -98,7 +98,7 @@ data:
apiVersion: v1
kind: Pod
metadata:
name: "RELEASE-NAME-test"
name: "release-name-test"
annotations:
"helm.sh/hook": test
spec:
@ -107,7 +107,7 @@ spec:
image: "alpine:latest"
envFrom:
- configMapRef:
name: "RELEASE-NAME-testconfig"
name: "release-name-testconfig"
command:
- echo
- "$message"

@ -6,7 +6,7 @@ metadata:
name: subchart
labels:
helm.sh/chart: "subchart-0.1.0"
app.kubernetes.io/instance: "RELEASE-NAME"
app.kubernetes.io/instance: "release-name"
kube-version/major: "1"
kube-version/minor: "20"
kube-version/version: "v1.20.0"

@ -6,7 +6,7 @@ metadata:
name: subchart
labels:
helm.sh/chart: "subchart-0.1.0"
app.kubernetes.io/instance: "RELEASE-NAME"
app.kubernetes.io/instance: "release-name"
kube-version/major: "1"
kube-version/minor: "20"
kube-version/version: "v1.20.0"

@ -70,7 +70,7 @@ metadata:
name: subchart
labels:
helm.sh/chart: "subchart-0.1.0"
app.kubernetes.io/instance: "RELEASE-NAME"
app.kubernetes.io/instance: "release-name"
kube-version/major: "1"
kube-version/minor: "20"
kube-version/version: "v1.20.0"

@ -70,7 +70,7 @@ metadata:
name: subchart
labels:
helm.sh/chart: "subchart-0.1.0"
app.kubernetes.io/instance: "RELEASE-NAME"
app.kubernetes.io/instance: "release-name"
kube-version/major: "1"
kube-version/minor: "20"
kube-version/version: "v1.20.0"
@ -88,7 +88,7 @@ spec:
apiVersion: v1
kind: ConfigMap
metadata:
name: "RELEASE-NAME-testconfig"
name: "release-name-testconfig"
annotations:
"helm.sh/hook": test
data:
@ -98,7 +98,7 @@ data:
apiVersion: v1
kind: Pod
metadata:
name: "RELEASE-NAME-test"
name: "release-name-test"
annotations:
"helm.sh/hook": test
spec:
@ -107,7 +107,7 @@ spec:
image: "alpine:latest"
envFrom:
- configMapRef:
name: "RELEASE-NAME-testconfig"
name: "release-name-testconfig"
command:
- echo
- "$message"

@ -70,7 +70,7 @@ metadata:
name: subchart
labels:
helm.sh/chart: "subchart-0.1.0"
app.kubernetes.io/instance: "RELEASE-NAME"
app.kubernetes.io/instance: "release-name"
kube-version/major: "1"
kube-version/minor: "20"
kube-version/version: "v1.20.0"
@ -89,7 +89,7 @@ spec:
apiVersion: v1
kind: ConfigMap
metadata:
name: "RELEASE-NAME-testconfig"
name: "release-name-testconfig"
annotations:
"helm.sh/hook": test
data:
@ -99,7 +99,7 @@ data:
apiVersion: v1
kind: Pod
metadata:
name: "RELEASE-NAME-test"
name: "release-name-test"
annotations:
"helm.sh/hook": test
spec:
@ -108,7 +108,7 @@ spec:
image: "alpine:latest"
envFrom:
- configMapRef:
name: "RELEASE-NAME-testconfig"
name: "release-name-testconfig"
command:
- echo
- "$message"

@ -87,7 +87,7 @@ metadata:
name: subchart
labels:
helm.sh/chart: "subchart-0.1.0"
app.kubernetes.io/instance: "RELEASE-NAME"
app.kubernetes.io/instance: "release-name"
kube-version/major: "1"
kube-version/minor: "20"
kube-version/version: "v1.20.0"
@ -105,7 +105,7 @@ spec:
apiVersion: v1
kind: ConfigMap
metadata:
name: "RELEASE-NAME-testconfig"
name: "release-name-testconfig"
annotations:
"helm.sh/hook": test
data:
@ -115,7 +115,7 @@ data:
apiVersion: v1
kind: Pod
metadata:
name: "RELEASE-NAME-test"
name: "release-name-test"
annotations:
"helm.sh/hook": test
spec:
@ -124,7 +124,7 @@ spec:
image: "alpine:latest"
envFrom:
- configMapRef:
name: "RELEASE-NAME-testconfig"
name: "release-name-testconfig"
command:
- echo
- "$message"

@ -3,7 +3,7 @@
apiVersion: v1
kind: Pod
metadata:
name: "RELEASE-NAME-my-alpine"
name: "release-name-my-alpine"
spec:
containers:
- name: waiter

@ -70,7 +70,7 @@ metadata:
name: subchart
labels:
helm.sh/chart: "subchart-0.1.0"
app.kubernetes.io/instance: "RELEASE-NAME"
app.kubernetes.io/instance: "release-name"
kube-version/major: "1"
kube-version/minor: "16"
kube-version/version: "v1.16.0"
@ -88,7 +88,7 @@ spec:
apiVersion: v1
kind: ConfigMap
metadata:
name: "RELEASE-NAME-testconfig"
name: "release-name-testconfig"
annotations:
"helm.sh/hook": test
data:
@ -98,7 +98,7 @@ data:
apiVersion: v1
kind: Pod
metadata:
name: "RELEASE-NAME-test"
name: "release-name-test"
annotations:
"helm.sh/hook": test
spec:
@ -107,7 +107,7 @@ spec:
image: "alpine:latest"
envFrom:
- configMapRef:
name: "RELEASE-NAME-testconfig"
name: "release-name-testconfig"
command:
- echo
- "$message"

@ -70,7 +70,7 @@ metadata:
name: subchart
labels:
helm.sh/chart: "subchart-0.1.0"
app.kubernetes.io/instance: "RELEASE-NAME"
app.kubernetes.io/instance: "release-name"
kube-version/major: "1"
kube-version/minor: "20"
kube-version/version: "v1.20.0"
@ -88,7 +88,7 @@ spec:
apiVersion: v1
kind: ConfigMap
metadata:
name: "RELEASE-NAME-testconfig"
name: "release-name-testconfig"
annotations:
"helm.sh/hook": test
data:
@ -98,7 +98,7 @@ data:
apiVersion: v1
kind: Pod
metadata:
name: "RELEASE-NAME-test"
name: "release-name-test"
annotations:
"helm.sh/hook": test
spec:
@ -107,7 +107,7 @@ spec:
image: "alpine:latest"
envFrom:
- configMapRef:
name: "RELEASE-NAME-testconfig"
name: "release-name-testconfig"
command:
- echo
- "$message"

@ -1 +1 @@
Error: found in Chart.yaml, but missing in charts/ directory: reqsubchart2
Error: An error occurred while checking for chart dependencies. You may need to run `helm dependency build` to fetch missing dependencies: found in Chart.yaml, but missing in charts/ directory: reqsubchart2

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

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

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

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

@ -13,7 +13,7 @@ A few tips for working with Common:
- Be careful when using functions that generate random data (like `common.fullname.unique`).
They may trigger unwanted upgrades or have other side effects.
In this document, we use `RELEASE-NAME` as the name of the release.
In this document, we use `release-name` as the name of the release.
## Resource Kinds
@ -733,7 +733,7 @@ metadata:
labels:
app: metadata
heritage: "Tiller"
release: "RELEASE-NAME"
release: "release-name"
chart: metadata-0.1.0
first: "matt"
last: "butcher"
@ -748,7 +748,7 @@ metadata:
labels:
app: metadata
heritage: "Tiller"
release: "RELEASE-NAME"
release: "release-name"
chart: metadata-0.1.0
annotations:
```
@ -791,7 +791,7 @@ Example output:
```yaml
app: labelizer
heritage: "Tiller"
release: "RELEASE-NAME"
release: "release-name"
chart: labelizer-0.1.0
```

@ -13,7 +13,7 @@ A few tips for working with Common:
- Be careful when using functions that generate random data (like `common.fullname.unique`).
They may trigger unwanted upgrades or have other side effects.
In this document, we use `RELEASE-NAME` as the name of the release.
In this document, we use `release-name` as the name of the release.
## Resource Kinds
@ -733,7 +733,7 @@ metadata:
labels:
app.kubernetes.io/name: metadata
app.kubernetes.io/managed-by: "Helm"
app.kubernetes.io/instance: "RELEASE-NAME"
app.kubernetes.io/instance: "release-name"
helm.sh/chart: metadata-0.1.0
first: "matt"
last: "butcher"
@ -748,7 +748,7 @@ metadata:
labels:
app.kubernetes.io/name: metadata
app.kubernetes.io/managed-by: "Helm"
app.kubernetes.io/instance: "RELEASE-NAME"
app.kubernetes.io/instance: "release-name"
helm.sh/chart: metadata-0.1.0
annotations:
```
@ -791,7 +791,7 @@ Example output:
```yaml
app.kubernetes.io/name: labelizer
app.kubernetes.io/managed-by: "Tiller"
app.kubernetes.io/instance: "RELEASE-NAME"
app.kubernetes.io/instance: "release-name"
helm.sh/chart: labelizer-0.1.0
```

@ -49,9 +49,9 @@ version will be specified unless the '--version' flag is set.
To override values in a chart, use either the '--values' flag and pass in a file
or use the '--set' flag and pass configuration from the command line, to force string
values, use '--set-string'. In case a value is large and therefore
you want not to use neither '--values' nor '--set', use '--set-file' to read the
single large value from file.
values, use '--set-string'. You can use '--set-file' to set individual
values from a file when the value itself is too long for the command line
or is dynamically generated.
You can specify the '--values'/'-f' flag multiple times. The priority will be given to the
last (right-most) file specified. For example, if both myvalues.yaml and override.yaml
@ -87,10 +87,6 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
return nil, cobra.ShellCompDirectiveNoFileComp
},
RunE: func(cmd *cobra.Command, args []string) error {
if err := checkOCI(args[1]); err != nil {
return err
}
client.Namespace = settings.Namespace()
// Fixes #7002 - Support reading values from STDIN for `upgrade` command
@ -154,6 +150,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
}
if req := ch.Metadata.Dependencies; req != nil {
if err := action.CheckDependencies(ch, req); err != nil {
err = errors.Wrap(err, "An error occurred while checking for chart dependencies. You may need to run `helm dependency build` to fetch missing dependencies")
if client.DependencyUpdate {
man := &downloader.Manager{
Out: out,
@ -186,8 +183,10 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
// Handle SIGTERM
cSignal := make(chan os.Signal)
// Set up channel on which to send signal notifications.
// We must use a buffered channel or risk missing the signal
// if we're not ready to receive when the signal is sent.
cSignal := make(chan os.Signal, 2)
signal.Notify(cSignal, os.Interrupt, syscall.SIGTERM)
go func() {
<-cSignal

@ -406,6 +406,10 @@ func TestUpgradeVersionCompletion(t *testing.T) {
name: "completion for upgrade version flag",
cmd: fmt.Sprintf("%s __complete upgrade releasename testing/alpine --version ''", repoSetup),
golden: "output/version-comp.txt",
}, {
name: "completion for upgrade version flag, no filter",
cmd: fmt.Sprintf("%s __complete upgrade releasename testing/alpine --version 0.3", repoSetup),
golden: "output/version-comp.txt",
}, {
name: "completion for upgrade version flag too few args",
cmd: fmt.Sprintf("%s __complete upgrade releasename --version ''", repoSetup),

@ -3,46 +3,45 @@ module helm.sh/helm/v3
go 1.16
require (
github.com/BurntSushi/toml v0.3.1
github.com/BurntSushi/toml v0.4.1
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.0
github.com/Masterminds/squirrel v1.5.2
github.com/Masterminds/vcs v1.13.1
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535
github.com/containerd/containerd v1.5.7
github.com/containerd/containerd v1.5.9
github.com/cyphar/filepath-securejoin v0.2.3
github.com/distribution/distribution/v3 v3.0.0-20210804104954-38ab4c606ee3
github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible
github.com/evanphx/json-patch v4.11.0+incompatible
github.com/distribution/distribution/v3 v3.0.0-20211118083504-a29a3c99a684
github.com/docker/docker v20.10.12+incompatible
github.com/evanphx/json-patch v4.12.0+incompatible
github.com/gobwas/glob v0.2.3
github.com/gofrs/flock v0.8.0
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.3
github.com/mattn/go-shellwords v1.0.11
github.com/mitchellh/copystructure v1.1.1
github.com/opencontainers/image-spec v1.0.1
github.com/lib/pq v1.10.4
github.com/mattn/go-shellwords v1.0.12
github.com/mitchellh/copystructure v1.2.0
github.com/opencontainers/image-spec v1.0.2
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2
github.com/pkg/errors v0.9.1
github.com/rubenv/sql-migrate v0.0.0-20210614095031-55d5740dbbcc
github.com/sirupsen/logrus v1.8.1
github.com/spf13/cobra v1.2.1
github.com/spf13/cobra v1.3.0
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.7.0
github.com/xeipuuv/gojsonschema v1.2.0
github.com/ziutek/mymysql v1.5.4 // indirect
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d
k8s.io/api v0.22.1
k8s.io/apiextensions-apiserver v0.22.1
k8s.io/apimachinery v0.22.1
k8s.io/apiserver v0.22.1
k8s.io/cli-runtime v0.22.1
k8s.io/client-go v0.22.1
k8s.io/klog/v2 v2.9.0
k8s.io/kubectl v0.22.1
oras.land/oras-go v0.4.0
rsc.io/letsencrypt v0.0.3 // indirect
sigs.k8s.io/yaml v1.2.0
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b
k8s.io/api v0.23.3
k8s.io/apiextensions-apiserver v0.23.3
k8s.io/apimachinery v0.23.3
k8s.io/apiserver v0.23.3
k8s.io/cli-runtime v0.23.3
k8s.io/client-go v0.23.3
k8s.io/klog/v2 v2.30.0
k8s.io/kubectl v0.23.3
oras.land/oras-go v1.1.0
sigs.k8s.io/yaml v1.3.0
)

456
go.sum

File diff suppressed because it is too large Load Diff

@ -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
}

@ -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
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,
registryClient: registryClient,
}
}
@ -135,9 +135,22 @@ 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)
// 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 +162,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
}

@ -20,6 +20,7 @@ import (
"testing"
"helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/registry"
)
func TestResolve(t *testing.T) {
@ -139,7 +140,8 @@ func TestResolve(t *testing.T) {
}
repoNames := map[string]string{"alpine": "kubernetes-charts", "redis": "kubernetes-charts"}
r := New("testdata/chartpath", "testdata/repository")
registryClient, _ := registry.NewClient()
r := New("testdata/chartpath", "testdata/repository", registryClient)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
l, err := r.Resolve(tt.req, repoNames)

@ -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.8"
// metadata is extra build time data
metadata = ""

@ -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"

@ -23,10 +23,10 @@ import (
fakeclientset "k8s.io/client-go/kubernetes/fake"
"helm.sh/helm/v3/internal/experimental/registry"
"helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/chartutil"
kubefake "helm.sh/helm/v3/pkg/kube/fake"
"helm.sh/helm/v3/pkg/registry"
"helm.sh/helm/v3/pkg/release"
"helm.sh/helm/v3/pkg/storage"
"helm.sh/helm/v3/pkg/storage/driver"

@ -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 {

@ -29,6 +29,7 @@ import (
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"helm.sh/helm/v3/internal/test"
"helm.sh/helm/v3/pkg/chart"
@ -56,9 +57,12 @@ func installAction(t *testing.T) *Install {
func TestInstallRelease(t *testing.T) {
is := assert.New(t)
req := require.New(t)
instAction := installAction(t)
vals := map[string]interface{}{}
res, err := instAction.Run(buildChart(), vals)
ctx, done := context.WithCancel(context.Background())
res, err := instAction.RunWithContext(ctx, buildChart(), vals)
if err != nil {
t.Fatalf("Failed install: %s", err)
}
@ -77,6 +81,14 @@ func TestInstallRelease(t *testing.T) {
is.NotEqual(len(rel.Manifest), 0)
is.Contains(rel.Manifest, "---\n# Source: hello/templates/hello\nhello: world")
is.Equal(rel.Info.Description, "Install complete")
// Detecting previous bug where context termination after successful release
// caused release to fail.
done()
time.Sleep(time.Millisecond * 100)
lastRelease, err := instAction.cfg.Releases.Last(rel.Name)
req.NoError(err)
is.Equal(lastRelease.Info.Status, release.StatusDeployed)
}
func TestInstallReleaseWithValues(t *testing.T) {
@ -132,7 +144,7 @@ func TestInstallRelease_NoName(t *testing.T) {
if err == nil {
t.Fatal("expected failure when no name is specified")
}
assert.Contains(t, err.Error(), "name is required")
assert.Contains(t, err.Error(), "no name provided")
}
func TestInstallRelease_WithNotes(t *testing.T) {

@ -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 {

@ -19,11 +19,10 @@ package action
import (
"strings"
"helm.sh/helm/v3/internal/experimental/pusher"
"helm.sh/helm/v3/internal/experimental/registry"
"helm.sh/helm/v3/internal/experimental/uploader"
"helm.sh/helm/v3/pkg/action"
"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.
@ -31,14 +30,14 @@ import (
// It provides the implementation of 'helm push'.
type Push struct {
Settings *cli.EnvSettings
cfg *action.Configuration
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 *action.Configuration) PushOpt {
func WithPushConfig(cfg *Configuration) PushOpt {
return func(p *Push) {
p.cfg = cfg
}

@ -19,17 +19,16 @@ package action
import (
"io"
"helm.sh/helm/v3/internal/experimental/registry"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/registry"
)
// RegistryLogin performs a registry login operation.
type RegistryLogin struct {
cfg *action.Configuration
cfg *Configuration
}
// NewRegistryLogin creates a new RegistryLogin object with the given configuration.
func NewRegistryLogin(cfg *action.Configuration) *RegistryLogin {
func NewRegistryLogin(cfg *Configuration) *RegistryLogin {
return &RegistryLogin{
cfg: cfg,
}

@ -18,17 +18,15 @@ package action
import (
"io"
"helm.sh/helm/v3/pkg/action"
)
// RegistryLogout performs a registry login operation.
type RegistryLogout struct {
cfg *action.Configuration
cfg *Configuration
}
// NewRegistryLogout creates a new RegistryLogout object with the given configuration.
func NewRegistryLogout(cfg *action.Configuration) *RegistryLogout {
func NewRegistryLogout(cfg *Configuration) *RegistryLogout {
return &RegistryLogout{
cfg: cfg,
}

@ -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 {

@ -23,7 +23,8 @@ import (
)
func TestShow(t *testing.T) {
client := NewShow(ShowAll)
config := actionConfigFixture(t)
client := NewShowWithConfig(ShowAll, config)
client.chart = &chart.Chart{
Metadata: &chart.Metadata{Name: "alpine"},
Files: []*chart.File{

@ -112,9 +112,12 @@ 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.
@ -321,11 +324,17 @@ func (u *Upgrade) performUpgrade(ctx context.Context, originalRelease, upgradedR
return nil, err
}
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, rChan, upgradedRelease)
result := <-rChan
go u.handleContext(ctx, doneChan, ctxChan, upgradedRelease)
select {
case result := <-rChan:
return result.r, result.e
case result := <-ctxChan:
return result.r, result.e
}
}
// Function used to lock the Mutex, this is important for the case when the atomic flag is set.
@ -341,14 +350,16 @@ func (u *Upgrade) reportToPerformUpgrade(c chan<- resultMessage, rel *release.Re
}
// Setup listener for SIGINT and SIGTERM
func (u *Upgrade) handleContext(ctx context.Context, c chan<- resultMessage, upgradedRelease *release.Release) {
go func() {
<-ctx.Done()
func (u *Upgrade) handleContext(ctx context.Context, done chan interface{}, c chan<- resultMessage, upgradedRelease *release.Release) {
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
}
}
func (u *Upgrade) releasingUpgrade(c chan<- resultMessage, upgradedRelease *release.Release, current kube.ResourceList, target kube.ResourceList, originalRelease *release.Release) {
// pre-upgrade hooks

@ -40,6 +40,33 @@ func upgradeAction(t *testing.T) *Upgrade {
return upAction
}
func TestUpgradeRelease_Success(t *testing.T) {
is := assert.New(t)
req := require.New(t)
upAction := upgradeAction(t)
rel := releaseStub()
rel.Name = "previous-release"
rel.Info.Status = release.StatusDeployed
req.NoError(upAction.cfg.Releases.Create(rel))
upAction.Wait = true
vals := map[string]interface{}{}
ctx, done := context.WithCancel(context.Background())
res, err := upAction.RunWithContext(ctx, rel.Name, buildChart(), vals)
done()
req.NoError(err)
is.Equal(res.Info.Status, release.StatusDeployed)
// Detecting previous bug where context termination after successful release
// caused release to fail.
time.Sleep(time.Millisecond * 100)
lastRelease, err := upAction.cfg.Releases.Last(rel.Name)
req.NoError(err)
is.Equal(lastRelease.Info.Status, release.StatusDeployed)
}
func TestUpgradeRelease_Wait(t *testing.T) {
is := assert.New(t)
req := require.New(t)

@ -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.

@ -28,21 +28,21 @@ import (
helmversion "helm.sh/helm/v3/internal/version"
)
const (
k8sVersionMajor = 1
k8sVersionMinor = 20
)
var (
// The Kubernetes version can be set by LDFLAGS. In order to do that the value
// must be a string.
k8sVersionMajor = "1"
k8sVersionMinor = "20"
// DefaultVersionSet is the default version set, which includes only Core V1 ("v1").
DefaultVersionSet = allKnownVersions()
// DefaultCapabilities is the default set of capabilities.
DefaultCapabilities = &Capabilities{
KubeVersion: KubeVersion{
Version: fmt.Sprintf("v%d.%d.0", k8sVersionMajor, k8sVersionMinor),
Major: strconv.Itoa(k8sVersionMajor),
Minor: strconv.Itoa(k8sVersionMinor),
Version: fmt.Sprintf("v%s.%s.0", k8sVersionMajor, k8sVersionMinor),
Major: k8sVersionMajor,
Minor: k8sVersionMinor,
},
APIVersions: DefaultVersionSet,
HelmVersion: helmversion.Get(),

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

@ -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

@ -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

@ -25,6 +25,20 @@ import (
"github.com/spf13/pflag"
)
func TestSetNamespace(t *testing.T) {
settings := New()
if settings.namespace != "" {
t.Errorf("Expected empty namespace, got %s", settings.namespace)
}
settings.SetNamespace("testns")
if settings.namespace != "testns" {
t.Errorf("Expected namespace testns, got %s", settings.namespace)
}
}
func TestEnvSettings(t *testing.T) {
tests := []struct {
name string
@ -37,9 +51,9 @@ func TestEnvSettings(t *testing.T) {
ns, kcontext string
debug bool
maxhistory int
kAsUser string
kAsGroups []string
kCaFile string
kubeAsUser string
kubeAsGroups []string
kubeCaFile string
}{
{
name: "defaults",
@ -52,9 +66,9 @@ func TestEnvSettings(t *testing.T) {
ns: "myns",
debug: true,
maxhistory: defaultMaxHistory,
kAsUser: "poro",
kAsGroups: []string{"admins", "teatime", "snackeaters"},
kCaFile: "/tmp/ca.crt",
kubeAsUser: "poro",
kubeAsGroups: []string{"admins", "teatime", "snackeaters"},
kubeCaFile: "/tmp/ca.crt",
},
{
name: "with envvars set",
@ -62,9 +76,9 @@ func TestEnvSettings(t *testing.T) {
ns: "yourns",
maxhistory: 5,
debug: true,
kAsUser: "pikachu",
kAsGroups: []string{"operators", "snackeaters", "partyanimals"},
kCaFile: "/tmp/ca.crt",
kubeAsUser: "pikachu",
kubeAsGroups: []string{"operators", "snackeaters", "partyanimals"},
kubeCaFile: "/tmp/ca.crt",
},
{
name: "with flags and envvars set",
@ -73,9 +87,9 @@ func TestEnvSettings(t *testing.T) {
ns: "myns",
debug: true,
maxhistory: 5,
kAsUser: "poro",
kAsGroups: []string{"admins", "teatime", "snackeaters"},
kCaFile: "/my/ca.crt",
kubeAsUser: "poro",
kubeAsGroups: []string{"admins", "teatime", "snackeaters"},
kubeCaFile: "/my/ca.crt",
},
}
@ -105,14 +119,14 @@ func TestEnvSettings(t *testing.T) {
if settings.MaxHistory != tt.maxhistory {
t.Errorf("expected maxHistory %d, got %d", tt.maxhistory, settings.MaxHistory)
}
if tt.kAsUser != settings.KubeAsUser {
t.Errorf("expected kAsUser %q, got %q", tt.kAsUser, settings.KubeAsUser)
if tt.kubeAsUser != settings.KubeAsUser {
t.Errorf("expected kAsUser %q, got %q", tt.kubeAsUser, settings.KubeAsUser)
}
if !reflect.DeepEqual(tt.kAsGroups, settings.KubeAsGroups) {
t.Errorf("expected kAsGroups %+v, got %+v", len(tt.kAsGroups), len(settings.KubeAsGroups))
if !reflect.DeepEqual(tt.kubeAsGroups, settings.KubeAsGroups) {
t.Errorf("expected kAsGroups %+v, got %+v", len(tt.kubeAsGroups), len(settings.KubeAsGroups))
}
if tt.kCaFile != settings.KubeCaFile {
t.Errorf("expected kCaFile %q, got %q", tt.kCaFile, settings.KubeCaFile)
if tt.kubeCaFile != settings.KubeCaFile {
t.Errorf("expected kCaFile %q, got %q", tt.kubeCaFile, settings.KubeCaFile)
}
})
}

@ -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

@ -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),
@ -343,11 +344,6 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error {
version := ""
if registry.IsOCI(churl) {
if !resolver.FeatureGateOCI.IsEnabled() {
return errors.Wrapf(resolver.FeatureGateOCI.Error(),
"the repository %s is an OCI registry", 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())
@ -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 == "" || registry.IsOCI(dd.Repository) {
if dd.Repository == "" {
continue
}
// if dep chart is from local path, verify the path is valid

@ -22,8 +22,8 @@ import (
"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.

@ -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

@ -11,6 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build !windows
// +build !windows
package helmpath

@ -11,6 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build darwin
// +build darwin
package helmpath

@ -11,6 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build darwin
// +build darwin
package helmpath

@ -11,6 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build !windows && !darwin
// +build !windows,!darwin
package helmpath

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save