2019-09-25 14:20:47 -04:00
/ *
Copyright The Helm Authors .
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
2025-02-24 10:11:54 -05:00
package cmd
2019-09-25 14:20:47 -04:00
import (
2020-05-06 16:43:17 -04:00
"flag"
2019-09-25 14:20:47 -04:00
"fmt"
2020-04-11 14:06:56 -04:00
"log"
2025-04-10 09:06:03 -04:00
"log/slog"
2020-06-12 08:24:55 -04:00
"path/filepath"
2020-11-14 11:50:06 -05:00
"sort"
2019-10-10 15:49:18 -04:00
"strings"
2019-09-25 14:20:47 -04:00
"github.com/spf13/cobra"
"github.com/spf13/pflag"
2025-08-22 16:12:49 -04:00
2020-12-08 10:56:57 -05:00
"k8s.io/klog/v2"
2019-09-25 14:20:47 -04:00
2024-12-26 16:33:51 -05:00
"helm.sh/helm/v4/pkg/action"
2025-08-20 17:17:16 -04:00
"helm.sh/helm/v4/pkg/cli"
2024-12-26 16:33:51 -05:00
"helm.sh/helm/v4/pkg/cli/output"
"helm.sh/helm/v4/pkg/cli/values"
"helm.sh/helm/v4/pkg/helmpath"
2025-02-16 15:38:28 -05:00
"helm.sh/helm/v4/pkg/kube"
2025-08-20 17:17:16 -04:00
"helm.sh/helm/v4/pkg/postrenderer"
2025-08-31 09:04:48 -04:00
"helm.sh/helm/v4/pkg/repo/v1"
2019-09-25 14:20:47 -04:00
)
2021-12-31 04:35:14 -05:00
const (
outputFlag = "output"
postRenderFlag = "post-renderer"
postRenderArgsFlag = "post-renderer-args"
)
2019-09-25 14:20:47 -04:00
func addValueOptionsFlags ( f * pflag . FlagSet , v * values . Options ) {
2020-01-02 10:32:50 -05:00
f . StringSliceVarP ( & v . ValueFiles , "values" , "f" , [ ] string { } , "specify values in a YAML file or a URL (can specify multiple)" )
2019-09-25 14:20:47 -04:00
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 . 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 . 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)" )
2025-02-07 21:04:14 -05:00
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 or using json format: {\"key1\": jsonval1, \"key2\": \"jsonval2\"})" )
2021-01-31 10:03:39 -05:00
f . StringArrayVar ( & v . LiteralValues , "set-literal" , [ ] string { } , "set a literal STRING value on the command line" )
2019-09-25 14:20:47 -04:00
}
2025-02-16 15:38:28 -05:00
func AddWaitFlag ( cmd * cobra . Command , wait * kube . WaitStrategy ) {
cmd . Flags ( ) . Var (
2025-02-16 16:10:06 -05:00
newWaitValue ( kube . HookOnlyStrategy , wait ) ,
2025-02-16 15:38:28 -05:00
"wait" ,
2026-01-14 07:11:59 -05:00
"wait until resources are ready (up to --timeout). Use '--wait' alone for 'watcher' strategy, or specify one of: 'watcher', 'hookOnly', 'legacy'. Default when flag is omitted: 'hookOnly'." ,
2025-02-16 15:38:28 -05:00
)
cmd . Flags ( ) . Lookup ( "wait" ) . NoOptDefVal = string ( kube . StatusWatcherStrategy )
}
type waitValue kube . WaitStrategy
2025-02-16 16:10:06 -05:00
func newWaitValue ( defaultValue kube . WaitStrategy , ws * kube . WaitStrategy ) * waitValue {
* ws = defaultValue
2025-02-16 15:38:28 -05:00
return ( * waitValue ) ( ws )
}
func ( ws * waitValue ) String ( ) string {
if ws == nil {
return ""
}
return string ( * ws )
}
func ( ws * waitValue ) Set ( s string ) error {
switch s {
2025-10-25 07:25:22 -04:00
case string ( kube . StatusWatcherStrategy ) , string ( kube . LegacyStrategy ) , string ( kube . HookOnlyStrategy ) :
2025-02-16 15:38:28 -05:00
* ws = waitValue ( s )
return nil
case "true" :
2025-04-10 09:06:03 -04:00
slog . Warn ( "--wait=true is deprecated (boolean value) and can be replaced with --wait=watcher" )
2025-02-16 15:38:28 -05:00
* ws = waitValue ( kube . StatusWatcherStrategy )
return nil
case "false" :
2025-10-25 07:27:35 -04:00
slog . Warn ( "--wait=false is deprecated (boolean value) and can be replaced with --wait=hookOnly" )
2025-03-03 14:56:00 -05:00
* ws = waitValue ( kube . HookOnlyStrategy )
2025-02-16 15:38:28 -05:00
return nil
default :
2025-10-27 10:39:13 -04:00
return fmt . Errorf ( "invalid wait input %q. Valid inputs are %s, %s, and %s" , s , kube . StatusWatcherStrategy , kube . HookOnlyStrategy , kube . LegacyStrategy )
2025-02-16 15:38:28 -05:00
}
}
func ( ws * waitValue ) Type ( ) string {
return "WaitStrategy"
}
2019-09-25 14:20:47 -04:00
func addChartPathOptionsFlags ( f * pflag . FlagSet , c * action . ChartPathOptions ) {
2021-03-17 13:38:27 -04:00
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" )
2020-05-29 14:58:52 -04:00
f . BoolVar ( & c . Verify , "verify" , false , "verify the package before using it" )
2019-09-25 14:20:47 -04:00
f . StringVar ( & c . Keyring , "keyring" , defaultKeyring ( ) , "location of public keys used for verification" )
f . StringVar ( & c . RepoURL , "repo" , "" , "chart repository url where to locate the requested chart" )
f . StringVar ( & c . Username , "username" , "" , "chart repository username where to locate the requested chart" )
f . StringVar ( & c . Password , "password" , "" , "chart repository password where to locate the requested chart" )
f . StringVar ( & c . CertFile , "cert-file" , "" , "identify HTTPS client using this SSL certificate file" )
f . StringVar ( & c . KeyFile , "key-file" , "" , "identify HTTPS client using this SSL key file" )
2025-11-03 22:33:42 -05:00
f . BoolVar ( & c . InsecureSkipTLSVerify , "insecure-skip-tls-verify" , false , "skip tls certificate checks for the chart download" )
2023-06-07 02:24:02 -04:00
f . BoolVar ( & c . PlainHTTP , "plain-http" , false , "use insecure HTTP connections for the chart download" )
2019-09-25 14:20:47 -04:00
f . StringVar ( & c . CaFile , "ca-file" , "" , "verify certificates of HTTPS-enabled servers using this CA bundle" )
2021-06-11 14:36:55 -04:00
f . BoolVar ( & c . PassCredentialsAll , "pass-credentials" , false , "pass credentials to all domains" )
2019-09-25 14:20:47 -04:00
}
// bindOutputFlag will add the output flag to the given command and bind the
2019-10-07 13:17:22 -04:00
// value to the given format pointer
func bindOutputFlag ( cmd * cobra . Command , varRef * output . Format ) {
2020-04-11 14:06:56 -04:00
cmd . Flags ( ) . VarP ( newOutputValue ( output . Table , varRef ) , outputFlag , "o" ,
2019-10-10 22:39:18 -04:00
fmt . Sprintf ( "prints the output in the specified format. Allowed values: %s" , strings . Join ( output . Formats ( ) , ", " ) ) )
2019-12-31 08:54:20 -05:00
2024-03-11 17:13:34 -04:00
err := cmd . RegisterFlagCompletionFunc ( outputFlag , func ( _ * cobra . Command , _ [ ] string , _ string ) ( [ ] string , cobra . ShellCompDirective ) {
2019-12-31 08:54:20 -05:00
var formatNames [ ] string
2020-11-14 11:50:06 -05:00
for format , desc := range output . FormatsWithDesc ( ) {
2021-12-31 10:49:49 -05:00
formatNames = append ( formatNames , fmt . Sprintf ( "%s\t%s" , format , desc ) )
2019-12-31 08:54:20 -05:00
}
2020-11-14 11:50:06 -05:00
// Sort the results to get a deterministic order for the tests
sort . Strings ( formatNames )
2020-09-14 07:36:41 -04:00
return formatNames , cobra . ShellCompDirectiveNoFileComp
2019-12-31 08:54:20 -05:00
} )
2020-04-11 14:06:56 -04:00
if err != nil {
log . Fatal ( err )
}
2019-10-07 13:17:22 -04:00
}
type outputValue output . Format
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
// output.Format
return string ( * o )
}
func ( o * outputValue ) Type ( ) string {
return "format"
}
func ( o * outputValue ) Set ( s string ) error {
outfmt , err := output . ParseFormat ( s )
if err != nil {
return err
}
* o = outputValue ( outfmt )
return nil
2019-09-25 14:20:47 -04:00
}
2019-09-23 13:29:24 -04:00
2025-08-20 17:17:16 -04:00
// TODO there is probably a better way to pass cobra settings than as a param
func bindPostRenderFlag ( cmd * cobra . Command , varRef * postrenderer . PostRenderer , settings * cli . EnvSettings ) {
p := & postRendererOptions { varRef , "" , [ ] string { } , settings }
cmd . Flags ( ) . Var ( & postRendererString { p } , postRenderFlag , "the name of a postrenderer type plugin to be used for post rendering. If it exists, the plugin will be used" )
2022-01-21 22:45:15 -05:00
cmd . Flags ( ) . Var ( & postRendererArgsSlice { p } , postRenderArgsFlag , "an argument to the post-renderer (can specify multiple)" )
2021-12-31 04:35:14 -05:00
}
type postRendererOptions struct {
2025-08-20 17:17:16 -04:00
renderer * postrenderer . PostRenderer
pluginName string
2021-12-31 04:35:14 -05:00
args [ ] string
2025-08-20 17:17:16 -04:00
settings * cli . EnvSettings
2019-09-23 13:29:24 -04:00
}
2022-01-21 07:41:04 -05:00
type postRendererString struct {
2021-12-31 04:35:14 -05:00
options * postRendererOptions
2019-09-23 13:29:24 -04:00
}
2022-01-21 07:41:04 -05:00
func ( p * postRendererString ) String ( ) string {
2025-08-20 17:17:16 -04:00
return p . options . pluginName
2019-09-23 13:29:24 -04:00
}
2022-01-21 07:41:04 -05:00
func ( p * postRendererString ) Type ( ) string {
return "postRendererString"
2019-09-23 13:29:24 -04:00
}
2022-01-21 07:41:04 -05:00
func ( p * postRendererString ) Set ( val string ) error {
if val == "" {
2019-09-23 13:29:24 -04:00
return nil
}
2025-08-20 17:17:16 -04:00
if p . options . pluginName != "" {
2025-02-24 00:23:18 -05:00
return fmt . Errorf ( "cannot specify --post-renderer flag more than once" )
2025-02-22 07:25:26 -05:00
}
2025-08-20 17:17:16 -04:00
p . options . pluginName = val
pr , err := postrenderer . NewPostRendererPlugin ( p . options . settings , p . options . pluginName , p . options . args ... )
2019-09-23 13:29:24 -04:00
if err != nil {
return err
}
2021-12-31 04:35:14 -05:00
* p . options . renderer = pr
2019-09-23 13:29:24 -04:00
return nil
}
2020-06-12 08:24:55 -04:00
2022-01-21 07:41:04 -05:00
type postRendererArgsSlice struct {
2021-12-31 04:35:14 -05:00
options * postRendererOptions
}
2022-01-21 07:41:04 -05:00
func ( p * postRendererArgsSlice ) String ( ) string {
return "[" + strings . Join ( p . options . args , "," ) + "]"
2021-12-31 04:35:14 -05:00
}
2022-01-21 07:41:04 -05:00
func ( p * postRendererArgsSlice ) Type ( ) string {
return "postRendererArgsSlice"
2021-12-31 04:35:14 -05:00
}
2022-01-21 07:41:04 -05:00
func ( p * postRendererArgsSlice ) Set ( val string ) error {
2022-01-21 22:45:15 -05:00
2022-01-22 23:25:41 -05:00
// a post-renderer defined by a user may accept empty arguments
2022-01-21 07:41:04 -05:00
p . options . args = append ( p . options . args , val )
2022-01-21 22:45:15 -05:00
2025-08-20 17:17:16 -04:00
if p . options . pluginName == "" {
2022-01-21 22:45:15 -05:00
return nil
}
2022-01-21 07:41:04 -05:00
// overwrite if already create PostRenderer by `post-renderer` flags
2025-08-20 17:17:16 -04:00
pr , err := postrenderer . NewPostRendererPlugin ( p . options . settings , p . options . pluginName , p . options . args ... )
2021-12-31 04:35:14 -05:00
if err != nil {
return err
}
* p . options . renderer = pr
2019-09-23 13:29:24 -04:00
return nil
}
2020-06-12 08:24:55 -04:00
2022-01-21 07:41:04 -05:00
func ( p * postRendererArgsSlice ) Append ( val string ) error {
p . options . args = append ( p . options . args , val )
return nil
}
func ( p * postRendererArgsSlice ) Replace ( val [ ] string ) error {
p . options . args = val
return nil
}
func ( p * postRendererArgsSlice ) GetSlice ( ) [ ] string {
return p . options . args
}
2024-01-05 16:35:24 -05:00
func compVersionFlag ( chartRef string , _ string ) ( [ ] string , cobra . ShellCompDirective ) {
2020-06-12 08:24:55 -04:00
chartInfo := strings . Split ( chartRef , "/" )
if len ( chartInfo ) != 2 {
return nil , cobra . ShellCompDirectiveNoFileComp
}
repoName := chartInfo [ 0 ]
chartName := chartInfo [ 1 ]
path := filepath . Join ( settings . RepositoryCache , helmpath . CacheIndexFile ( repoName ) )
var versions [ ] string
if indexFile , err := repo . LoadIndexFile ( path ) ; err == nil {
for _ , details := range indexFile . Entries [ chartName ] {
2025-04-14 04:39:09 -04:00
appVersion := details . AppVersion
2021-12-31 10:49:49 -05:00
appVersionDesc := ""
if appVersion != "" {
appVersionDesc = fmt . Sprintf ( "App: %s, " , appVersion )
}
created := details . Created . Format ( "January 2, 2006" )
createdDesc := ""
if created != "" {
createdDesc = fmt . Sprintf ( "Created: %s " , created )
}
deprecated := ""
2025-04-14 04:39:09 -04:00
if details . Deprecated {
2021-12-31 10:49:49 -05:00
deprecated = "(deprecated)"
2020-06-12 08:24:55 -04:00
}
2025-04-14 04:39:09 -04:00
versions = append ( versions , fmt . Sprintf ( "%s\t%s%s%s" , details . Version , appVersionDesc , createdDesc , deprecated ) )
2020-06-12 08:24:55 -04:00
}
}
return versions , cobra . ShellCompDirectiveNoFileComp
}
2020-05-06 16:43:17 -04:00
// addKlogFlags adds flags from k8s.io/klog
// marks the flags as hidden to avoid polluting the help text
func addKlogFlags ( fs * pflag . FlagSet ) {
local := flag . NewFlagSet ( "klog" , flag . ExitOnError )
klog . InitFlags ( local )
local . VisitAll ( func ( fl * flag . Flag ) {
fl . Name = normalize ( fl . Name )
if fs . Lookup ( fl . Name ) != nil {
return
}
newflag := pflag . PFlagFromGoFlag ( fl )
newflag . Hidden = true
fs . AddFlag ( newflag )
} )
}
// normalize replaces underscores with hyphens
func normalize ( s string ) string {
return strings . ReplaceAll ( s , "_" , "-" )
}