mirror of https://github.com/helm/helm
Signed-off-by: Vineet Aggarwal <vineet.aggarwal@rakuten.com>pull/13491/head
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
|
||||||
|
}
|
||||||
|
@ -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, ¤tMap); 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, ¤tMap); 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…
Reference in new issue