Added Vault Getter integration for values and application specific properties file

Signed-off-by: Vineet Aggarwal <vineet.aggarwal@rakuten.com>
pull/13491/head
Vineet Aggarwal 10 months ago
parent d1e9c022c6
commit 63cc76d21c

@ -1,252 +1,254 @@
/* /*
Copyright The Helm Authors. Copyright The Helm Authors.
Copyright (c) 2024 Rakuten Symphony India.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. Licensed under the Apache License, Version 2.0 (the "License");
You may obtain a copy of the License at 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
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, Unless required by applicable law or agreed to in writing, software
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. distributed under the License is distributed on an "AS IS" BASIS,
See the License for the specific language governing permissions and WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
limitations under the License. See the License for the specific language governing permissions and
*/ limitations under the License.
*/
package main
package main
import (
"flag" import (
"fmt" "flag"
"log" "fmt"
"path/filepath" "log"
"sort" "path/filepath"
"strings" "sort"
"strings"
"github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/cobra"
"k8s.io/klog/v2" "github.com/spf13/pflag"
"k8s.io/klog/v2"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/cli/output" "helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/cli/values" "helm.sh/helm/v3/pkg/cli/output"
"helm.sh/helm/v3/pkg/helmpath" "helm.sh/helm/v3/pkg/cli/values"
"helm.sh/helm/v3/pkg/postrender" "helm.sh/helm/v3/pkg/helmpath"
"helm.sh/helm/v3/pkg/repo" "helm.sh/helm/v3/pkg/postrender"
) "helm.sh/helm/v3/pkg/repo"
)
const (
outputFlag = "output" const (
postRenderFlag = "post-renderer" outputFlag = "output"
postRenderArgsFlag = "post-renderer-args" 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)") func addValueOptionsFlags(f *pflag.FlagSet, v *values.Options) {
f.StringArrayVar(&v.Values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)") f.StringSliceVarP(&v.ValueFiles, "values", "f", []string{}, "specify values in a YAML file or a URL (can specify multiple)")
f.StringArrayVar(&v.StringValues, "set-string", []string{}, "set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)") f.StringArrayVar(&v.Values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
f.StringArrayVar(&v.FileValues, "set-file", []string{}, "set values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2)") f.StringArrayVar(&v.StringValues, "set-string", []string{}, "set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
f.StringArrayVar(&v.JSONValues, "set-json", []string{}, "set JSON values on the command line (can specify multiple or separate values with commas: key1=jsonval1,key2=jsonval2)") f.StringArrayVar(&v.FileValues, "set-file", []string{}, "set values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2)")
f.StringArrayVar(&v.LiteralValues, "set-literal", []string{}, "set a literal STRING value on the command line") f.StringArrayVar(&v.JSONValues, "set-json", []string{}, "set JSON values on the command line (can specify multiple or separate values with commas: key1=jsonval1,key2=jsonval2)")
} f.StringArrayVar(&v.LiteralValues, "set-literal", []string{}, "set a literal STRING value on the command line")
f.StringSliceVarP(&v.PropertyFiles, "property-file", "p", []string{}, "specify property files to load (can specify multiple)")
func addChartPathOptionsFlags(f *pflag.FlagSet, c *action.ChartPathOptions) { }
f.StringVar(&c.Version, "version", "", "specify a version constraint for the chart version to use. This constraint can be a specific tag (e.g. 1.1.1) or it may reference a valid range (e.g. ^2.0.0). If this is not specified, the latest version is used")
f.BoolVar(&c.Verify, "verify", false, "verify the package before using it") func addChartPathOptionsFlags(f *pflag.FlagSet, c *action.ChartPathOptions) {
f.StringVar(&c.Keyring, "keyring", defaultKeyring(), "location of public keys used for verification") f.StringVar(&c.Version, "version", "", "specify a version constraint for the chart version to use. This constraint can be a specific tag (e.g. 1.1.1) or it may reference a valid range (e.g. ^2.0.0). If this is not specified, the latest version is used")
f.StringVar(&c.RepoURL, "repo", "", "chart repository url where to locate the requested chart") f.BoolVar(&c.Verify, "verify", false, "verify the package before using it")
f.StringVar(&c.Username, "username", "", "chart repository username where to locate the requested chart") f.StringVar(&c.Keyring, "keyring", defaultKeyring(), "location of public keys used for verification")
f.StringVar(&c.Password, "password", "", "chart repository password where to locate the requested chart") f.StringVar(&c.RepoURL, "repo", "", "chart repository url where to locate the requested chart")
f.StringVar(&c.CertFile, "cert-file", "", "identify HTTPS client using this SSL certificate file") f.StringVar(&c.Username, "username", "", "chart repository username where to locate the requested chart")
f.StringVar(&c.KeyFile, "key-file", "", "identify HTTPS client using this SSL key file") f.StringVar(&c.Password, "password", "", "chart repository password where to locate the requested chart")
f.BoolVar(&c.InsecureSkipTLSverify, "insecure-skip-tls-verify", false, "skip tls certificate checks for the chart download") f.StringVar(&c.CertFile, "cert-file", "", "identify HTTPS client using this SSL certificate file")
f.BoolVar(&c.PlainHTTP, "plain-http", false, "use insecure HTTP connections for the chart download") f.StringVar(&c.KeyFile, "key-file", "", "identify HTTPS client using this SSL key file")
f.StringVar(&c.CaFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle") f.BoolVar(&c.InsecureSkipTLSverify, "insecure-skip-tls-verify", false, "skip tls certificate checks for the chart download")
f.BoolVar(&c.PassCredentialsAll, "pass-credentials", false, "pass credentials to all domains") f.BoolVar(&c.PlainHTTP, "plain-http", false, "use insecure HTTP connections for the chart download")
} f.StringVar(&c.CaFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
f.BoolVar(&c.PassCredentialsAll, "pass-credentials", false, "pass credentials to all domains")
// bindOutputFlag will add the output flag to the given command and bind the }
// value to the given format pointer
func bindOutputFlag(cmd *cobra.Command, varRef *output.Format) { // bindOutputFlag will add the output flag to the given command and bind the
cmd.Flags().VarP(newOutputValue(output.Table, varRef), outputFlag, "o", // value to the given format pointer
fmt.Sprintf("prints the output in the specified format. Allowed values: %s", strings.Join(output.Formats(), ", "))) func bindOutputFlag(cmd *cobra.Command, varRef *output.Format) {
cmd.Flags().VarP(newOutputValue(output.Table, varRef), outputFlag, "o",
err := cmd.RegisterFlagCompletionFunc(outputFlag, func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) { fmt.Sprintf("prints the output in the specified format. Allowed values: %s", strings.Join(output.Formats(), ", ")))
var formatNames []string
for format, desc := range output.FormatsWithDesc() { err := cmd.RegisterFlagCompletionFunc(outputFlag, func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
formatNames = append(formatNames, fmt.Sprintf("%s\t%s", format, desc)) var formatNames []string
} for format, desc := range output.FormatsWithDesc() {
formatNames = append(formatNames, fmt.Sprintf("%s\t%s", format, desc))
// Sort the results to get a deterministic order for the tests }
sort.Strings(formatNames)
return formatNames, cobra.ShellCompDirectiveNoFileComp // Sort the results to get a deterministic order for the tests
}) sort.Strings(formatNames)
return formatNames, cobra.ShellCompDirectiveNoFileComp
if err != nil { })
log.Fatal(err)
} if err != nil {
} log.Fatal(err)
}
type outputValue output.Format }
func newOutputValue(defaultValue output.Format, p *output.Format) *outputValue { type outputValue output.Format
*p = defaultValue
return (*outputValue)(p) func newOutputValue(defaultValue output.Format, p *output.Format) *outputValue {
} *p = defaultValue
return (*outputValue)(p)
func (o *outputValue) String() string { }
// It is much cleaner looking (and technically less allocations) to just
// convert to a string rather than type asserting to the underlying func (o *outputValue) String() string {
// output.Format // It is much cleaner looking (and technically less allocations) to just
return string(*o) // convert to a string rather than type asserting to the underlying
} // output.Format
return string(*o)
func (o *outputValue) Type() string { }
return "format"
} func (o *outputValue) Type() string {
return "format"
func (o *outputValue) Set(s string) error { }
outfmt, err := output.ParseFormat(s)
if err != nil { func (o *outputValue) Set(s string) error {
return err outfmt, err := output.ParseFormat(s)
} if err != nil {
*o = outputValue(outfmt) return err
return nil }
} *o = outputValue(outfmt)
return nil
func bindPostRenderFlag(cmd *cobra.Command, varRef *postrender.PostRenderer) { }
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") func bindPostRenderFlag(cmd *cobra.Command, varRef *postrender.PostRenderer) {
cmd.Flags().Var(&postRendererArgsSlice{p}, postRenderArgsFlag, "an argument to the post-renderer (can specify multiple)") 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 postRendererOptions struct { }
renderer *postrender.PostRenderer
binaryPath string type postRendererOptions struct {
args []string renderer *postrender.PostRenderer
} binaryPath string
args []string
type postRendererString struct { }
options *postRendererOptions
} type postRendererString struct {
options *postRendererOptions
func (p *postRendererString) String() string { }
return p.options.binaryPath
} func (p *postRendererString) String() string {
return p.options.binaryPath
func (p *postRendererString) Type() string { }
return "postRendererString"
} func (p *postRendererString) Type() string {
return "postRendererString"
func (p *postRendererString) Set(val string) error { }
if val == "" {
return nil func (p *postRendererString) Set(val string) error {
} if val == "" {
p.options.binaryPath = val return nil
pr, err := postrender.NewExec(p.options.binaryPath, p.options.args...) }
if err != nil { p.options.binaryPath = val
return err pr, err := postrender.NewExec(p.options.binaryPath, p.options.args...)
} if err != nil {
*p.options.renderer = pr return err
return nil }
} *p.options.renderer = pr
return nil
type postRendererArgsSlice struct { }
options *postRendererOptions
} type postRendererArgsSlice struct {
options *postRendererOptions
func (p *postRendererArgsSlice) String() string { }
return "[" + strings.Join(p.options.args, ",") + "]"
} func (p *postRendererArgsSlice) String() string {
return "[" + strings.Join(p.options.args, ",") + "]"
func (p *postRendererArgsSlice) Type() string { }
return "postRendererArgsSlice"
} 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 func (p *postRendererArgsSlice) Set(val string) error {
p.options.args = append(p.options.args, val)
// a post-renderer defined by a user may accept empty arguments
if p.options.binaryPath == "" { p.options.args = append(p.options.args, val)
return nil
} if p.options.binaryPath == "" {
// overwrite if already create PostRenderer by `post-renderer` flags return nil
pr, err := postrender.NewExec(p.options.binaryPath, p.options.args...) }
if err != nil { // overwrite if already create PostRenderer by `post-renderer` flags
return err pr, err := postrender.NewExec(p.options.binaryPath, p.options.args...)
} if err != nil {
*p.options.renderer = pr return err
return nil }
} *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) 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) Replace(val []string) error {
} p.options.args = val
return nil
func (p *postRendererArgsSlice) GetSlice() []string { }
return p.options.args
} func (p *postRendererArgsSlice) GetSlice() []string {
return p.options.args
func compVersionFlag(chartRef string, _ string) ([]string, cobra.ShellCompDirective) { }
chartInfo := strings.Split(chartRef, "/")
if len(chartInfo) != 2 { func compVersionFlag(chartRef string, _ string) ([]string, cobra.ShellCompDirective) {
return nil, cobra.ShellCompDirectiveNoFileComp chartInfo := strings.Split(chartRef, "/")
} if len(chartInfo) != 2 {
return nil, cobra.ShellCompDirectiveNoFileComp
repoName := chartInfo[0] }
chartName := chartInfo[1]
repoName := chartInfo[0]
path := filepath.Join(settings.RepositoryCache, helmpath.CacheIndexFile(repoName)) chartName := chartInfo[1]
var versions []string path := filepath.Join(settings.RepositoryCache, helmpath.CacheIndexFile(repoName))
if indexFile, err := repo.LoadIndexFile(path); err == nil {
for _, details := range indexFile.Entries[chartName] { var versions []string
appVersion := details.Metadata.AppVersion if indexFile, err := repo.LoadIndexFile(path); err == nil {
appVersionDesc := "" for _, details := range indexFile.Entries[chartName] {
if appVersion != "" { appVersion := details.Metadata.AppVersion
appVersionDesc = fmt.Sprintf("App: %s, ", appVersion) appVersionDesc := ""
} if appVersion != "" {
created := details.Created.Format("January 2, 2006") appVersionDesc = fmt.Sprintf("App: %s, ", appVersion)
createdDesc := "" }
if created != "" { created := details.Created.Format("January 2, 2006")
createdDesc = fmt.Sprintf("Created: %s ", created) createdDesc := ""
} if created != "" {
deprecated := "" createdDesc = fmt.Sprintf("Created: %s ", created)
if details.Metadata.Deprecated { }
deprecated = "(deprecated)" deprecated := ""
} if details.Metadata.Deprecated {
versions = append(versions, fmt.Sprintf("%s\t%s%s%s", details.Metadata.Version, appVersionDesc, createdDesc, deprecated)) deprecated = "(deprecated)"
} }
} versions = append(versions, fmt.Sprintf("%s\t%s%s%s", details.Metadata.Version, appVersionDesc, createdDesc, deprecated))
}
return versions, cobra.ShellCompDirectiveNoFileComp }
}
return versions, cobra.ShellCompDirectiveNoFileComp
// addKlogFlags adds flags from k8s.io/klog }
// marks the flags as hidden to avoid polluting the help text
func addKlogFlags(fs *pflag.FlagSet) { // addKlogFlags adds flags from k8s.io/klog
local := flag.NewFlagSet("klog", flag.ExitOnError) // marks the flags as hidden to avoid polluting the help text
klog.InitFlags(local) func addKlogFlags(fs *pflag.FlagSet) {
local.VisitAll(func(fl *flag.Flag) { local := flag.NewFlagSet("klog", flag.ExitOnError)
fl.Name = normalize(fl.Name) klog.InitFlags(local)
if fs.Lookup(fl.Name) != nil { local.VisitAll(func(fl *flag.Flag) {
return fl.Name = normalize(fl.Name)
} if fs.Lookup(fl.Name) != nil {
newflag := pflag.PFlagFromGoFlag(fl) return
newflag.Hidden = true }
fs.AddFlag(newflag) newflag := pflag.PFlagFromGoFlag(fl)
}) newflag.Hidden = true
} fs.AddFlag(newflag)
})
// normalize replaces underscores with hyphens }
func normalize(s string) string {
return strings.ReplaceAll(s, "_", "-") // normalize replaces underscores with hyphens
} func normalize(s string) string {
return strings.ReplaceAll(s, "_", "-")
}

@ -1,356 +1,357 @@
/* /*
Copyright The Helm Authors. Copyright The Helm Authors.
Copyright (c) 2024 Rakuten Symphony India.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. Licensed under the Apache License, Version 2.0 (the "License");
You may obtain a copy of the License at 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
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, Unless required by applicable law or agreed to in writing, software
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. distributed under the License is distributed on an "AS IS" BASIS,
See the License for the specific language governing permissions and WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
limitations under the License. See the License for the specific language governing permissions and
*/ limitations under the License.
*/
package main
package main
import (
"context" import (
"fmt" "context"
"io" "fmt"
"log" "io"
"os" "log"
"os/signal" "os"
"syscall" "os/signal"
"time" "syscall"
"time"
"github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/pkg/errors"
"github.com/spf13/pflag" "github.com/spf13/cobra"
"github.com/spf13/pflag"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/chart/loader" "helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/cli/output" "helm.sh/helm/v3/pkg/chart/loader"
"helm.sh/helm/v3/pkg/cli/values" "helm.sh/helm/v3/pkg/cli/output"
"helm.sh/helm/v3/pkg/downloader" "helm.sh/helm/v3/pkg/cli/values"
"helm.sh/helm/v3/pkg/getter" "helm.sh/helm/v3/pkg/downloader"
"helm.sh/helm/v3/pkg/release" "helm.sh/helm/v3/pkg/getter"
) "helm.sh/helm/v3/pkg/release"
)
const installDesc = `
This command installs a chart archive. const installDesc = `
This command installs a chart archive.
The install argument must be a chart reference, a path to a packaged chart,
a path to an unpacked chart directory or a URL. The install argument must be a chart reference, a path to a packaged chart,
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 To override values in a chart, use either the '--values' flag and pass in a file
a string value use '--set-string'. You can use '--set-file' to set individual or use the '--set' flag and pass configuration from the command line, to force
values from a file when the value itself is too long for the command line a string value use '--set-string'. You can use '--set-file' to set individual
or is dynamically generated. You can also use '--set-json' to set json values values from a file when the value itself is too long for the command line
(scalars/objects/arrays) from the command line. or is dynamically generated. You can also use '--set-json' to set json values
(scalars/objects/arrays) from the command line.
$ helm install -f myvalues.yaml myredis ./redis
$ helm install -f myvalues.yaml myredis ./redis
or
or
$ helm install --set name=prod myredis ./redis
$ helm install --set name=prod myredis ./redis
or
or
$ helm install --set-string long_int=1234567890 myredis ./redis
$ helm install --set-string long_int=1234567890 myredis ./redis
or
or
$ helm install --set-file my_script=dothings.sh myredis ./redis
$ helm install --set-file my_script=dothings.sh myredis ./redis
or
or
$ helm install --set-json 'master.sidecars=[{"name":"sidecar","image":"myImage","imagePullPolicy":"Always","ports":[{"name":"portname","containerPort":1234}]}]' myredis ./redis
$ helm install --set-json 'master.sidecars=[{"name":"sidecar","image":"myImage","imagePullPolicy":"Always","ports":[{"name":"portname","containerPort":1234}]}]' myredis ./redis
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 You can specify the '--values'/'-f' flag multiple times. The priority will be given to the
contained a key called 'Test', the value set in override.yaml would take precedence: last (right-most) file specified. For example, if both myvalues.yaml and override.yaml
contained a key called 'Test', the value set in override.yaml would take precedence:
$ helm install -f myvalues.yaml -f override.yaml myredis ./redis
$ helm install -f myvalues.yaml -f override.yaml myredis ./redis
You can specify the '--set' flag multiple times. The priority will be given to the
last (right-most) set specified. For example, if both 'bar' and 'newbar' values are You can specify the '--set' flag multiple times. The priority will be given to the
set for a key called 'foo', the 'newbar' value would take precedence: last (right-most) set specified. For example, if both 'bar' and 'newbar' values are
set for a key called 'foo', the 'newbar' value would take precedence:
$ helm install --set foo=bar --set foo=newbar myredis ./redis
$ helm install --set foo=bar --set foo=newbar myredis ./redis
Similarly, in the following example 'foo' is set to '["four"]':
Similarly, in the following example 'foo' is set to '["four"]':
$ helm install --set-json='foo=["one", "two", "three"]' --set-json='foo=["four"]' myredis ./redis
$ helm install --set-json='foo=["one", "two", "three"]' --set-json='foo=["four"]' myredis ./redis
And in the following example, 'foo' is set to '{"key1":"value1","key2":"bar"}':
And in the following example, 'foo' is set to '{"key1":"value1","key2":"bar"}':
$ helm install --set-json='foo={"key1":"value1","key2":"value2"}' --set-json='foo.key2="bar"' myredis ./redis
$ helm install --set-json='foo={"key1":"value1","key2":"value2"}' --set-json='foo.key2="bar"' myredis ./redis
To check the generated manifests of a release without installing the chart,
the --debug and --dry-run flags can be combined. To check the generated manifests of a release without installing the chart,
the --debug and --dry-run flags can be combined.
The --dry-run flag will output all generated chart manifests, including Secrets
which can contain sensitive values. To hide Kubernetes Secrets use the The --dry-run flag will output all generated chart manifests, including Secrets
--hide-secret flag. Please carefully consider how and when these flags are used. which can contain sensitive values. To hide Kubernetes Secrets use the
--hide-secret flag. Please carefully consider how and when these flags are used.
If --verify is set, the chart MUST have a provenance file, and the provenance
file MUST pass all verification steps. If --verify is set, the chart MUST have a provenance file, and the provenance
file MUST pass all verification steps.
There are six different ways you can express the chart you want to install:
There are six different ways you can express the chart you want to install:
1. By chart reference: helm install mymaria example/mariadb
2. By path to a packaged chart: helm install mynginx ./nginx-1.2.3.tgz 1. By chart reference: helm install mymaria example/mariadb
3. By path to an unpacked chart directory: helm install mynginx ./nginx 2. By path to a packaged chart: helm install mynginx ./nginx-1.2.3.tgz
4. By absolute URL: helm install mynginx https://example.com/charts/nginx-1.2.3.tgz 3. By path to an unpacked chart directory: helm install mynginx ./nginx
5. By chart reference and repo url: helm install --repo https://example.com/charts/ mynginx nginx 4. By absolute URL: helm install mynginx https://example.com/charts/nginx-1.2.3.tgz
6. By OCI registries: helm install mynginx --version 1.2.3 oci://example.com/charts/nginx 5. By chart reference and repo url: helm install --repo https://example.com/charts/ mynginx nginx
6. By OCI registries: helm install mynginx --version 1.2.3 oci://example.com/charts/nginx
CHART REFERENCES
CHART REFERENCES
A chart reference is a convenient way of referencing a chart in a chart repository.
A chart reference is a convenient way of referencing a chart in a chart repository.
When you use a chart reference with a repo prefix ('example/mariadb'), Helm will look in the local
configuration for a chart repository named 'example', and will then look for a When you use a chart reference with a repo prefix ('example/mariadb'), Helm will look in the local
chart in that repository whose name is 'mariadb'. It will install the latest stable version of that chart configuration for a chart repository named 'example', and will then look for a
until you specify '--devel' flag to also include development version (alpha, beta, and release candidate releases), or chart in that repository whose name is 'mariadb'. It will install the latest stable version of that chart
supply a version number with the '--version' flag. until you specify '--devel' flag to also include development version (alpha, beta, and release candidate releases), or
supply a version number with the '--version' flag.
To see the list of chart repositories, use 'helm repo list'. To search for
charts in a repository, use 'helm search'. To see the list of chart repositories, use 'helm repo list'. To search for
` charts in a repository, use 'helm search'.
`
func newInstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
client := action.NewInstall(cfg) func newInstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
valueOpts := &values.Options{} client := action.NewInstall(cfg)
var outfmt output.Format valueOpts := &values.Options{}
var outfmt output.Format
cmd := &cobra.Command{
Use: "install [NAME] [CHART]", cmd := &cobra.Command{
Short: "install a chart", Use: "install [NAME] [CHART]",
Long: installDesc, Short: "install a chart",
Args: require.MinimumNArgs(1), Long: installDesc,
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { Args: require.MinimumNArgs(1),
return compInstall(args, toComplete, client) ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
}, return compInstall(args, toComplete, client)
RunE: func(_ *cobra.Command, args []string) error { },
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile, RunE: func(_ *cobra.Command, args []string) error {
client.InsecureSkipTLSverify, client.PlainHTTP, client.Username, client.Password) registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
if err != nil { client.InsecureSkipTLSverify, client.PlainHTTP, client.Username, client.Password)
return fmt.Errorf("missing registry client: %w", err) if err != nil {
} return fmt.Errorf("missing registry client: %w", err)
client.SetRegistryClient(registryClient) }
client.SetRegistryClient(registryClient)
// This is for the case where "" is specifically passed in as a
// value. When there is no value passed in NoOptDefVal will be used // This is for the case where "" is specifically passed in as a
// and it is set to client. See addInstallFlags. // value. When there is no value passed in NoOptDefVal will be used
if client.DryRunOption == "" { // and it is set to client. See addInstallFlags.
client.DryRunOption = "none" if client.DryRunOption == "" {
} client.DryRunOption = "none"
rel, err := runInstall(args, client, valueOpts, out) }
if err != nil { rel, err := runInstall(args, client, valueOpts, out)
return errors.Wrap(err, "INSTALLATION FAILED") if err != nil {
} return errors.Wrap(err, "INSTALLATION FAILED")
}
return outfmt.Write(out, &statusPrinter{rel, settings.Debug, false, false, false, client.HideNotes})
}, return outfmt.Write(out, &statusPrinter{rel, settings.Debug, false, false, false, client.HideNotes})
} },
}
addInstallFlags(cmd, cmd.Flags(), client, valueOpts)
// hide-secret is not available in all places the install flags are used so addInstallFlags(cmd, cmd.Flags(), client, valueOpts)
// it is added separately // hide-secret is not available in all places the install flags are used so
f := cmd.Flags() // it is added separately
f.BoolVar(&client.HideSecret, "hide-secret", false, "hide Kubernetes Secrets when also using the --dry-run flag") f := cmd.Flags()
bindOutputFlag(cmd, &outfmt) f.BoolVar(&client.HideSecret, "hide-secret", false, "hide Kubernetes Secrets when also using the --dry-run flag")
bindPostRenderFlag(cmd, &client.PostRenderer) bindOutputFlag(cmd, &outfmt)
bindPostRenderFlag(cmd, &client.PostRenderer)
return cmd
} return cmd
}
func addInstallFlags(cmd *cobra.Command, f *pflag.FlagSet, client *action.Install, valueOpts *values.Options) {
f.BoolVar(&client.CreateNamespace, "create-namespace", false, "create the release namespace if not present") func addInstallFlags(cmd *cobra.Command, f *pflag.FlagSet, client *action.Install, valueOpts *values.Options) {
// --dry-run options with expected outcome: f.BoolVar(&client.CreateNamespace, "create-namespace", false, "create the release namespace if not present")
// - Not set means no dry run and server is contacted. // --dry-run options with expected outcome:
// - Set with no value, a value of client, or a value of true and the server is not contacted // - Not set means no dry run and server is contacted.
// - Set with a value of false, none, or false and the server is contacted // - Set with no value, a value of client, or a value of true and the server is not contacted
// The true/false part is meant to reflect some legacy behavior while none is equal to "". // - Set with a value of false, none, or false and the server is contacted
f.StringVar(&client.DryRunOption, "dry-run", "", "simulate an install. If --dry-run is set with no option being specified or as '--dry-run=client', it will not attempt cluster connections. Setting '--dry-run=server' allows attempting cluster connections.") // The true/false part is meant to reflect some legacy behavior while none is equal to "".
f.Lookup("dry-run").NoOptDefVal = "client" f.StringVar(&client.DryRunOption, "dry-run", "", "simulate an install. If --dry-run is set with no option being specified or as '--dry-run=client', it will not attempt cluster connections. Setting '--dry-run=server' allows attempting cluster connections.")
f.BoolVar(&client.Force, "force", false, "force resource updates through a replacement strategy") f.Lookup("dry-run").NoOptDefVal = "client"
f.BoolVar(&client.DisableHooks, "no-hooks", false, "prevent hooks from running during install") f.BoolVar(&client.Force, "force", false, "force resource updates through a replacement strategy")
f.BoolVar(&client.Replace, "replace", false, "reuse the given name, only if that name is a deleted release which remains in the history. This is unsafe in production") f.BoolVar(&client.DisableHooks, "no-hooks", false, "prevent hooks from running during install")
f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)") f.BoolVar(&client.Replace, "replace", false, "reuse the given name, only if that name is a deleted release which remains in the history. This is unsafe in production")
f.BoolVar(&client.Wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment, StatefulSet, or ReplicaSet are in a ready state before marking the release as successful. It will wait for as long as --timeout") f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)")
f.BoolVar(&client.WaitForJobs, "wait-for-jobs", false, "if set and --wait enabled, will wait until all Jobs have been completed before marking the release as successful. It will wait for as long as --timeout") f.BoolVar(&client.Wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment, StatefulSet, or ReplicaSet are in a ready state before marking the release as successful. It will wait for as long as --timeout")
f.BoolVarP(&client.GenerateName, "generate-name", "g", false, "generate the name (and omit the NAME parameter)") f.BoolVar(&client.WaitForJobs, "wait-for-jobs", false, "if set and --wait enabled, will wait until all Jobs have been completed before marking the release as successful. It will wait for as long as --timeout")
f.StringVar(&client.NameTemplate, "name-template", "", "specify template used to name the release") f.BoolVarP(&client.GenerateName, "generate-name", "g", false, "generate the name (and omit the NAME parameter)")
f.StringVar(&client.Description, "description", "", "add a custom description") f.StringVar(&client.NameTemplate, "name-template", "", "specify template used to name the release")
f.BoolVar(&client.Devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored") f.StringVar(&client.Description, "description", "", "add a custom description")
f.BoolVar(&client.DependencyUpdate, "dependency-update", false, "update dependencies if they are missing before installing the chart") f.BoolVar(&client.Devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored")
f.BoolVar(&client.DisableOpenAPIValidation, "disable-openapi-validation", false, "if set, the installation process will not validate rendered templates against the Kubernetes OpenAPI Schema") f.BoolVar(&client.DependencyUpdate, "dependency-update", false, "update dependencies if they are missing before installing the chart")
f.BoolVar(&client.Atomic, "atomic", false, "if set, the installation process deletes the installation on failure. The --wait flag will be set automatically if --atomic is used") f.BoolVar(&client.DisableOpenAPIValidation, "disable-openapi-validation", false, "if set, the installation process will not validate rendered templates against the Kubernetes OpenAPI Schema")
f.BoolVar(&client.SkipCRDs, "skip-crds", false, "if set, no CRDs will be installed. By default, CRDs are installed if not already present") f.BoolVar(&client.Atomic, "atomic", false, "if set, the installation process deletes the installation on failure. The --wait flag will be set automatically if --atomic is used")
f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent") f.BoolVar(&client.SkipCRDs, "skip-crds", false, "if set, no CRDs will be installed. By default, CRDs are installed if not already present")
f.BoolVar(&client.SkipSchemaValidation, "skip-schema-validation", false, "if set, disables JSON schema validation") f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent")
f.StringToStringVarP(&client.Labels, "labels", "l", nil, "Labels that would be added to release metadata. Should be divided by comma.") f.BoolVar(&client.SkipSchemaValidation, "skip-schema-validation", false, "if set, disables JSON schema validation")
f.BoolVar(&client.EnableDNS, "enable-dns", false, "enable DNS lookups when rendering templates") f.StringToStringVarP(&client.Labels, "labels", "l", nil, "Labels that would be added to release metadata. Should be divided by comma.")
f.BoolVar(&client.HideNotes, "hide-notes", false, "if set, do not show notes in install output. Does not affect presence in chart metadata") f.BoolVar(&client.EnableDNS, "enable-dns", false, "enable DNS lookups when rendering templates")
addValueOptionsFlags(f, valueOpts) f.BoolVar(&client.HideNotes, "hide-notes", false, "if set, do not show notes in install output. Does not affect presence in chart metadata")
addChartPathOptionsFlags(f, &client.ChartPathOptions) addValueOptionsFlags(f, valueOpts)
addChartPathOptionsFlags(f, &client.ChartPathOptions)
err := cmd.RegisterFlagCompletionFunc("version", func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
requiredArgs := 2 err := cmd.RegisterFlagCompletionFunc("version", func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if client.GenerateName { requiredArgs := 2
requiredArgs = 1 if client.GenerateName {
} requiredArgs = 1
if len(args) != requiredArgs { }
return nil, cobra.ShellCompDirectiveNoFileComp if len(args) != requiredArgs {
} return nil, cobra.ShellCompDirectiveNoFileComp
return compVersionFlag(args[requiredArgs-1], toComplete) }
}) return compVersionFlag(args[requiredArgs-1], toComplete)
})
if err != nil {
log.Fatal(err) if err != nil {
} log.Fatal(err)
} }
}
func runInstall(args []string, client *action.Install, valueOpts *values.Options, out io.Writer) (*release.Release, error) {
debug("Original chart version: %q", client.Version) func runInstall(args []string, client *action.Install, valueOpts *values.Options, out io.Writer) (*release.Release, error) {
if client.Version == "" && client.Devel { debug("Original chart version: %q", client.Version)
debug("setting version to >0.0.0-0") if client.Version == "" && client.Devel {
client.Version = ">0.0.0-0" debug("setting version to >0.0.0-0")
} client.Version = ">0.0.0-0"
}
name, chart, err := client.NameAndChart(args)
if err != nil { name, chart, err := client.NameAndChart(args)
return nil, err if err != nil {
} return nil, err
client.ReleaseName = name }
client.ReleaseName = name
cp, err := client.ChartPathOptions.LocateChart(chart, settings)
if err != nil { cp, err := client.ChartPathOptions.LocateChart(chart, settings)
return nil, err if err != nil {
} return nil, err
}
debug("CHART PATH: %s\n", cp)
debug("CHART PATH: %s\n", cp)
p := getter.All(settings)
vals, err := valueOpts.MergeValues(p) p := getter.All(settings)
if err != nil { vals, err := valueOpts.MergeValues(p, settings.VaultAddress, settings.Token)
return nil, err if err != nil {
} return nil, err
}
// Check chart dependencies to make sure all are present in /charts
chartRequested, err := loader.Load(cp) // Check chart dependencies to make sure all are present in /charts
if err != nil { chartRequested, err := loader.Load(cp)
return nil, err if err != nil {
} return nil, err
}
if err := checkIfInstallable(chartRequested); err != nil {
return nil, err if err := checkIfInstallable(chartRequested); err != nil {
} return nil, err
}
if chartRequested.Metadata.Deprecated {
warning("This chart is deprecated") if chartRequested.Metadata.Deprecated {
} warning("This chart is deprecated")
}
if req := chartRequested.Metadata.Dependencies; req != nil {
// If CheckDependencies returns an error, we have unfulfilled dependencies. if req := chartRequested.Metadata.Dependencies; req != nil {
// As of Helm 2.4.0, this is treated as a stopping condition: // If CheckDependencies returns an error, we have unfulfilled dependencies.
// https://github.com/helm/helm/issues/2209 // As of Helm 2.4.0, this is treated as a stopping condition:
if err := action.CheckDependencies(chartRequested, req); err != nil { // https://github.com/helm/helm/issues/2209
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 err := action.CheckDependencies(chartRequested, req); err != nil {
if client.DependencyUpdate { err = errors.Wrap(err, "An error occurred while checking for chart dependencies. You may need to run `helm dependency build` to fetch missing dependencies")
man := &downloader.Manager{ if client.DependencyUpdate {
Out: out, man := &downloader.Manager{
ChartPath: cp, Out: out,
Keyring: client.ChartPathOptions.Keyring, ChartPath: cp,
SkipUpdate: false, Keyring: client.ChartPathOptions.Keyring,
Getters: p, SkipUpdate: false,
RepositoryConfig: settings.RepositoryConfig, Getters: p,
RepositoryCache: settings.RepositoryCache, RepositoryConfig: settings.RepositoryConfig,
Debug: settings.Debug, RepositoryCache: settings.RepositoryCache,
RegistryClient: client.GetRegistryClient(), Debug: settings.Debug,
} RegistryClient: client.GetRegistryClient(),
if err := man.Update(); err != nil { }
return nil, err if err := man.Update(); err != nil {
} return nil, err
// Reload the chart with the updated Chart.lock file. }
if chartRequested, err = loader.Load(cp); err != nil { // Reload the chart with the updated Chart.lock file.
return nil, errors.Wrap(err, "failed reloading chart after repo update") if chartRequested, err = loader.Load(cp); err != nil {
} return nil, errors.Wrap(err, "failed reloading chart after repo update")
} else { }
return nil, err } else {
} return nil, err
} }
} }
}
client.Namespace = settings.Namespace()
client.Namespace = settings.Namespace()
// Validate DryRunOption member is one of the allowed values
if err := validateDryRunOptionFlag(client.DryRunOption); err != nil { // Validate DryRunOption member is one of the allowed values
return nil, err if err := validateDryRunOptionFlag(client.DryRunOption); err != nil {
} return nil, err
}
// Create context and prepare the handle of SIGTERM
ctx := context.Background() // Create context and prepare the handle of SIGTERM
ctx, cancel := context.WithCancel(ctx) ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
// Set up channel on which to send signal notifications.
// We must use a buffered channel or risk missing the signal // Set up channel on which to send signal notifications.
// if we're not ready to receive when the signal is sent. // We must use a buffered channel or risk missing the signal
cSignal := make(chan os.Signal, 2) // if we're not ready to receive when the signal is sent.
signal.Notify(cSignal, os.Interrupt, syscall.SIGTERM) cSignal := make(chan os.Signal, 2)
go func() { signal.Notify(cSignal, os.Interrupt, syscall.SIGTERM)
<-cSignal go func() {
fmt.Fprintf(out, "Release %s has been cancelled.\n", args[0]) <-cSignal
cancel() fmt.Fprintf(out, "Release %s has been cancelled.\n", args[0])
}() cancel()
}()
return client.RunWithContext(ctx, chartRequested, vals)
} return client.RunWithContext(ctx, chartRequested, vals)
}
// checkIfInstallable validates if a chart can be installed
// // checkIfInstallable validates if a chart can be installed
// Application chart type is only installable //
func checkIfInstallable(ch *chart.Chart) error { // Application chart type is only installable
switch ch.Metadata.Type { func checkIfInstallable(ch *chart.Chart) error {
case "", "application": switch ch.Metadata.Type {
return nil case "", "application":
} return nil
return errors.Errorf("%s charts are not installable", ch.Metadata.Type) }
} return errors.Errorf("%s charts are not installable", ch.Metadata.Type)
}
// Provide dynamic auto-completion for the install and template commands
func compInstall(args []string, toComplete string, client *action.Install) ([]string, cobra.ShellCompDirective) { // Provide dynamic auto-completion for the install and template commands
requiredArgs := 1 func compInstall(args []string, toComplete string, client *action.Install) ([]string, cobra.ShellCompDirective) {
if client.GenerateName { requiredArgs := 1
requiredArgs = 0 if client.GenerateName {
} requiredArgs = 0
if len(args) == requiredArgs { }
return compListCharts(toComplete, true) if len(args) == requiredArgs {
} return compListCharts(toComplete, true)
return nil, cobra.ShellCompDirectiveNoFileComp }
} return nil, cobra.ShellCompDirectiveNoFileComp
}
func validateDryRunOptionFlag(dryRunOptionFlagValue string) error {
// Validate dry-run flag value with a set of allowed value func validateDryRunOptionFlag(dryRunOptionFlagValue string) error {
allowedDryRunValues := []string{"false", "true", "none", "client", "server"} // Validate dry-run flag value with a set of allowed value
isAllowed := false allowedDryRunValues := []string{"false", "true", "none", "client", "server"}
for _, v := range allowedDryRunValues { isAllowed := false
if dryRunOptionFlagValue == v { for _, v := range allowedDryRunValues {
isAllowed = true if dryRunOptionFlagValue == v {
break isAllowed = true
} break
} }
if !isAllowed { }
return errors.New("Invalid dry-run flag. Flag must one of the following: false, true, none, client, server") if !isAllowed {
} return errors.New("Invalid dry-run flag. Flag must one of the following: false, true, none, client, server")
return nil }
} return nil
}

@ -1,156 +1,157 @@
/* /*
Copyright The Helm Authors. Copyright The Helm Authors.
Copyright (c) 2024 Rakuten Symphony India.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. Licensed under the Apache License, Version 2.0 (the "License");
You may obtain a copy of the License at 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
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, Unless required by applicable law or agreed to in writing, software
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. distributed under the License is distributed on an "AS IS" BASIS,
See the License for the specific language governing permissions and WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
limitations under the License. See the License for the specific language governing permissions and
*/ limitations under the License.
*/
package main
package main
import (
"fmt" import (
"io" "fmt"
"os" "io"
"path/filepath" "os"
"strings" "path/filepath"
"strings"
"github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/pkg/errors"
"github.com/spf13/cobra"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/chartutil" "helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/cli/values" "helm.sh/helm/v3/pkg/chartutil"
"helm.sh/helm/v3/pkg/getter" "helm.sh/helm/v3/pkg/cli/values"
"helm.sh/helm/v3/pkg/lint/support" "helm.sh/helm/v3/pkg/getter"
) "helm.sh/helm/v3/pkg/lint/support"
)
var longLintHelp = `
This command takes a path to a chart and runs a series of tests to verify that var longLintHelp = `
the chart is well-formed. This command takes a path to a chart and runs a series of tests to verify that
the chart is well-formed.
If the linter encounters things that will cause the chart to fail installation,
it will emit [ERROR] messages. If it encounters issues that break with convention If the linter encounters things that will cause the chart to fail installation,
or recommendation, it will emit [WARNING] messages. it will emit [ERROR] messages. If it encounters issues that break with convention
` or recommendation, it will emit [WARNING] messages.
`
func newLintCmd(out io.Writer) *cobra.Command {
client := action.NewLint() func newLintCmd(out io.Writer) *cobra.Command {
valueOpts := &values.Options{} client := action.NewLint()
var kubeVersion string valueOpts := &values.Options{}
var kubeVersion string
cmd := &cobra.Command{
Use: "lint PATH", cmd := &cobra.Command{
Short: "examine a chart for possible issues", Use: "lint PATH",
Long: longLintHelp, Short: "examine a chart for possible issues",
RunE: func(_ *cobra.Command, args []string) error { Long: longLintHelp,
paths := []string{"."} RunE: func(_ *cobra.Command, args []string) error {
if len(args) > 0 { paths := []string{"."}
paths = args if len(args) > 0 {
} paths = args
}
if kubeVersion != "" {
parsedKubeVersion, err := chartutil.ParseKubeVersion(kubeVersion) if kubeVersion != "" {
if err != nil { parsedKubeVersion, err := chartutil.ParseKubeVersion(kubeVersion)
return fmt.Errorf("invalid kube version '%s': %s", kubeVersion, err) if err != nil {
} return fmt.Errorf("invalid kube version '%s': %s", kubeVersion, err)
client.KubeVersion = parsedKubeVersion }
} client.KubeVersion = parsedKubeVersion
}
if client.WithSubcharts {
for _, p := range paths { if client.WithSubcharts {
filepath.Walk(filepath.Join(p, "charts"), func(path string, info os.FileInfo, _ error) error { for _, p := range paths {
if info != nil { filepath.Walk(filepath.Join(p, "charts"), func(path string, info os.FileInfo, _ error) error {
if info.Name() == "Chart.yaml" { if info != nil {
paths = append(paths, filepath.Dir(path)) if info.Name() == "Chart.yaml" {
} else if strings.HasSuffix(path, ".tgz") || strings.HasSuffix(path, ".tar.gz") { paths = append(paths, filepath.Dir(path))
paths = append(paths, path) } else if strings.HasSuffix(path, ".tgz") || strings.HasSuffix(path, ".tar.gz") {
} paths = append(paths, path)
} }
return nil }
}) return nil
} })
} }
}
client.Namespace = settings.Namespace()
vals, err := valueOpts.MergeValues(getter.All(settings)) client.Namespace = settings.Namespace()
if err != nil { vals, err := valueOpts.MergeValues(getter.All(settings), settings.VaultAddress, settings.Token)
return err if err != nil {
} return err
}
var message strings.Builder
failed := 0 var message strings.Builder
errorsOrWarnings := 0 failed := 0
errorsOrWarnings := 0
for _, path := range paths {
result := client.Run([]string{path}, vals) for _, path := range paths {
result := client.Run([]string{path}, vals)
// If there is no errors/warnings and quiet flag is set
// go to the next chart // If there is no errors/warnings and quiet flag is set
hasWarningsOrErrors := action.HasWarningsOrErrors(result) // go to the next chart
if hasWarningsOrErrors { hasWarningsOrErrors := action.HasWarningsOrErrors(result)
errorsOrWarnings++ if hasWarningsOrErrors {
} errorsOrWarnings++
if client.Quiet && !hasWarningsOrErrors { }
continue if client.Quiet && !hasWarningsOrErrors {
} continue
}
fmt.Fprintf(&message, "==> Linting %s\n", path)
fmt.Fprintf(&message, "==> Linting %s\n", path)
// All the Errors that are generated by a chart
// that failed a lint will be included in the // All the Errors that are generated by a chart
// results.Messages so we only need to print // that failed a lint will be included in the
// the Errors if there are no Messages. // results.Messages so we only need to print
if len(result.Messages) == 0 { // the Errors if there are no Messages.
for _, err := range result.Errors { if len(result.Messages) == 0 {
fmt.Fprintf(&message, "Error %s\n", err) for _, err := range result.Errors {
} fmt.Fprintf(&message, "Error %s\n", err)
} }
}
for _, msg := range result.Messages {
if !client.Quiet || msg.Severity > support.InfoSev { for _, msg := range result.Messages {
fmt.Fprintf(&message, "%s\n", msg) if !client.Quiet || msg.Severity > support.InfoSev {
} fmt.Fprintf(&message, "%s\n", msg)
} }
}
if len(result.Errors) != 0 {
failed++ if len(result.Errors) != 0 {
} failed++
}
// Adding extra new line here to break up the
// results, stops this from being a big wall of // Adding extra new line here to break up the
// text and makes it easier to follow. // results, stops this from being a big wall of
fmt.Fprint(&message, "\n") // text and makes it easier to follow.
} fmt.Fprint(&message, "\n")
}
fmt.Fprint(out, message.String())
fmt.Fprint(out, message.String())
summary := fmt.Sprintf("%d chart(s) linted, %d chart(s) failed", len(paths), failed)
if failed > 0 { summary := fmt.Sprintf("%d chart(s) linted, %d chart(s) failed", len(paths), failed)
return errors.New(summary) if failed > 0 {
} return errors.New(summary)
if !client.Quiet || errorsOrWarnings > 0 { }
fmt.Fprintln(out, summary) if !client.Quiet || errorsOrWarnings > 0 {
} fmt.Fprintln(out, summary)
return nil }
}, return nil
} },
}
f := cmd.Flags()
f.BoolVar(&client.Strict, "strict", false, "fail on lint warnings") f := cmd.Flags()
f.BoolVar(&client.WithSubcharts, "with-subcharts", false, "lint dependent charts") f.BoolVar(&client.Strict, "strict", false, "fail on lint warnings")
f.BoolVar(&client.Quiet, "quiet", false, "print only warnings and errors") f.BoolVar(&client.WithSubcharts, "with-subcharts", false, "lint dependent charts")
f.BoolVar(&client.SkipSchemaValidation, "skip-schema-validation", false, "if set, disables JSON schema validation") f.BoolVar(&client.Quiet, "quiet", false, "print only warnings and errors")
f.StringVar(&kubeVersion, "kube-version", "", "Kubernetes version used for capabilities and deprecation checks") f.BoolVar(&client.SkipSchemaValidation, "skip-schema-validation", false, "if set, disables JSON schema validation")
addValueOptionsFlags(f, valueOpts) f.StringVar(&kubeVersion, "kube-version", "", "Kubernetes version used for capabilities and deprecation checks")
addValueOptionsFlags(f, valueOpts)
return cmd
} return cmd
}

@ -1,137 +1,138 @@
/* /*
Copyright The Helm Authors. Copyright The Helm Authors.
Copyright (c) 2024 Rakuten Symphony India.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. Licensed under the Apache License, Version 2.0 (the "License");
You may obtain a copy of the License at 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
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, Unless required by applicable law or agreed to in writing, software
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. distributed under the License is distributed on an "AS IS" BASIS,
See the License for the specific language governing permissions and WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
limitations under the License. See the License for the specific language governing permissions and
*/ limitations under the License.
*/
package main
package main
import (
"fmt" import (
"io" "fmt"
"os" "io"
"path/filepath" "os"
"path/filepath"
"github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/pkg/errors"
"github.com/spf13/cobra"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/cli/values" "helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/downloader" "helm.sh/helm/v3/pkg/cli/values"
"helm.sh/helm/v3/pkg/getter" "helm.sh/helm/v3/pkg/downloader"
) "helm.sh/helm/v3/pkg/getter"
)
const packageDesc = `
This command packages a chart into a versioned chart archive file. If a path const packageDesc = `
is given, this will look at that path for a chart (which must contain a This command packages a chart into a versioned chart archive file. If a path
Chart.yaml file) and then package that directory. is given, this will look at that path for a chart (which must contain a
Chart.yaml file) and then package that directory.
Versioned chart archives are used by Helm package repositories.
Versioned chart archives are used by Helm package repositories.
To sign a chart, use the '--sign' flag. In most cases, you should also
provide '--keyring path/to/secret/keys' and '--key keyname'. To sign a chart, use the '--sign' flag. In most cases, you should also
provide '--keyring path/to/secret/keys' and '--key keyname'.
$ helm package --sign ./mychart --key mykey --keyring ~/.gnupg/secring.gpg
$ helm package --sign ./mychart --key mykey --keyring ~/.gnupg/secring.gpg
If '--keyring' is not specified, Helm usually defaults to the public keyring
unless your environment is otherwise configured. If '--keyring' is not specified, Helm usually defaults to the public keyring
` unless your environment is otherwise configured.
`
func newPackageCmd(out io.Writer) *cobra.Command {
client := action.NewPackage() func newPackageCmd(out io.Writer) *cobra.Command {
valueOpts := &values.Options{} client := action.NewPackage()
valueOpts := &values.Options{}
cmd := &cobra.Command{
Use: "package [CHART_PATH] [...]", cmd := &cobra.Command{
Short: "package a chart directory into a chart archive", Use: "package [CHART_PATH] [...]",
Long: packageDesc, Short: "package a chart directory into a chart archive",
RunE: func(_ *cobra.Command, args []string) error { Long: packageDesc,
if len(args) == 0 { RunE: func(_ *cobra.Command, args []string) error {
return errors.Errorf("need at least one argument, the path to the chart") if len(args) == 0 {
} return errors.Errorf("need at least one argument, the path to the chart")
if client.Sign { }
if client.Key == "" { if client.Sign {
return errors.New("--key is required for signing a package") if client.Key == "" {
} return errors.New("--key is required for signing a package")
if client.Keyring == "" { }
return errors.New("--keyring is required for signing a package") if client.Keyring == "" {
} return errors.New("--keyring is required for signing a package")
} }
client.RepositoryConfig = settings.RepositoryConfig }
client.RepositoryCache = settings.RepositoryCache client.RepositoryConfig = settings.RepositoryConfig
p := getter.All(settings) client.RepositoryCache = settings.RepositoryCache
vals, err := valueOpts.MergeValues(p) p := getter.All(settings)
if err != nil { vals, err := valueOpts.MergeValues(p, settings.VaultAddress, settings.Token)
return err if err != nil {
} return err
}
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
client.InsecureSkipTLSverify, client.PlainHTTP, client.Username, client.Password) registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
if err != nil { client.InsecureSkipTLSverify, client.PlainHTTP, client.Username, client.Password)
return fmt.Errorf("missing registry client: %w", err) if err != nil {
} return fmt.Errorf("missing registry client: %w", err)
}
for i := 0; i < len(args); i++ {
path, err := filepath.Abs(args[i]) for i := 0; i < len(args); i++ {
if err != nil { path, err := filepath.Abs(args[i])
return err if err != nil {
} return err
if _, err := os.Stat(args[i]); err != nil { }
return err if _, err := os.Stat(args[i]); err != nil {
} return err
}
if client.DependencyUpdate {
downloadManager := &downloader.Manager{ if client.DependencyUpdate {
Out: io.Discard, downloadManager := &downloader.Manager{
ChartPath: path, Out: io.Discard,
Keyring: client.Keyring, ChartPath: path,
Getters: p, Keyring: client.Keyring,
Debug: settings.Debug, Getters: p,
RegistryClient: registryClient, Debug: settings.Debug,
RepositoryConfig: settings.RepositoryConfig, RegistryClient: registryClient,
RepositoryCache: settings.RepositoryCache, RepositoryConfig: settings.RepositoryConfig,
} RepositoryCache: settings.RepositoryCache,
}
if err := downloadManager.Update(); err != nil {
return err if err := downloadManager.Update(); err != nil {
} return err
} }
p, err := client.Run(path, vals) }
if err != nil { p, err := client.Run(path, vals)
return err if err != nil {
} return err
fmt.Fprintf(out, "Successfully packaged chart and saved it to: %s\n", p) }
} fmt.Fprintf(out, "Successfully packaged chart and saved it to: %s\n", p)
return nil }
}, return nil
} },
}
f := cmd.Flags()
f.BoolVar(&client.Sign, "sign", false, "use a PGP private key to sign this package") f := cmd.Flags()
f.StringVar(&client.Key, "key", "", "name of the key to use when signing. Used if --sign is true") f.BoolVar(&client.Sign, "sign", false, "use a PGP private key to sign this package")
f.StringVar(&client.Keyring, "keyring", defaultKeyring(), "location of a public keyring") f.StringVar(&client.Key, "key", "", "name of the key to use when signing. Used if --sign is true")
f.StringVar(&client.PassphraseFile, "passphrase-file", "", `location of a file which contains the passphrase for the signing key. Use "-" in order to read from stdin.`) f.StringVar(&client.Keyring, "keyring", defaultKeyring(), "location of a public keyring")
f.StringVar(&client.Version, "version", "", "set the version on the chart to this semver version") f.StringVar(&client.PassphraseFile, "passphrase-file", "", `location of a file which contains the passphrase for the signing key. Use "-" in order to read from stdin.`)
f.StringVar(&client.AppVersion, "app-version", "", "set the appVersion on the chart to this version") f.StringVar(&client.Version, "version", "", "set the version on the chart to this semver version")
f.StringVarP(&client.Destination, "destination", "d", ".", "location to write the chart.") f.StringVar(&client.AppVersion, "app-version", "", "set the appVersion on the chart to this version")
f.BoolVarP(&client.DependencyUpdate, "dependency-update", "u", false, `update dependencies from "Chart.yaml" to dir "charts/" before packaging`) f.StringVarP(&client.Destination, "destination", "d", ".", "location to write the chart.")
f.StringVar(&client.Username, "username", "", "chart repository username where to locate the requested chart") f.BoolVarP(&client.DependencyUpdate, "dependency-update", "u", false, `update dependencies from "Chart.yaml" to dir "charts/" before packaging`)
f.StringVar(&client.Password, "password", "", "chart repository password where to locate the requested chart") f.StringVar(&client.Username, "username", "", "chart repository username where to locate the requested chart")
f.StringVar(&client.CertFile, "cert-file", "", "identify HTTPS client using this SSL certificate file") f.StringVar(&client.Password, "password", "", "chart repository password where to locate the requested chart")
f.StringVar(&client.KeyFile, "key-file", "", "identify HTTPS client using this SSL key file") f.StringVar(&client.CertFile, "cert-file", "", "identify HTTPS client using this SSL certificate file")
f.BoolVar(&client.InsecureSkipTLSverify, "insecure-skip-tls-verify", false, "skip tls certificate checks for the chart download") f.StringVar(&client.KeyFile, "key-file", "", "identify HTTPS client using this SSL key file")
f.BoolVar(&client.PlainHTTP, "plain-http", false, "use insecure HTTP connections for the chart download") f.BoolVar(&client.InsecureSkipTLSverify, "insecure-skip-tls-verify", false, "skip tls certificate checks for the chart download")
f.StringVar(&client.CaFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle") f.BoolVar(&client.PlainHTTP, "plain-http", false, "use insecure HTTP connections for the chart download")
f.StringVar(&client.CaFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
return cmd
} return cmd
}

@ -1,304 +1,305 @@
/* /*
Copyright The Helm Authors. Copyright The Helm Authors.
Copyright (c) 2024 Rakuten Symphony India.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. Licensed under the Apache License, Version 2.0 (the "License");
You may obtain a copy of the License at 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
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, Unless required by applicable law or agreed to in writing, software
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. distributed under the License is distributed on an "AS IS" BASIS,
See the License for the specific language governing permissions and WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
limitations under the License. See the License for the specific language governing permissions and
*/ limitations under the License.
*/
package main
package main
import (
"context" import (
"fmt" "context"
"io" "fmt"
"log" "io"
"os" "log"
"os/signal" "os"
"syscall" "os/signal"
"time" "syscall"
"time"
"github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/pkg/errors"
"github.com/spf13/cobra"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v3/pkg/chart/loader" "helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/cli/output" "helm.sh/helm/v3/pkg/chart/loader"
"helm.sh/helm/v3/pkg/cli/values" "helm.sh/helm/v3/pkg/cli/output"
"helm.sh/helm/v3/pkg/downloader" "helm.sh/helm/v3/pkg/cli/values"
"helm.sh/helm/v3/pkg/getter" "helm.sh/helm/v3/pkg/downloader"
"helm.sh/helm/v3/pkg/release" "helm.sh/helm/v3/pkg/getter"
"helm.sh/helm/v3/pkg/storage/driver" "helm.sh/helm/v3/pkg/release"
) "helm.sh/helm/v3/pkg/storage/driver"
)
const upgradeDesc = `
This command upgrades a release to a new version of a chart. const upgradeDesc = `
This command upgrades a release to a new version of a chart.
The upgrade arguments must be a release and chart. The chart
argument can be either: a chart reference('example/mariadb'), a path to a chart directory, The upgrade arguments must be a release and chart. The chart
a packaged chart, or a fully qualified URL. For chart references, the latest argument can be either: a chart reference('example/mariadb'), a path to a chart directory,
version will be specified unless the '--version' flag is set. a packaged chart, or a fully qualified URL. For chart references, the latest
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 To override values in a chart, use either the '--values' flag and pass in a file
values, use '--set-string'. You can use '--set-file' to set individual or use the '--set' flag and pass configuration from the command line, to force string
values from a file when the value itself is too long for the command line values, use '--set-string'. You can use '--set-file' to set individual
or is dynamically generated. You can also use '--set-json' to set json values values from a file when the value itself is too long for the command line
(scalars/objects/arrays) from the command line. or is dynamically generated. You can also use '--set-json' to set json values
(scalars/objects/arrays) from the command line.
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 You can specify the '--values'/'-f' flag multiple times. The priority will be given to the
contained a key called 'Test', the value set in override.yaml would take precedence: last (right-most) file specified. For example, if both myvalues.yaml and override.yaml
contained a key called 'Test', the value set in override.yaml would take precedence:
$ helm upgrade -f myvalues.yaml -f override.yaml redis ./redis
$ helm upgrade -f myvalues.yaml -f override.yaml redis ./redis
You can specify the '--set' flag multiple times. The priority will be given to the
last (right-most) set specified. For example, if both 'bar' and 'newbar' values are You can specify the '--set' flag multiple times. The priority will be given to the
set for a key called 'foo', the 'newbar' value would take precedence: last (right-most) set specified. For example, if both 'bar' and 'newbar' values are
set for a key called 'foo', the 'newbar' value would take precedence:
$ helm upgrade --set foo=bar --set foo=newbar redis ./redis
$ helm upgrade --set foo=bar --set foo=newbar redis ./redis
You can update the values for an existing release with this command as well via the
'--reuse-values' flag. The 'RELEASE' and 'CHART' arguments should be set to the original You can update the values for an existing release with this command as well via the
parameters, and existing values will be merged with any values set via '--values'/'-f' '--reuse-values' flag. The 'RELEASE' and 'CHART' arguments should be set to the original
or '--set' flags. Priority is given to new values. parameters, and existing values will be merged with any values set via '--values'/'-f'
or '--set' flags. Priority is given to new values.
$ helm upgrade --reuse-values --set foo=bar --set foo=newbar redis ./redis
$ helm upgrade --reuse-values --set foo=bar --set foo=newbar redis ./redis
The --dry-run flag will output all generated chart manifests, including Secrets
which can contain sensitive values. To hide Kubernetes Secrets use the The --dry-run flag will output all generated chart manifests, including Secrets
--hide-secret flag. Please carefully consider how and when these flags are used. which can contain sensitive values. To hide Kubernetes Secrets use the
` --hide-secret flag. Please carefully consider how and when these flags are used.
`
func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
client := action.NewUpgrade(cfg) func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
valueOpts := &values.Options{} client := action.NewUpgrade(cfg)
var outfmt output.Format valueOpts := &values.Options{}
var createNamespace bool var outfmt output.Format
var createNamespace bool
cmd := &cobra.Command{
Use: "upgrade [RELEASE] [CHART]", cmd := &cobra.Command{
Short: "upgrade a release", Use: "upgrade [RELEASE] [CHART]",
Long: upgradeDesc, Short: "upgrade a release",
Args: require.ExactArgs(2), Long: upgradeDesc,
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { Args: require.ExactArgs(2),
if len(args) == 0 { ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return compListReleases(toComplete, args, cfg) if len(args) == 0 {
} return compListReleases(toComplete, args, cfg)
if len(args) == 1 { }
return compListCharts(toComplete, true) if len(args) == 1 {
} return compListCharts(toComplete, true)
return noMoreArgsComp() }
}, return noMoreArgsComp()
RunE: func(_ *cobra.Command, args []string) error { },
client.Namespace = settings.Namespace() RunE: func(_ *cobra.Command, args []string) error {
client.Namespace = settings.Namespace()
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
client.InsecureSkipTLSverify, client.PlainHTTP, client.Username, client.Password) registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
if err != nil { client.InsecureSkipTLSverify, client.PlainHTTP, client.Username, client.Password)
return fmt.Errorf("missing registry client: %w", err) if err != nil {
} return fmt.Errorf("missing registry client: %w", err)
client.SetRegistryClient(registryClient) }
client.SetRegistryClient(registryClient)
// This is for the case where "" is specifically passed in as a
// value. When there is no value passed in NoOptDefVal will be used // This is for the case where "" is specifically passed in as a
// and it is set to client. See addInstallFlags. // value. When there is no value passed in NoOptDefVal will be used
if client.DryRunOption == "" { // and it is set to client. See addInstallFlags.
client.DryRunOption = "none" if client.DryRunOption == "" {
} client.DryRunOption = "none"
// Fixes #7002 - Support reading values from STDIN for `upgrade` command }
// Must load values AFTER determining if we have to call install so that values loaded from stdin are not read twice // Fixes #7002 - Support reading values from STDIN for `upgrade` command
if client.Install { // Must load values AFTER determining if we have to call install so that values loaded from stdin are not read twice
// If a release does not exist, install it. if client.Install {
histClient := action.NewHistory(cfg) // If a release does not exist, install it.
histClient.Max = 1 histClient := action.NewHistory(cfg)
versions, err := histClient.Run(args[0]) histClient.Max = 1
if err == driver.ErrReleaseNotFound || isReleaseUninstalled(versions) { versions, err := histClient.Run(args[0])
// Only print this to stdout for table output if err == driver.ErrReleaseNotFound || isReleaseUninstalled(versions) {
if outfmt == output.Table { // Only print this to stdout for table output
fmt.Fprintf(out, "Release %q does not exist. Installing it now.\n", args[0]) if outfmt == output.Table {
} fmt.Fprintf(out, "Release %q does not exist. Installing it now.\n", args[0])
instClient := action.NewInstall(cfg) }
instClient.CreateNamespace = createNamespace instClient := action.NewInstall(cfg)
instClient.ChartPathOptions = client.ChartPathOptions instClient.CreateNamespace = createNamespace
instClient.Force = client.Force instClient.ChartPathOptions = client.ChartPathOptions
instClient.DryRun = client.DryRun instClient.Force = client.Force
instClient.DryRunOption = client.DryRunOption instClient.DryRun = client.DryRun
instClient.DisableHooks = client.DisableHooks instClient.DryRunOption = client.DryRunOption
instClient.SkipCRDs = client.SkipCRDs instClient.DisableHooks = client.DisableHooks
instClient.Timeout = client.Timeout instClient.SkipCRDs = client.SkipCRDs
instClient.Wait = client.Wait instClient.Timeout = client.Timeout
instClient.WaitForJobs = client.WaitForJobs instClient.Wait = client.Wait
instClient.Devel = client.Devel instClient.WaitForJobs = client.WaitForJobs
instClient.Namespace = client.Namespace instClient.Devel = client.Devel
instClient.Atomic = client.Atomic instClient.Namespace = client.Namespace
instClient.PostRenderer = client.PostRenderer instClient.Atomic = client.Atomic
instClient.DisableOpenAPIValidation = client.DisableOpenAPIValidation instClient.PostRenderer = client.PostRenderer
instClient.SubNotes = client.SubNotes instClient.DisableOpenAPIValidation = client.DisableOpenAPIValidation
instClient.HideNotes = client.HideNotes instClient.SubNotes = client.SubNotes
instClient.SkipSchemaValidation = client.SkipSchemaValidation instClient.HideNotes = client.HideNotes
instClient.Description = client.Description instClient.SkipSchemaValidation = client.SkipSchemaValidation
instClient.DependencyUpdate = client.DependencyUpdate instClient.Description = client.Description
instClient.Labels = client.Labels instClient.DependencyUpdate = client.DependencyUpdate
instClient.EnableDNS = client.EnableDNS instClient.Labels = client.Labels
instClient.HideSecret = client.HideSecret instClient.EnableDNS = client.EnableDNS
instClient.HideSecret = client.HideSecret
if isReleaseUninstalled(versions) {
instClient.Replace = true if isReleaseUninstalled(versions) {
} instClient.Replace = true
}
rel, err := runInstall(args, instClient, valueOpts, out)
if err != nil { rel, err := runInstall(args, instClient, valueOpts, out)
return err if err != nil {
} return err
return outfmt.Write(out, &statusPrinter{rel, settings.Debug, false, false, false, instClient.HideNotes}) }
} else if err != nil { return outfmt.Write(out, &statusPrinter{rel, settings.Debug, false, false, false, instClient.HideNotes})
return err } else if err != nil {
} return err
} }
}
if client.Version == "" && client.Devel {
debug("setting version to >0.0.0-0") if client.Version == "" && client.Devel {
client.Version = ">0.0.0-0" debug("setting version to >0.0.0-0")
} client.Version = ">0.0.0-0"
}
chartPath, err := client.ChartPathOptions.LocateChart(args[1], settings)
if err != nil { chartPath, err := client.ChartPathOptions.LocateChart(args[1], settings)
return err if err != nil {
} return err
// Validate dry-run flag value is one of the allowed values }
if err := validateDryRunOptionFlag(client.DryRunOption); err != nil { // Validate dry-run flag value is one of the allowed values
return err if err := validateDryRunOptionFlag(client.DryRunOption); err != nil {
} return err
}
p := getter.All(settings)
vals, err := valueOpts.MergeValues(p) p := getter.All(settings)
if err != nil { vals, err := valueOpts.MergeValues(p, settings.VaultAddress, settings.Token)
return err if err != nil {
} return err
}
// Check chart dependencies to make sure all are present in /charts
ch, err := loader.Load(chartPath) // Check chart dependencies to make sure all are present in /charts
if err != nil { ch, err := loader.Load(chartPath)
return err if err != nil {
} return err
if req := ch.Metadata.Dependencies; req != nil { }
if err := action.CheckDependencies(ch, req); err != nil { if req := ch.Metadata.Dependencies; req != 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 err := action.CheckDependencies(ch, req); err != nil {
if client.DependencyUpdate { err = errors.Wrap(err, "An error occurred while checking for chart dependencies. You may need to run `helm dependency build` to fetch missing dependencies")
man := &downloader.Manager{ if client.DependencyUpdate {
Out: out, man := &downloader.Manager{
ChartPath: chartPath, Out: out,
Keyring: client.ChartPathOptions.Keyring, ChartPath: chartPath,
SkipUpdate: false, Keyring: client.ChartPathOptions.Keyring,
Getters: p, SkipUpdate: false,
RepositoryConfig: settings.RepositoryConfig, Getters: p,
RepositoryCache: settings.RepositoryCache, RepositoryConfig: settings.RepositoryConfig,
Debug: settings.Debug, RepositoryCache: settings.RepositoryCache,
} Debug: settings.Debug,
if err := man.Update(); err != nil { }
return err if err := man.Update(); err != nil {
} return err
// Reload the chart with the updated Chart.lock file. }
if ch, err = loader.Load(chartPath); err != nil { // Reload the chart with the updated Chart.lock file.
return errors.Wrap(err, "failed reloading chart after repo update") if ch, err = loader.Load(chartPath); err != nil {
} return errors.Wrap(err, "failed reloading chart after repo update")
} else { }
return err } else {
} return err
} }
} }
}
if ch.Metadata.Deprecated {
warning("This chart is deprecated") if ch.Metadata.Deprecated {
} warning("This chart is deprecated")
}
// Create context and prepare the handle of SIGTERM
ctx := context.Background() // Create context and prepare the handle of SIGTERM
ctx, cancel := context.WithCancel(ctx) ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
// Set up channel on which to send signal notifications.
// We must use a buffered channel or risk missing the signal // Set up channel on which to send signal notifications.
// if we're not ready to receive when the signal is sent. // We must use a buffered channel or risk missing the signal
cSignal := make(chan os.Signal, 2) // if we're not ready to receive when the signal is sent.
signal.Notify(cSignal, os.Interrupt, syscall.SIGTERM) cSignal := make(chan os.Signal, 2)
go func() { signal.Notify(cSignal, os.Interrupt, syscall.SIGTERM)
<-cSignal go func() {
fmt.Fprintf(out, "Release %s has been cancelled.\n", args[0]) <-cSignal
cancel() fmt.Fprintf(out, "Release %s has been cancelled.\n", args[0])
}() cancel()
}()
rel, err := client.RunWithContext(ctx, args[0], ch, vals)
rel, err := client.RunWithContext(ctx, args[0], ch, vals)
if err != nil {
return errors.Wrap(err, "UPGRADE FAILED") if err != nil {
} return errors.Wrap(err, "UPGRADE FAILED")
}
if outfmt == output.Table {
fmt.Fprintf(out, "Release %q has been upgraded. Happy Helming!\n", args[0]) if outfmt == output.Table {
} fmt.Fprintf(out, "Release %q has been upgraded. Happy Helming!\n", args[0])
}
return outfmt.Write(out, &statusPrinter{rel, settings.Debug, false, false, false, client.HideNotes})
}, return outfmt.Write(out, &statusPrinter{rel, settings.Debug, false, false, false, client.HideNotes})
} },
}
f := cmd.Flags()
f.BoolVar(&createNamespace, "create-namespace", false, "if --install is set, create the release namespace if not present") f := cmd.Flags()
f.BoolVarP(&client.Install, "install", "i", false, "if a release by this name doesn't already exist, run an install") f.BoolVar(&createNamespace, "create-namespace", false, "if --install is set, create the release namespace if not present")
f.BoolVar(&client.Devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored") f.BoolVarP(&client.Install, "install", "i", false, "if a release by this name doesn't already exist, run an install")
f.StringVar(&client.DryRunOption, "dry-run", "", "simulate an install. If --dry-run is set with no option being specified or as '--dry-run=client', it will not attempt cluster connections. Setting '--dry-run=server' allows attempting cluster connections.") f.BoolVar(&client.Devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored")
f.BoolVar(&client.HideSecret, "hide-secret", false, "hide Kubernetes Secrets when also using the --dry-run flag") f.StringVar(&client.DryRunOption, "dry-run", "", "simulate an install. If --dry-run is set with no option being specified or as '--dry-run=client', it will not attempt cluster connections. Setting '--dry-run=server' allows attempting cluster connections.")
f.Lookup("dry-run").NoOptDefVal = "client" f.BoolVar(&client.HideSecret, "hide-secret", false, "hide Kubernetes Secrets when also using the --dry-run flag")
f.BoolVar(&client.Recreate, "recreate-pods", false, "performs pods restart for the resource if applicable") f.Lookup("dry-run").NoOptDefVal = "client"
f.MarkDeprecated("recreate-pods", "functionality will no longer be updated. Consult the documentation for other methods to recreate pods") f.BoolVar(&client.Recreate, "recreate-pods", false, "performs pods restart for the resource if applicable")
f.BoolVar(&client.Force, "force", false, "force resource updates through a replacement strategy") f.MarkDeprecated("recreate-pods", "functionality will no longer be updated. Consult the documentation for other methods to recreate pods")
f.BoolVar(&client.DisableHooks, "no-hooks", false, "disable pre/post upgrade hooks") f.BoolVar(&client.Force, "force", false, "force resource updates through a replacement strategy")
f.BoolVar(&client.DisableOpenAPIValidation, "disable-openapi-validation", false, "if set, the upgrade process will not validate rendered templates against the Kubernetes OpenAPI Schema") f.BoolVar(&client.DisableHooks, "no-hooks", false, "disable pre/post upgrade hooks")
f.BoolVar(&client.SkipCRDs, "skip-crds", false, "if set, no CRDs will be installed when an upgrade is performed with install flag enabled. By default, CRDs are installed if not already present, when an upgrade is performed with install flag enabled") f.BoolVar(&client.DisableOpenAPIValidation, "disable-openapi-validation", false, "if set, the upgrade process will not validate rendered templates against the Kubernetes OpenAPI Schema")
f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)") f.BoolVar(&client.SkipCRDs, "skip-crds", false, "if set, no CRDs will be installed when an upgrade is performed with install flag enabled. By default, CRDs are installed if not already present, when an upgrade is performed with install flag enabled")
f.BoolVar(&client.ResetValues, "reset-values", false, "when upgrading, reset the values to the ones built into the chart") f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)")
f.BoolVar(&client.ReuseValues, "reuse-values", false, "when upgrading, reuse the last release's values and merge in any overrides from the command line via --set and -f. If '--reset-values' is specified, this is ignored") f.BoolVar(&client.ResetValues, "reset-values", false, "when upgrading, reset the values to the ones built into the chart")
f.BoolVar(&client.ResetThenReuseValues, "reset-then-reuse-values", false, "when upgrading, reset the values to the ones built into the chart, apply the last release's values and merge in any overrides from the command line via --set and -f. If '--reset-values' or '--reuse-values' is specified, this is ignored") f.BoolVar(&client.ReuseValues, "reuse-values", false, "when upgrading, reuse the last release's values and merge in any overrides from the command line via --set and -f. If '--reset-values' is specified, this is ignored")
f.BoolVar(&client.Wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment, StatefulSet, or ReplicaSet are in a ready state before marking the release as successful. It will wait for as long as --timeout") f.BoolVar(&client.ResetThenReuseValues, "reset-then-reuse-values", false, "when upgrading, reset the values to the ones built into the chart, apply the last release's values and merge in any overrides from the command line via --set and -f. If '--reset-values' or '--reuse-values' is specified, this is ignored")
f.BoolVar(&client.WaitForJobs, "wait-for-jobs", false, "if set and --wait enabled, will wait until all Jobs have been completed before marking the release as successful. It will wait for as long as --timeout") f.BoolVar(&client.Wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment, StatefulSet, or ReplicaSet are in a ready state before marking the release as successful. It will wait for as long as --timeout")
f.BoolVar(&client.Atomic, "atomic", false, "if set, upgrade process rolls back changes made in case of failed upgrade. The --wait flag will be set automatically if --atomic is used") f.BoolVar(&client.WaitForJobs, "wait-for-jobs", false, "if set and --wait enabled, will wait until all Jobs have been completed before marking the release as successful. It will wait for as long as --timeout")
f.IntVar(&client.MaxHistory, "history-max", settings.MaxHistory, "limit the maximum number of revisions saved per release. Use 0 for no limit") f.BoolVar(&client.Atomic, "atomic", false, "if set, upgrade process rolls back changes made in case of failed upgrade. The --wait flag will be set automatically if --atomic is used")
f.BoolVar(&client.CleanupOnFail, "cleanup-on-fail", false, "allow deletion of new resources created in this upgrade when upgrade fails") f.IntVar(&client.MaxHistory, "history-max", settings.MaxHistory, "limit the maximum number of revisions saved per release. Use 0 for no limit")
f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent") f.BoolVar(&client.CleanupOnFail, "cleanup-on-fail", false, "allow deletion of new resources created in this upgrade when upgrade fails")
f.BoolVar(&client.HideNotes, "hide-notes", false, "if set, do not show notes in upgrade output. Does not affect presence in chart metadata") f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent")
f.BoolVar(&client.SkipSchemaValidation, "skip-schema-validation", false, "if set, disables JSON schema validation") f.BoolVar(&client.HideNotes, "hide-notes", false, "if set, do not show notes in upgrade output. Does not affect presence in chart metadata")
f.StringToStringVarP(&client.Labels, "labels", "l", nil, "Labels that would be added to release metadata. Should be separated by comma. Original release labels will be merged with upgrade labels. You can unset label using null.") f.BoolVar(&client.SkipSchemaValidation, "skip-schema-validation", false, "if set, disables JSON schema validation")
f.StringVar(&client.Description, "description", "", "add a custom description") f.StringToStringVarP(&client.Labels, "labels", "l", nil, "Labels that would be added to release metadata. Should be separated by comma. Original release labels will be merged with upgrade labels. You can unset label using null.")
f.BoolVar(&client.DependencyUpdate, "dependency-update", false, "update dependencies if they are missing before installing the chart") f.StringVar(&client.Description, "description", "", "add a custom description")
f.BoolVar(&client.EnableDNS, "enable-dns", false, "enable DNS lookups when rendering templates") f.BoolVar(&client.DependencyUpdate, "dependency-update", false, "update dependencies if they are missing before installing the chart")
addChartPathOptionsFlags(f, &client.ChartPathOptions) f.BoolVar(&client.EnableDNS, "enable-dns", false, "enable DNS lookups when rendering templates")
addValueOptionsFlags(f, valueOpts) addChartPathOptionsFlags(f, &client.ChartPathOptions)
bindOutputFlag(cmd, &outfmt) addValueOptionsFlags(f, valueOpts)
bindPostRenderFlag(cmd, &client.PostRenderer) bindOutputFlag(cmd, &outfmt)
bindPostRenderFlag(cmd, &client.PostRenderer)
err := cmd.RegisterFlagCompletionFunc("version", func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 2 { err := cmd.RegisterFlagCompletionFunc("version", func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return nil, cobra.ShellCompDirectiveNoFileComp if len(args) != 2 {
} return nil, cobra.ShellCompDirectiveNoFileComp
return compVersionFlag(args[1], toComplete) }
}) return compVersionFlag(args[1], toComplete)
})
if err != nil {
log.Fatal(err) if err != nil {
} log.Fatal(err)
}
return cmd
} return cmd
}
func isReleaseUninstalled(versions []*release.Release) bool {
return len(versions) > 0 && versions[len(versions)-1].Info.Status == release.StatusUninstalled func isReleaseUninstalled(versions []*release.Release) bool {
} return len(versions) > 0 && versions[len(versions)-1].Info.Status == release.StatusUninstalled
}

@ -20,6 +20,7 @@ require (
github.com/gofrs/flock v0.12.1 github.com/gofrs/flock v0.12.1
github.com/gosuri/uitable v0.0.4 github.com/gosuri/uitable v0.0.4
github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/vault/api v1.15.0
github.com/jmoiron/sqlx v1.4.0 github.com/jmoiron/sqlx v1.4.0
github.com/lib/pq v1.10.9 github.com/lib/pq v1.10.9
github.com/mattn/go-shellwords v1.0.12 github.com/mattn/go-shellwords v1.0.12
@ -37,6 +38,7 @@ require (
golang.org/x/crypto v0.29.0 golang.org/x/crypto v0.29.0
golang.org/x/term v0.26.0 golang.org/x/term v0.26.0
golang.org/x/text v0.20.0 golang.org/x/text v0.20.0
gopkg.in/yaml.v3 v3.0.1
k8s.io/api v0.31.2 k8s.io/api v0.31.2
k8s.io/apiextensions-apiserver v0.31.2 k8s.io/apiextensions-apiserver v0.31.2
k8s.io/apimachinery v0.31.2 k8s.io/apimachinery v0.31.2
@ -77,11 +79,12 @@ require (
github.com/docker/go-metrics v0.0.1 // indirect github.com/docker/go-metrics v0.0.1 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
github.com/fatih/color v1.13.0 // indirect github.com/fatih/color v1.16.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-errors/errors v1.4.2 // indirect github.com/go-errors/errors v1.4.2 // indirect
github.com/go-gorp/gorp/v3 v3.1.0 // indirect github.com/go-gorp/gorp/v3 v3.1.0 // indirect
github.com/go-jose/go-jose/v4 v4.0.2 // indirect
github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect
@ -101,8 +104,15 @@ require (
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 // indirect
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
github.com/hashicorp/go-sockaddr v1.0.2 // indirect
github.com/hashicorp/golang-lru/arc/v2 v2.0.5 // indirect github.com/hashicorp/golang-lru/arc/v2 v2.0.5 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.5 // indirect github.com/hashicorp/golang-lru/v2 v2.0.5 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/huandu/xstrings v1.5.0 // indirect github.com/huandu/xstrings v1.5.0 // indirect
github.com/imdario/mergo v0.3.16 // indirect github.com/imdario/mergo v0.3.16 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect
@ -114,10 +124,12 @@ require (
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
github.com/mailru/easyjson v0.7.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/miekg/dns v1.1.57 // indirect github.com/miekg/dns v1.1.57 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/locker v1.0.1 // indirect github.com/moby/locker v1.0.1 // indirect
github.com/moby/spdystream v0.4.0 // indirect github.com/moby/spdystream v0.4.0 // indirect
@ -137,6 +149,7 @@ require (
github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 // indirect github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 // indirect
github.com/redis/go-redis/v9 v9.1.0 // indirect github.com/redis/go-redis/v9 v9.1.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/ryanuber/go-glob v1.0.0 // indirect
github.com/shopspring/decimal v1.4.0 // indirect github.com/shopspring/decimal v1.4.0 // indirect
github.com/spf13/cast v1.7.0 // indirect github.com/spf13/cast v1.7.0 // indirect
github.com/x448/float16 v0.8.4 // indirect github.com/x448/float16 v0.8.4 // indirect
@ -179,7 +192,6 @@ require (
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/component-base v0.31.2 // indirect k8s.io/component-base v0.31.2 // indirect
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect

@ -30,6 +30,7 @@ github.com/Microsoft/hcsshim v0.11.7 h1:vl/nj3Bar/CvJSYo7gIQPyRWc9f3c6IeSNavBTSZ
github.com/Microsoft/hcsshim v0.11.7/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA09d4bExKcU= github.com/Microsoft/hcsshim v0.11.7/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA09d4bExKcU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
@ -38,6 +39,7 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70= github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70=
@ -114,8 +116,9 @@ github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lSh
github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM=
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/foxcpp/go-mockdns v1.1.0 h1:jI0rD8M0wuYAxL7r/ynTrCQQq0BVqfB99Vgk7DlmewI= github.com/foxcpp/go-mockdns v1.1.0 h1:jI0rD8M0wuYAxL7r/ynTrCQQq0BVqfB99Vgk7DlmewI=
@ -128,6 +131,8 @@ github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxI
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs= github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs=
github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw=
github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk=
github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
@ -148,6 +153,8 @@ github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqw
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw=
github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
@ -208,12 +215,32 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 h1:om4Al8Oy7kCm/B86rLCLah4Dt5Aa0Fr5rYBG60OzwHQ=
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8=
github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U=
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
github.com/hashicorp/golang-lru/arc/v2 v2.0.5 h1:l2zaLDubNhW4XO3LnliVj0GXO3+/CGNJAg1dcN2Fpfw= github.com/hashicorp/golang-lru/arc/v2 v2.0.5 h1:l2zaLDubNhW4XO3LnliVj0GXO3+/CGNJAg1dcN2Fpfw=
github.com/hashicorp/golang-lru/arc/v2 v2.0.5/go.mod h1:ny6zBSQZi2JxIeYcv7kt2sH2PXJtirBN7RDhRpxPkxU= github.com/hashicorp/golang-lru/arc/v2 v2.0.5/go.mod h1:ny6zBSQZi2JxIeYcv7kt2sH2PXJtirBN7RDhRpxPkxU=
github.com/hashicorp/golang-lru/v2 v2.0.5 h1:wW7h1TG88eUIJ2i69gaE3uNVtEPIagzhGvHgwfx2Vm4= github.com/hashicorp/golang-lru/v2 v2.0.5 h1:wW7h1TG88eUIJ2i69gaE3uNVtEPIagzhGvHgwfx2Vm4=
github.com/hashicorp/golang-lru/v2 v2.0.5/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/golang-lru/v2 v2.0.5/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/vault/api v1.15.0 h1:O24FYQCWwhwKnF7CuSqP30S51rTV7vz1iACXE/pj5DA=
github.com/hashicorp/vault/api v1.15.0/go.mod h1:+5YTO09JGn0u+b6ySD/LLVf8WkJCPLAL2Vkmrn2+CM8=
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
@ -255,14 +282,13 @@ github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhn
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
@ -272,10 +298,17 @@ github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxU
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
@ -320,6 +353,7 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY= github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY=
github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjzg= github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjzg=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
@ -354,6 +388,9 @@ github.com/rubenv/sql-migrate v1.7.0 h1:HtQq1xyTN2ISmQDggnh0c9U3JlP8apWh8YO2jzlX
github.com/rubenv/sql-migrate v1.7.0/go.mod h1:S4wtDEG1CKn+0ShpTtzWhFpHHI5PvCUtiGI+C+Z2THE= github.com/rubenv/sql-migrate v1.7.0/go.mod h1:S4wtDEG1CKn+0ShpTtzWhFpHHI5PvCUtiGI+C+Z2THE=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
@ -500,24 +537,23 @@ golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

@ -1,267 +1,275 @@
/* /*
Copyright The Helm Authors. Copyright The Helm Authors.
Copyright (c) 2024 Rakuten Symphony India.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. Licensed under the Apache License, Version 2.0 (the "License");
You may obtain a copy of the License at 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
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, Unless required by applicable law or agreed to in writing, software
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. distributed under the License is distributed on an "AS IS" BASIS,
See the License for the specific language governing permissions and WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
limitations under the License. See the License for the specific language governing permissions and
*/ limitations under the License.
*/
/*
Package cli describes the operating environment for the Helm CLI. /*
Package cli describes the operating environment for the Helm CLI.
Helm's environment encapsulates all of the service dependencies Helm has.
These dependencies are expressed as interfaces so that alternate implementations Helm's environment encapsulates all of the service dependencies Helm has.
(mocks, etc.) can be easily generated. These dependencies are expressed as interfaces so that alternate implementations
*/ (mocks, etc.) can be easily generated.
package cli */
package cli
import (
"fmt" import (
"net/http" "fmt"
"os" "net/http"
"strconv" "os"
"strings" "strconv"
"strings"
"github.com/spf13/pflag"
"k8s.io/cli-runtime/pkg/genericclioptions" "github.com/spf13/pflag"
"k8s.io/client-go/rest" "k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/client-go/rest"
"helm.sh/helm/v3/internal/version"
"helm.sh/helm/v3/pkg/helmpath" "helm.sh/helm/v3/internal/version"
"helm.sh/helm/v3/pkg/kube" "helm.sh/helm/v3/pkg/helmpath"
) "helm.sh/helm/v3/pkg/kube"
)
// defaultMaxHistory sets the maximum number of releases to 0: unlimited
const defaultMaxHistory = 10 // defaultMaxHistory sets the maximum number of releases to 0: unlimited
const defaultMaxHistory = 10
// defaultBurstLimit sets the default client-side throttling limit
const defaultBurstLimit = 100 // defaultBurstLimit sets the default client-side throttling limit
const defaultBurstLimit = 100
// defaultQPS sets the default QPS value to 0 to use library defaults unless specified
const defaultQPS = float32(0) // defaultQPS sets the default QPS value to 0 to use library defaults unless specified
const defaultQPS = float32(0)
// EnvSettings describes all of the environment settings.
type EnvSettings struct { // EnvSettings describes all of the environment settings.
namespace string type EnvSettings struct {
config *genericclioptions.ConfigFlags namespace string
config *genericclioptions.ConfigFlags
// KubeConfig is the path to the kubeconfig file
KubeConfig string // KubeConfig is the path to the kubeconfig file
// KubeContext is the name of the kubeconfig context. KubeConfig string
KubeContext string // KubeContext is the name of the kubeconfig context.
// Bearer KubeToken used for authentication KubeContext string
KubeToken string // Bearer KubeToken used for authentication
// Username to impersonate for the operation KubeToken string
KubeAsUser string // Username to impersonate for the operation
// Groups to impersonate for the operation, multiple groups parsed from a comma delimited list KubeAsUser string
KubeAsGroups []string // Groups to impersonate for the operation, multiple groups parsed from a comma delimited list
// Kubernetes API Server Endpoint for authentication KubeAsGroups []string
KubeAPIServer string // Kubernetes API Server Endpoint for authentication
// Custom certificate authority file. KubeAPIServer string
KubeCaFile string // Custom certificate authority file.
// KubeInsecureSkipTLSVerify indicates if server's certificate will not be checked for validity. KubeCaFile string
// This makes the HTTPS connections insecure // KubeInsecureSkipTLSVerify indicates if server's certificate will not be checked for validity.
KubeInsecureSkipTLSVerify bool // This makes the HTTPS connections insecure
// KubeTLSServerName overrides the name to use for server certificate validation. KubeInsecureSkipTLSVerify bool
// If it is not provided, the hostname used to contact the server is used // KubeTLSServerName overrides the name to use for server certificate validation.
KubeTLSServerName string // If it is not provided, the hostname used to contact the server is used
// Debug indicates whether or not Helm is running in Debug mode. KubeTLSServerName string
Debug bool // Debug indicates whether or not Helm is running in Debug mode.
// RegistryConfig is the path to the registry config file. Debug bool
RegistryConfig string // RegistryConfig is the path to the registry config file.
// RepositoryConfig is the path to the repositories file. RegistryConfig string
RepositoryConfig string // RepositoryConfig is the path to the repositories file.
// RepositoryCache is the path to the repository cache directory. RepositoryConfig string
RepositoryCache string // RepositoryCache is the path to the repository cache directory.
// PluginsDirectory is the path to the plugins directory. RepositoryCache string
PluginsDirectory string // PluginsDirectory is the path to the plugins directory.
// MaxHistory is the max release history maintained. PluginsDirectory string
MaxHistory int // MaxHistory is the max release history maintained.
// BurstLimit is the default client-side throttling limit. MaxHistory int
BurstLimit int // BurstLimit is the default client-side throttling limit.
// QPS is queries per second which may be used to avoid throttling. BurstLimit int
QPS float32 // QPS is queries per second which may be used to avoid throttling.
} QPS float32
// Add fields to authenticate for Vault or remote repo
func New() *EnvSettings { Token string
env := &EnvSettings{ VaultAddress string
namespace: os.Getenv("HELM_NAMESPACE"), }
MaxHistory: envIntOr("HELM_MAX_HISTORY", defaultMaxHistory),
KubeContext: os.Getenv("HELM_KUBECONTEXT"), func New() *EnvSettings {
KubeToken: os.Getenv("HELM_KUBETOKEN"), env := &EnvSettings{
KubeAsUser: os.Getenv("HELM_KUBEASUSER"), namespace: os.Getenv("HELM_NAMESPACE"),
KubeAsGroups: envCSV("HELM_KUBEASGROUPS"), MaxHistory: envIntOr("HELM_MAX_HISTORY", defaultMaxHistory),
KubeAPIServer: os.Getenv("HELM_KUBEAPISERVER"), KubeContext: os.Getenv("HELM_KUBECONTEXT"),
KubeCaFile: os.Getenv("HELM_KUBECAFILE"), KubeToken: os.Getenv("HELM_KUBETOKEN"),
KubeTLSServerName: os.Getenv("HELM_KUBETLS_SERVER_NAME"), KubeAsUser: os.Getenv("HELM_KUBEASUSER"),
KubeInsecureSkipTLSVerify: envBoolOr("HELM_KUBEINSECURE_SKIP_TLS_VERIFY", false), KubeAsGroups: envCSV("HELM_KUBEASGROUPS"),
PluginsDirectory: envOr("HELM_PLUGINS", helmpath.DataPath("plugins")), KubeAPIServer: os.Getenv("HELM_KUBEAPISERVER"),
RegistryConfig: envOr("HELM_REGISTRY_CONFIG", helmpath.ConfigPath("registry/config.json")), KubeCaFile: os.Getenv("HELM_KUBECAFILE"),
RepositoryConfig: envOr("HELM_REPOSITORY_CONFIG", helmpath.ConfigPath("repositories.yaml")), KubeTLSServerName: os.Getenv("HELM_KUBETLS_SERVER_NAME"),
RepositoryCache: envOr("HELM_REPOSITORY_CACHE", helmpath.CachePath("repository")), KubeInsecureSkipTLSVerify: envBoolOr("HELM_KUBEINSECURE_SKIP_TLS_VERIFY", false),
BurstLimit: envIntOr("HELM_BURST_LIMIT", defaultBurstLimit), PluginsDirectory: envOr("HELM_PLUGINS", helmpath.DataPath("plugins")),
QPS: envFloat32Or("HELM_QPS", defaultQPS), RegistryConfig: envOr("HELM_REGISTRY_CONFIG", helmpath.ConfigPath("registry/config.json")),
} RepositoryConfig: envOr("HELM_REPOSITORY_CONFIG", helmpath.ConfigPath("repositories.yaml")),
env.Debug, _ = strconv.ParseBool(os.Getenv("HELM_DEBUG")) RepositoryCache: envOr("HELM_REPOSITORY_CACHE", helmpath.CachePath("repository")),
BurstLimit: envIntOr("HELM_BURST_LIMIT", defaultBurstLimit),
// bind to kubernetes config flags QPS: envFloat32Or("HELM_QPS", defaultQPS),
config := &genericclioptions.ConfigFlags{ VaultAddress: os.Getenv("VAULT_ADDR"),
Namespace: &env.namespace, Token: os.Getenv("VAULT_TOKEN"),
Context: &env.KubeContext, }
BearerToken: &env.KubeToken, env.Debug, _ = strconv.ParseBool(os.Getenv("HELM_DEBUG"))
APIServer: &env.KubeAPIServer,
CAFile: &env.KubeCaFile, // bind to kubernetes config flags
KubeConfig: &env.KubeConfig, config := &genericclioptions.ConfigFlags{
Impersonate: &env.KubeAsUser, Namespace: &env.namespace,
Insecure: &env.KubeInsecureSkipTLSVerify, Context: &env.KubeContext,
TLSServerName: &env.KubeTLSServerName, BearerToken: &env.KubeToken,
ImpersonateGroup: &env.KubeAsGroups, APIServer: &env.KubeAPIServer,
WrapConfigFn: func(config *rest.Config) *rest.Config { CAFile: &env.KubeCaFile,
config.Burst = env.BurstLimit KubeConfig: &env.KubeConfig,
config.QPS = env.QPS Impersonate: &env.KubeAsUser,
config.Wrap(func(rt http.RoundTripper) http.RoundTripper { Insecure: &env.KubeInsecureSkipTLSVerify,
return &kube.RetryingRoundTripper{Wrapped: rt} TLSServerName: &env.KubeTLSServerName,
}) ImpersonateGroup: &env.KubeAsGroups,
config.UserAgent = version.GetUserAgent() WrapConfigFn: func(config *rest.Config) *rest.Config {
return config config.Burst = env.BurstLimit
}, config.QPS = env.QPS
} config.Wrap(func(rt http.RoundTripper) http.RoundTripper {
if env.BurstLimit != defaultBurstLimit { return &kube.RetryingRoundTripper{Wrapped: rt}
config = config.WithDiscoveryBurst(env.BurstLimit) })
} config.UserAgent = version.GetUserAgent()
env.config = config return config
},
return env }
} if env.BurstLimit != defaultBurstLimit {
config = config.WithDiscoveryBurst(env.BurstLimit)
// AddFlags binds flags to the given flagset. }
func (s *EnvSettings) AddFlags(fs *pflag.FlagSet) { env.config = config
fs.StringVarP(&s.namespace, "namespace", "n", s.namespace, "namespace scope for this request")
fs.StringVar(&s.KubeConfig, "kubeconfig", "", "path to the kubeconfig file") return env
fs.StringVar(&s.KubeContext, "kube-context", s.KubeContext, "name of the kubeconfig context to use") }
fs.StringVar(&s.KubeToken, "kube-token", s.KubeToken, "bearer token used for authentication")
fs.StringVar(&s.KubeAsUser, "kube-as-user", s.KubeAsUser, "username to impersonate for the operation") // AddFlags binds flags to the given flagset.
fs.StringArrayVar(&s.KubeAsGroups, "kube-as-group", s.KubeAsGroups, "group to impersonate for the operation, this flag can be repeated to specify multiple groups.") func (s *EnvSettings) AddFlags(fs *pflag.FlagSet) {
fs.StringVar(&s.KubeAPIServer, "kube-apiserver", s.KubeAPIServer, "the address and the port for the Kubernetes API server") fs.StringVarP(&s.namespace, "namespace", "n", s.namespace, "namespace scope for this request")
fs.StringVar(&s.KubeCaFile, "kube-ca-file", s.KubeCaFile, "the certificate authority file for the Kubernetes API server connection") fs.StringVar(&s.KubeConfig, "kubeconfig", "", "path to the kubeconfig file")
fs.StringVar(&s.KubeTLSServerName, "kube-tls-server-name", s.KubeTLSServerName, "server name to use for Kubernetes API server certificate validation. If it is not provided, the hostname used to contact the server is used") fs.StringVar(&s.KubeContext, "kube-context", s.KubeContext, "name of the kubeconfig context to use")
fs.BoolVar(&s.KubeInsecureSkipTLSVerify, "kube-insecure-skip-tls-verify", s.KubeInsecureSkipTLSVerify, "if true, the Kubernetes API server's certificate will not be checked for validity. This will make your HTTPS connections insecure") fs.StringVar(&s.KubeToken, "kube-token", s.KubeToken, "bearer token used for authentication")
fs.BoolVar(&s.Debug, "debug", s.Debug, "enable verbose output") fs.StringVar(&s.KubeAsUser, "kube-as-user", s.KubeAsUser, "username to impersonate for the operation")
fs.StringVar(&s.RegistryConfig, "registry-config", s.RegistryConfig, "path to the registry config file") fs.StringArrayVar(&s.KubeAsGroups, "kube-as-group", s.KubeAsGroups, "group to impersonate for the operation, this flag can be repeated to specify multiple groups.")
fs.StringVar(&s.RepositoryConfig, "repository-config", s.RepositoryConfig, "path to the file containing repository names and URLs") fs.StringVar(&s.KubeAPIServer, "kube-apiserver", s.KubeAPIServer, "the address and the port for the Kubernetes API server")
fs.StringVar(&s.RepositoryCache, "repository-cache", s.RepositoryCache, "path to the directory containing cached repository indexes") fs.StringVar(&s.KubeCaFile, "kube-ca-file", s.KubeCaFile, "the certificate authority file for the Kubernetes API server connection")
fs.IntVar(&s.BurstLimit, "burst-limit", s.BurstLimit, "client-side default throttling limit") fs.StringVar(&s.KubeTLSServerName, "kube-tls-server-name", s.KubeTLSServerName, "server name to use for Kubernetes API server certificate validation. If it is not provided, the hostname used to contact the server is used")
fs.Float32Var(&s.QPS, "qps", s.QPS, "queries per second used when communicating with the Kubernetes API, not including bursting") fs.BoolVar(&s.KubeInsecureSkipTLSVerify, "kube-insecure-skip-tls-verify", s.KubeInsecureSkipTLSVerify, "if true, the Kubernetes API server's certificate will not be checked for validity. This will make your HTTPS connections insecure")
} fs.BoolVar(&s.Debug, "debug", s.Debug, "enable verbose output")
fs.StringVar(&s.RegistryConfig, "registry-config", s.RegistryConfig, "path to the registry config file")
func envOr(name, def string) string { fs.StringVar(&s.RepositoryConfig, "repository-config", s.RepositoryConfig, "path to the file containing repository names and URLs")
if v, ok := os.LookupEnv(name); ok { fs.StringVar(&s.RepositoryCache, "repository-cache", s.RepositoryCache, "path to the directory containing cached repository indexes")
return v fs.IntVar(&s.BurstLimit, "burst-limit", s.BurstLimit, "client-side default throttling limit")
} fs.Float32Var(&s.QPS, "qps", s.QPS, "queries per second used when communicating with the Kubernetes API, not including bursting")
return def fs.StringVar(&s.Token, "token", s.Token, "Token to authenticate Vault or remote repo to download values.yaml")
} fs.StringVar(&s.VaultAddress, "vault-address", s.VaultAddress, "Host or Address to access Vault server to download Helm values or resources file")
}
func envBoolOr(name string, def bool) bool {
if name == "" { func envOr(name, def string) string {
return def if v, ok := os.LookupEnv(name); ok {
} return v
envVal := envOr(name, strconv.FormatBool(def)) }
ret, err := strconv.ParseBool(envVal) return def
if err != nil { }
return def
} func envBoolOr(name string, def bool) bool {
return ret if name == "" {
} return def
}
func envIntOr(name string, def int) int { envVal := envOr(name, strconv.FormatBool(def))
if name == "" { ret, err := strconv.ParseBool(envVal)
return def if err != nil {
} return def
envVal := envOr(name, strconv.Itoa(def)) }
ret, err := strconv.Atoi(envVal) return ret
if err != nil { }
return def
} func envIntOr(name string, def int) int {
return ret if name == "" {
} return def
}
func envFloat32Or(name string, def float32) float32 { envVal := envOr(name, strconv.Itoa(def))
if name == "" { ret, err := strconv.Atoi(envVal)
return def if err != nil {
} return def
envVal := envOr(name, strconv.FormatFloat(float64(def), 'f', 2, 32)) }
ret, err := strconv.ParseFloat(envVal, 32) return ret
if err != nil { }
return def
} func envFloat32Or(name string, def float32) float32 {
return float32(ret) if name == "" {
} return def
}
func envCSV(name string) (ls []string) { envVal := envOr(name, strconv.FormatFloat(float64(def), 'f', 2, 32))
trimmed := strings.Trim(os.Getenv(name), ", ") ret, err := strconv.ParseFloat(envVal, 32)
if trimmed != "" { if err != nil {
ls = strings.Split(trimmed, ",") return def
} }
return return float32(ret)
} }
func (s *EnvSettings) EnvVars() map[string]string { func envCSV(name string) (ls []string) {
envvars := map[string]string{ trimmed := strings.Trim(os.Getenv(name), ", ")
"HELM_BIN": os.Args[0], if trimmed != "" {
"HELM_CACHE_HOME": helmpath.CachePath(""), ls = strings.Split(trimmed, ",")
"HELM_CONFIG_HOME": helmpath.ConfigPath(""), }
"HELM_DATA_HOME": helmpath.DataPath(""), return
"HELM_DEBUG": fmt.Sprint(s.Debug), }
"HELM_PLUGINS": s.PluginsDirectory,
"HELM_REGISTRY_CONFIG": s.RegistryConfig, func (s *EnvSettings) EnvVars() map[string]string {
"HELM_REPOSITORY_CACHE": s.RepositoryCache, envvars := map[string]string{
"HELM_REPOSITORY_CONFIG": s.RepositoryConfig, "HELM_BIN": os.Args[0],
"HELM_NAMESPACE": s.Namespace(), "HELM_CACHE_HOME": helmpath.CachePath(""),
"HELM_MAX_HISTORY": strconv.Itoa(s.MaxHistory), "HELM_CONFIG_HOME": helmpath.ConfigPath(""),
"HELM_BURST_LIMIT": strconv.Itoa(s.BurstLimit), "HELM_DATA_HOME": helmpath.DataPath(""),
"HELM_QPS": strconv.FormatFloat(float64(s.QPS), 'f', 2, 32), "HELM_DEBUG": fmt.Sprint(s.Debug),
"HELM_PLUGINS": s.PluginsDirectory,
// broken, these are populated from helm flags and not kubeconfig. "HELM_REGISTRY_CONFIG": s.RegistryConfig,
"HELM_KUBECONTEXT": s.KubeContext, "HELM_REPOSITORY_CACHE": s.RepositoryCache,
"HELM_KUBETOKEN": s.KubeToken, "HELM_REPOSITORY_CONFIG": s.RepositoryConfig,
"HELM_KUBEASUSER": s.KubeAsUser, "HELM_NAMESPACE": s.Namespace(),
"HELM_KUBEASGROUPS": strings.Join(s.KubeAsGroups, ","), "HELM_MAX_HISTORY": strconv.Itoa(s.MaxHistory),
"HELM_KUBEAPISERVER": s.KubeAPIServer, "HELM_BURST_LIMIT": strconv.Itoa(s.BurstLimit),
"HELM_KUBECAFILE": s.KubeCaFile, "HELM_QPS": strconv.FormatFloat(float64(s.QPS), 'f', 2, 32),
"HELM_KUBEINSECURE_SKIP_TLS_VERIFY": strconv.FormatBool(s.KubeInsecureSkipTLSVerify),
"HELM_KUBETLS_SERVER_NAME": s.KubeTLSServerName, // broken, these are populated from helm flags and not kubeconfig.
} "HELM_KUBECONTEXT": s.KubeContext,
if s.KubeConfig != "" { "HELM_KUBETOKEN": s.KubeToken,
envvars["KUBECONFIG"] = s.KubeConfig "HELM_KUBEASUSER": s.KubeAsUser,
} "HELM_KUBEASGROUPS": strings.Join(s.KubeAsGroups, ","),
return envvars "HELM_KUBEAPISERVER": s.KubeAPIServer,
} "HELM_KUBECAFILE": s.KubeCaFile,
"HELM_KUBEINSECURE_SKIP_TLS_VERIFY": strconv.FormatBool(s.KubeInsecureSkipTLSVerify),
// Namespace gets the namespace from the configuration "HELM_KUBETLS_SERVER_NAME": s.KubeTLSServerName,
func (s *EnvSettings) Namespace() string { }
if ns, _, err := s.config.ToRawKubeConfigLoader().Namespace(); err == nil { if s.KubeConfig != "" {
return ns envvars["KUBECONFIG"] = s.KubeConfig
} }
if s.namespace != "" { return envvars
return s.namespace }
}
return "default" // Namespace gets the namespace from the configuration
} func (s *EnvSettings) Namespace() string {
if ns, _, err := s.config.ToRawKubeConfigLoader().Namespace(); err == nil {
// SetNamespace sets the namespace in the configuration return ns
func (s *EnvSettings) SetNamespace(namespace string) { }
s.namespace = namespace if s.namespace != "" {
} return s.namespace
}
// RESTClientGetter gets the kubeconfig from EnvSettings return "default"
func (s *EnvSettings) RESTClientGetter() genericclioptions.RESTClientGetter { }
return s.config
} // 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
}

@ -1,147 +1,196 @@
/* /*
Copyright The Helm Authors. Copyright The Helm Authors.
Copyright (c) 2024 Rakuten Symphony India.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. Licensed under the Apache License, Version 2.0 (the "License");
You may obtain a copy of the License at 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
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, Unless required by applicable law or agreed to in writing, software
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. distributed under the License is distributed on an "AS IS" BASIS,
See the License for the specific language governing permissions and WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
limitations under the License. See the License for the specific language governing permissions and
*/ limitations under the License.
*/
package values
package values
import (
"io" import (
"net/url" "bytes"
"os" "io"
"strings" "net/url"
"os"
"github.com/pkg/errors" "path/filepath"
"sigs.k8s.io/yaml" "strings"
"helm.sh/helm/v3/pkg/getter" "github.com/pkg/errors"
"helm.sh/helm/v3/pkg/strvals" "gopkg.in/yaml.v3"
)
"helm.sh/helm/v3/pkg/getter"
// Options captures the different ways to specify values "helm.sh/helm/v3/pkg/strvals"
type Options struct { )
ValueFiles []string // -f/--values
StringValues []string // --set-string // Options captures the different ways to specify values
Values []string // --set type Options struct {
FileValues []string // --set-file ValueFiles []string // -f/--values
JSONValues []string // --set-json StringValues []string // --set-string
LiteralValues []string // --set-literal Values []string // --set
} FileValues []string // --set-file
JSONValues []string // --set-json
// MergeValues merges values from files specified via -f/--values and directly LiteralValues []string // --set-literal
// via --set-json, --set, --set-string, or --set-file, marshaling them to YAML PropertyFiles []string // -p/--property-file
func (opts *Options) MergeValues(p getter.Providers) (map[string]interface{}, error) { }
base := map[string]interface{}{}
// MergeValues merges values from files specified via -f/--values and directly
// User specified a values files via -f/--values // via --set-json, --set, --set-string, or --set-file, marshaling them to YAML
for _, filePath := range opts.ValueFiles { func (opts *Options) MergeValues(p getter.Providers, vaultAddr, vaultToken string) (map[string]interface{}, error) {
currentMap := map[string]interface{}{} base := map[string]interface{}{}
bytes, err := readFile(filePath, p) // User specified a values files via -f/--values
if err != nil { for _, filePath := range opts.ValueFiles {
return nil, err currentMap := map[string]interface{}{}
}
bytes, err := readFile(filePath, p, vaultAddr, vaultToken)
if err := yaml.Unmarshal(bytes, &currentMap); err != nil { if err != nil {
return nil, errors.Wrapf(err, "failed to parse %s", filePath) return nil, err
} }
// Merge with the previous map
base = mergeMaps(base, currentMap) if err := yaml.Unmarshal(bytes, &currentMap); err != nil {
} return nil, errors.Wrapf(err, "failed to parse %s", filePath)
}
// User specified a value via --set-json // Merge with the previous map
for _, value := range opts.JSONValues { base = mergeMaps(base, currentMap)
if err := strvals.ParseJSON(value, base); err != nil { }
return nil, errors.Errorf("failed parsing --set-json data %s", value)
} // User specified a value via --set-json
} for _, value := range opts.JSONValues {
if err := strvals.ParseJSON(value, base); err != nil {
// User specified a value via --set return nil, errors.Errorf("failed parsing --set-json data %s", value)
for _, value := range opts.Values { }
if err := strvals.ParseInto(value, base); err != nil { }
return nil, errors.Wrap(err, "failed parsing --set data")
} // User specified a value via --set
} for _, value := range opts.Values {
if err := strvals.ParseInto(value, base); err != nil {
// User specified a value via --set-string return nil, errors.Wrap(err, "failed parsing --set data")
for _, value := range opts.StringValues { }
if err := strvals.ParseIntoString(value, base); err != nil { }
return nil, errors.Wrap(err, "failed parsing --set-string data")
} // User specified a value via --set-string
} for _, value := range opts.StringValues {
if err := strvals.ParseIntoString(value, base); err != nil {
// User specified a value via --set-file return nil, errors.Wrap(err, "failed parsing --set-string data")
for _, value := range opts.FileValues { }
reader := func(rs []rune) (interface{}, error) { }
bytes, err := readFile(string(rs), p)
if err != nil { // User specified a value via --set-file
return nil, err for _, value := range opts.FileValues {
} reader := func(rs []rune) (interface{}, error) {
return string(bytes), err bytes, err := readFile(string(rs), p, vaultAddr, vaultToken)
} if err != nil {
if err := strvals.ParseIntoFile(value, base, reader); err != nil { return nil, err
return nil, errors.Wrap(err, "failed parsing --set-file data") }
} return string(bytes), err
} }
if err := strvals.ParseIntoFile(value, base, reader); err != nil {
// User specified a value via --set-literal return nil, errors.Wrap(err, "failed parsing --set-file data")
for _, value := range opts.LiteralValues { }
if err := strvals.ParseLiteralInto(value, base); err != nil { }
return nil, errors.Wrap(err, "failed parsing --set-literal data")
} // User specified a value via --set-literal
} for _, value := range opts.LiteralValues {
if err := strvals.ParseLiteralInto(value, base); err != nil {
return base, nil return nil, errors.Wrap(err, "failed parsing --set-literal data")
} }
}
func mergeMaps(a, b map[string]interface{}) map[string]interface{} {
out := make(map[string]interface{}, len(a)) // User specified property files via -p/--property-file
for k, v := range a { if len(opts.PropertyFiles) > 0 {
out[k] = v propertiesFilesMap, err := opts.MergeProperties(p, vaultAddr, vaultToken)
} if err != nil {
for k, v := range b { return nil, err
if v, ok := v.(map[string]interface{}); ok { }
if bv, ok := out[k]; ok { base["propertiesFiles"] = propertiesFilesMap
if bv, ok := bv.(map[string]interface{}); ok { }
out[k] = mergeMaps(bv, v)
continue return base, nil
} }
}
} // MergeProperties merges properties from files specified via --property-file
out[k] = v func (opts *Options) MergeProperties(p getter.Providers, vaultAddr, vaultToken string) (map[string]interface{}, error) {
} propertiesFilesMap := make(map[string]interface{})
return out
} for _, filePath := range opts.PropertyFiles {
data, err := readFile(filePath, p, vaultAddr, vaultToken)
// readFile load a file from stdin, the local directory, or a remote file with a url. if err != nil {
func readFile(filePath string, p getter.Providers) ([]byte, error) { return nil, errors.Wrap(err, "failed to fetch properties")
if strings.TrimSpace(filePath) == "-" { }
return io.ReadAll(os.Stdin)
} properties := make(map[string]interface{})
u, err := url.Parse(filePath) for _, line := range bytes.Split(data, []byte("\n")) {
if err != nil { lineStr := strings.TrimSpace(string(line))
return nil, err if lineStr == "" || strings.HasPrefix(lineStr, "#") {
} continue
}
// FIXME: maybe someone handle other protocols like ftp. parts := strings.SplitN(lineStr, ":", 2)
g, err := p.ByScheme(u.Scheme) if len(parts) != 2 {
if err != nil { return nil, errors.Errorf("invalid properties line: %s", lineStr)
return os.ReadFile(filePath) }
} key := strings.TrimSpace(parts[0])
data, err := g.Get(filePath, getter.WithURL(filePath)) value := strings.TrimSpace(parts[1])
if err != nil { properties[key] = value
return nil, err }
}
return data.Bytes(), err // Use the base file name (or Vault key) as the key in .Values.propertiesFiles
} baseName := filepath.Base(filePath)
propertiesFilesMap[baseName] = properties
}
return propertiesFilesMap, nil
}
func mergeMaps(a, b map[string]interface{}) map[string]interface{} {
out := make(map[string]interface{}, len(a))
for k, v := range a {
out[k] = v
}
for k, v := range b {
if v, ok := v.(map[string]interface{}); ok {
if bv, ok := out[k]; ok {
if bv, ok := bv.(map[string]interface{}); ok {
out[k] = mergeMaps(bv, v)
continue
}
}
}
out[k] = v
}
return out
}
// readFile load a file from stdin, the local directory, or a remote file with a url.
func readFile(filePath string, p getter.Providers, vaultAddr, vaultToken string) ([]byte, error) {
if strings.TrimSpace(filePath) == "-" {
return io.ReadAll(os.Stdin)
}
u, err := url.Parse(filePath)
if err != nil {
return nil, err
}
// FIXME: maybe someone handle other protocols like ftp.
g, err := p.ByScheme(u.Scheme)
if err != nil {
return os.ReadFile(filePath)
}
// Fetch data using the provider
data, err := g.Get(filePath, getter.WithURL(filePath), getter.WithAddress(vaultAddr), getter.WithToken(vaultToken))
if err != nil {
return nil, err
}
return data.Bytes(), err
}

@ -1,88 +1,89 @@
/* /*
Copyright The Helm Authors. Copyright The Helm Authors.
Copyright (c) 2024 Rakuten Symphony India.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. Licensed under the Apache License, Version 2.0 (the "License");
You may obtain a copy of the License at 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
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, Unless required by applicable law or agreed to in writing, software
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. distributed under the License is distributed on an "AS IS" BASIS,
See the License for the specific language governing permissions and WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
limitations under the License. See the License for the specific language governing permissions and
*/ limitations under the License.
*/
package values
package values
import (
"reflect" import (
"testing" "reflect"
"testing"
"helm.sh/helm/v3/pkg/getter"
) "helm.sh/helm/v3/pkg/getter"
)
func TestMergeValues(t *testing.T) {
nestedMap := map[string]interface{}{ func TestMergeValues(t *testing.T) {
"foo": "bar", nestedMap := map[string]interface{}{
"baz": map[string]string{ "foo": "bar",
"cool": "stuff", "baz": map[string]string{
}, "cool": "stuff",
} },
anotherNestedMap := map[string]interface{}{ }
"foo": "bar", anotherNestedMap := map[string]interface{}{
"baz": map[string]string{ "foo": "bar",
"cool": "things", "baz": map[string]string{
"awesome": "stuff", "cool": "things",
}, "awesome": "stuff",
} },
flatMap := map[string]interface{}{ }
"foo": "bar", flatMap := map[string]interface{}{
"baz": "stuff", "foo": "bar",
} "baz": "stuff",
anotherFlatMap := map[string]interface{}{ }
"testing": "fun", anotherFlatMap := map[string]interface{}{
} "testing": "fun",
}
testMap := mergeMaps(flatMap, nestedMap)
equal := reflect.DeepEqual(testMap, nestedMap) testMap := mergeMaps(flatMap, nestedMap)
if !equal { equal := reflect.DeepEqual(testMap, nestedMap)
t.Errorf("Expected a nested map to overwrite a flat value. Expected: %v, got %v", nestedMap, testMap) if !equal {
} t.Errorf("Expected a nested map to overwrite a flat value. Expected: %v, got %v", nestedMap, testMap)
}
testMap = mergeMaps(nestedMap, flatMap)
equal = reflect.DeepEqual(testMap, flatMap) testMap = mergeMaps(nestedMap, flatMap)
if !equal { equal = reflect.DeepEqual(testMap, flatMap)
t.Errorf("Expected a flat value to overwrite a map. Expected: %v, got %v", flatMap, testMap) if !equal {
} t.Errorf("Expected a flat value to overwrite a map. Expected: %v, got %v", flatMap, testMap)
}
testMap = mergeMaps(nestedMap, anotherNestedMap)
equal = reflect.DeepEqual(testMap, anotherNestedMap) testMap = mergeMaps(nestedMap, anotherNestedMap)
if !equal { equal = reflect.DeepEqual(testMap, anotherNestedMap)
t.Errorf("Expected a nested map to overwrite another nested map. Expected: %v, got %v", anotherNestedMap, testMap) if !equal {
} t.Errorf("Expected a nested map to overwrite another nested map. Expected: %v, got %v", anotherNestedMap, testMap)
}
testMap = mergeMaps(anotherFlatMap, anotherNestedMap)
expectedMap := map[string]interface{}{ testMap = mergeMaps(anotherFlatMap, anotherNestedMap)
"testing": "fun", expectedMap := map[string]interface{}{
"foo": "bar", "testing": "fun",
"baz": map[string]string{ "foo": "bar",
"cool": "things", "baz": map[string]string{
"awesome": "stuff", "cool": "things",
}, "awesome": "stuff",
} },
equal = reflect.DeepEqual(testMap, expectedMap) }
if !equal { equal = reflect.DeepEqual(testMap, expectedMap)
t.Errorf("Expected a map with different keys to merge properly with another map. Expected: %v, got %v", expectedMap, testMap) if !equal {
} t.Errorf("Expected a map with different keys to merge properly with another map. Expected: %v, got %v", expectedMap, testMap)
} }
}
func TestReadFile(t *testing.T) {
var p getter.Providers func TestReadFile(t *testing.T) {
filePath := "%a.txt" var p getter.Providers
_, err := readFile(filePath, p) filePath := "%a.txt"
if err == nil { _, err := readFile(filePath, p, "", "")
t.Errorf("Expected error when has special strings") if err == nil {
} t.Errorf("Expected error when has special strings")
} }
}

@ -1,220 +1,246 @@
/* /*
Copyright The Helm Authors. Copyright The Helm Authors.
Copyright (c) 2024 Rakuten Symphony India.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. Licensed under the Apache License, Version 2.0 (the "License");
You may obtain a copy of the License at 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
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, Unless required by applicable law or agreed to in writing, software
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. distributed under the License is distributed on an "AS IS" BASIS,
See the License for the specific language governing permissions and WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
limitations under the License. See the License for the specific language governing permissions and
*/ limitations under the License.
*/
package getter
package getter
import (
"bytes" import (
"net/http" "bytes"
"time" "net/http"
"time"
"github.com/pkg/errors"
"github.com/pkg/errors"
"helm.sh/helm/v3/pkg/cli"
"helm.sh/helm/v3/pkg/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.
// // options are generic parameters to be provided to the getter during instantiation.
// Getters may or may not ignore these parameters as they are passed in. //
type options struct { // Getters may or may not ignore these parameters as they are passed in.
url string type options struct {
certFile string url string
keyFile string certFile string
caFile string keyFile string
unTar bool caFile string
insecureSkipVerifyTLS bool unTar bool
plainHTTP bool insecureSkipVerifyTLS bool
acceptHeader string plainHTTP bool
username string acceptHeader string
password string username string
passCredentialsAll bool password string
userAgent string passCredentialsAll bool
version string userAgent string
registryClient *registry.Client version string
timeout time.Duration registryClient *registry.Client
transport *http.Transport timeout time.Duration
} transport *http.Transport
// Option allows specifying various settings configurable by the user for overriding the defaults // Added field for Vault integration
// used when performing Get operations with the Getter. address string // Vault address for accessing Vault server
type Option func(*options) token string // Vault token for authentication
}
// WithURL informs the getter the server name that will be used when fetching objects. Used in conjunction with
// WithTLSClientConfig to set the TLSClientConfig's server name. // Option allows specifying various settings configurable by the user for overriding the defaults
func WithURL(url string) Option { // used when performing Get operations with the Getter.
return func(opts *options) { type Option func(*options)
opts.url = url
} // WithURL informs the getter the server name that will be used when fetching objects. Used in conjunction with
} // WithTLSClientConfig to set the TLSClientConfig's server name.
func WithURL(url string) Option {
// WithAcceptHeader sets the request's Accept header as some REST APIs serve multiple content types return func(opts *options) {
func WithAcceptHeader(header string) Option { opts.url = url
return func(opts *options) { }
opts.acceptHeader = header }
}
} // WithAcceptHeader sets the request's Accept header as some REST APIs serve multiple content types
func WithAcceptHeader(header string) Option {
// WithBasicAuth sets the request's Authorization header to use the provided credentials return func(opts *options) {
func WithBasicAuth(username, password string) Option { opts.acceptHeader = header
return func(opts *options) { }
opts.username = username }
opts.password = password
} // WithBasicAuth sets the request's Authorization header to use the provided credentials
} func WithBasicAuth(username, password string) Option {
return func(opts *options) {
func WithPassCredentialsAll(pass bool) Option { opts.username = username
return func(opts *options) { opts.password = password
opts.passCredentialsAll = pass }
} }
}
func WithPassCredentialsAll(pass bool) Option {
// WithUserAgent sets the request's User-Agent header to use the provided agent name. return func(opts *options) {
func WithUserAgent(userAgent string) Option { opts.passCredentialsAll = pass
return func(opts *options) { }
opts.userAgent = userAgent }
}
} // WithUserAgent sets the request's User-Agent header to use the provided agent name.
func WithUserAgent(userAgent string) Option {
// WithInsecureSkipVerifyTLS determines if a TLS Certificate will be checked return func(opts *options) {
func WithInsecureSkipVerifyTLS(insecureSkipVerifyTLS bool) Option { opts.userAgent = userAgent
return func(opts *options) { }
opts.insecureSkipVerifyTLS = insecureSkipVerifyTLS }
}
} // WithInsecureSkipVerifyTLS determines if a TLS Certificate will be checked
func WithInsecureSkipVerifyTLS(insecureSkipVerifyTLS bool) Option {
// WithTLSClientConfig sets the client auth with the provided credentials. return func(opts *options) {
func WithTLSClientConfig(certFile, keyFile, caFile string) Option { opts.insecureSkipVerifyTLS = insecureSkipVerifyTLS
return func(opts *options) { }
opts.certFile = certFile }
opts.keyFile = keyFile
opts.caFile = caFile // WithTLSClientConfig sets the client auth with the provided credentials.
} func WithTLSClientConfig(certFile, keyFile, caFile string) Option {
} return func(opts *options) {
opts.certFile = certFile
func WithPlainHTTP(plainHTTP bool) Option { opts.keyFile = keyFile
return func(opts *options) { opts.caFile = caFile
opts.plainHTTP = plainHTTP }
} }
}
func WithPlainHTTP(plainHTTP bool) Option {
// WithTimeout sets the timeout for requests return func(opts *options) {
func WithTimeout(timeout time.Duration) Option { opts.plainHTTP = plainHTTP
return func(opts *options) { }
opts.timeout = timeout }
}
} // WithTimeout sets the timeout for requests
func WithTimeout(timeout time.Duration) Option {
func WithTagName(tagname string) Option { return func(opts *options) {
return func(opts *options) { opts.timeout = timeout
opts.version = tagname }
} }
}
func WithTagName(tagname string) Option {
func WithRegistryClient(client *registry.Client) Option { return func(opts *options) {
return func(opts *options) { opts.version = tagname
opts.registryClient = client }
} }
}
func WithRegistryClient(client *registry.Client) Option {
func WithUntar() Option { return func(opts *options) {
return func(opts *options) { opts.registryClient = client
opts.unTar = true }
} }
}
func WithUntar() Option {
// WithTransport sets the http.Transport to allow overwriting the HTTPGetter default. return func(opts *options) {
func WithTransport(transport *http.Transport) Option { opts.unTar = true
return func(opts *options) { }
opts.transport = transport }
}
} // WithTransport sets the http.Transport to allow overwriting the HTTPGetter default.
func WithTransport(transport *http.Transport) Option {
// Getter is an interface to support GET to the specified URL. return func(opts *options) {
type Getter interface { opts.transport = transport
// Get file content by url string }
Get(url string, options ...Option) (*bytes.Buffer, error) }
}
// WithAddress sets the Vault address to allow download values.yaml from Vault server.
// Constructor is the function for every getter which creates a specific instance func WithAddress(address string) Option {
// according to the configuration return func(opts *options) {
type Constructor func(options ...Option) (Getter, error) opts.address = address
}
// Provider represents any getter and the schemes that it supports. }
//
// For example, an HTTP provider may provide one getter that handles both // WithToken sets the token to allow authentication to Vault server default.
// 'http' and 'https' schemes. func WithToken(token string) Option {
type Provider struct { return func(o *options) {
Schemes []string o.token = token
New Constructor }
} }
// Provides returns true if the given scheme is supported by this Provider. // Getter is an interface to support GET to the specified URL.
func (p Provider) Provides(scheme string) bool { type Getter interface {
for _, i := range p.Schemes { // Get file content by url string
if i == scheme { Get(url string, options ...Option) (*bytes.Buffer, error)
return true }
}
} // Constructor is the function for every getter which creates a specific instance
return false // according to the configuration
} type Constructor func(options ...Option) (Getter, error)
// Providers is a collection of Provider objects. // Provider represents any getter and the schemes that it supports.
type Providers []Provider //
// For example, an HTTP provider may provide one getter that handles both
// ByScheme returns a Provider that handles the given scheme. // 'http' and 'https' schemes.
// type Provider struct {
// If no provider handles this scheme, this will return an error. Schemes []string
func (p Providers) ByScheme(scheme string) (Getter, error) { New Constructor
for _, pp := range p { }
if pp.Provides(scheme) {
return pp.New() // Provides returns true if the given scheme is supported by this Provider.
} func (p Provider) Provides(scheme string) bool {
} for _, i := range p.Schemes {
return nil, errors.Errorf("scheme %q not supported", scheme) if i == scheme {
} return true
}
const ( }
// The cost timeout references curl's default connection timeout. return false
// https://github.com/curl/curl/blob/master/lib/connect.h#L40C21-L40C21 }
// The helm commands are usually executed manually. Considering the acceptable waiting time, we reduced the entire request time to 120s.
DefaultHTTPTimeout = 120 // Providers is a collection of Provider objects.
) type Providers []Provider
var defaultOptions = []Option{WithTimeout(time.Second * DefaultHTTPTimeout)} // ByScheme returns a Provider that handles the given scheme.
//
var httpProvider = Provider{ // If no provider handles this scheme, this will return an error.
Schemes: []string{"http", "https"}, func (p Providers) ByScheme(scheme string) (Getter, error) {
New: func(options ...Option) (Getter, error) { for _, pp := range p {
options = append(options, defaultOptions...) if pp.Provides(scheme) {
return NewHTTPGetter(options...) return pp.New()
}, }
} }
return nil, errors.Errorf("scheme %q not supported", scheme)
var ociProvider = Provider{ }
Schemes: []string{registry.OCIScheme},
New: NewOCIGetter, const (
} // The cost timeout references curl's default connection timeout.
// https://github.com/curl/curl/blob/master/lib/connect.h#L40C21-L40C21
// All finds all of the registered getters as a list of Provider instances. // The helm commands are usually executed manually. Considering the acceptable waiting time, we reduced the entire request time to 120s.
// Currently, the built-in getters and the discovered plugins with downloader DefaultHTTPTimeout = 120
// notations are collected. )
func All(settings *cli.EnvSettings) Providers {
result := Providers{httpProvider, ociProvider} var defaultOptions = []Option{WithTimeout(time.Second * DefaultHTTPTimeout)}
pluginDownloaders, _ := collectPlugins(settings)
result = append(result, pluginDownloaders...) var httpProvider = Provider{
return result Schemes: []string{"http", "https"},
} New: func(options ...Option) (Getter, error) {
options = append(options, defaultOptions...)
return NewHTTPGetter(options...)
},
}
var ociProvider = Provider{
Schemes: []string{registry.OCIScheme},
New: NewOCIGetter,
}
var vaultProvider = Provider{
Schemes: []string{"vault"}, // Define "vault" as the scheme
New: func(options ...Option) (Getter, error) {
return NewVaultGetter(options...)
},
}
// All finds all of the registered getters as a list of Provider instances.
// Currently, the built-in getters and the discovered plugins with downloader
// notations are collected.
func All(settings *cli.EnvSettings) Providers {
result := Providers{httpProvider, ociProvider, vaultProvider} // Including new vaultProvider as well for Vault integration
pluginDownloaders, _ := collectPlugins(settings)
result = append(result, pluginDownloaders...)
return result
}

@ -0,0 +1,154 @@
/*
Copyright (c) 2024 Rakuten Symphony India.
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 getter
import (
"bytes"
"fmt"
"strings"
"sync"
"github.com/hashicorp/vault/api"
"gopkg.in/yaml.v3"
)
// VaultGetter is the struct that handles retrieving data from Vault.
type VaultGetter struct {
opts options // Options for Vault (address, token, etc.)
client *api.Client // The Vault client
once sync.Once // Ensure the Vault client is initialized only once
}
// Get performs a Get from repo.Getter and returns the body.
func (v *VaultGetter) Get(href string, options ...Option) (*bytes.Buffer, error) {
for _, opt := range options {
opt(&v.opts)
}
return v.get(href)
}
func (v *VaultGetter) get(href string) (*bytes.Buffer, error) {
// Initialize the Vault client
client, err := v.vaultClient()
if err != nil {
return nil, err
}
// Fetch the values from Vault using the Vault client
valPath := strings.TrimPrefix(href, "vault://")
val, err := client.Logical().Read(valPath)
if err != nil {
return nil, fmt.Errorf("failed to fetch values from Vault: %v", err)
}
// Ensure the values contains data
if val == nil || val.Data == nil {
return nil, fmt.Errorf("no data found at Vault path: %s", valPath)
}
// Retrieve the data (assumed to be a string in the Vault response)
data, ok := val.Data["data"].(map[string]interface{})
if !ok {
return nil, fmt.Errorf("unexpected data format at Vault path: %s", href)
}
// Unwrap the "values" key if it exists
if values, ok := data["values"].(string); ok {
data = make(map[string]interface{})
if err := yaml.Unmarshal([]byte(values), &data); err != nil {
return nil, fmt.Errorf("failed to unmarshal values: %v", err)
}
}
// Check if the data is in properties format
if properties, ok := data["properties"].(string); ok {
data = make(map[string]interface{})
for _, line := range strings.Split(properties, "\n") {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "#") {
continue
}
parts := strings.SplitN(line, "=", 2)
if len(parts) != 2 {
return nil, fmt.Errorf("invalid properties line: %s", line)
}
key := strings.TrimSpace(parts[0])
value := strings.TrimSpace(parts[1])
data[key] = value
}
}
// Return the data in a byte buffer
buf := bytes.NewBuffer(nil)
if err = yaml.NewEncoder(buf).Encode(data); err != nil {
return nil, fmt.Errorf("failed to encode data to YAML: %v", err)
}
return buf, nil
}
// NewVaultGetter creates a new instance of VaultGetter.
func NewVaultGetter(options ...Option) (Getter, error) {
var v VaultGetter
for _, opt := range options {
opt(&v.opts)
}
return &v, nil
}
func (v *VaultGetter) vaultClient() (*api.Client, error) {
if v.client != nil {
return v.client, nil
}
var config *api.Config
// Use sync.Once to initialize the Vault client only once
v.once.Do(func() {
config = &api.Config{
Address: v.opts.address, // Vault URL is set from options
}
})
// Configure TLS if needed
if v.opts.caFile != "" || v.opts.insecureSkipVerifyTLS {
tlsConfig := &api.TLSConfig{
CACert: v.opts.caFile,
Insecure: v.opts.insecureSkipVerifyTLS,
ClientCert: v.opts.certFile,
ClientKey: v.opts.keyFile,
}
err := config.ConfigureTLS(tlsConfig)
if err != nil {
return nil, fmt.Errorf("failed to configure TLS for Vault client: %v", err)
}
}
// Initialize the Vault client
client, err := api.NewClient(config)
if err != nil {
return nil, fmt.Errorf("failed to create Vault client: %v", err)
}
// Set the token for authentication
client.SetToken(v.opts.token)
v.client = client
return v.client, nil
}
Loading…
Cancel
Save