mirror of
https://github.com/helm/helm.git
synced 2026-08-12 12:14:45 +00:00
fix(helm): fix race conditions in flag parsing
* fix a number of issues with flag parsing * add support for `HELM_DEBUG` * lazy expand flag default envars
This commit is contained in:
@@ -87,7 +87,7 @@ func TestCreateStarterCmd(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
old := helmpath.Home(environment.DefaultHelmHome())
|
||||
old := helmpath.Home(environment.DefaultHelmHome)
|
||||
settings.Home = thome
|
||||
defer func() {
|
||||
settings.Home = old
|
||||
|
||||
@@ -52,12 +52,12 @@ func newDeleteCmd(c helm.Interface, out io.Writer) *cobra.Command {
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "delete [flags] RELEASE_NAME [...]",
|
||||
Aliases: []string{"del"},
|
||||
SuggestFor: []string{"remove", "rm"},
|
||||
Short: "given a release name, delete the release from Kubernetes",
|
||||
Long: deleteDesc,
|
||||
PersistentPreRunE: setupConnection,
|
||||
Use: "delete [flags] RELEASE_NAME [...]",
|
||||
Aliases: []string{"del"},
|
||||
SuggestFor: []string{"remove", "rm"},
|
||||
Short: "given a release name, delete the release from Kubernetes",
|
||||
Long: deleteDesc,
|
||||
PreRunE: setupConnection,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) == 0 {
|
||||
return errors.New("command 'delete' requires a release name")
|
||||
|
||||
@@ -34,7 +34,7 @@ func TestFetchCmd(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
old := helmpath.Home(environment.DefaultHelmHome())
|
||||
old := helmpath.Home(environment.DefaultHelmHome)
|
||||
settings.Home = hh
|
||||
defer func() {
|
||||
settings.Home = old
|
||||
|
||||
@@ -54,10 +54,10 @@ func newGetCmd(client helm.Interface, out io.Writer) *cobra.Command {
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "get [flags] RELEASE_NAME",
|
||||
Short: "download a named release",
|
||||
Long: getHelp,
|
||||
PersistentPreRunE: setupConnection,
|
||||
Use: "get [flags] RELEASE_NAME",
|
||||
Short: "download a named release",
|
||||
Long: getHelp,
|
||||
PreRunE: setupConnection,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) == 0 {
|
||||
return errReleaseRequired
|
||||
|
||||
@@ -39,10 +39,6 @@ import (
|
||||
"k8s.io/helm/pkg/tlsutil"
|
||||
)
|
||||
|
||||
const (
|
||||
localRepoIndexFilePath = "index.yaml"
|
||||
)
|
||||
|
||||
var (
|
||||
tlsCaCertFile string // path to TLS CA certificate file
|
||||
tlsCertFile string // path to TLS certificate file
|
||||
@@ -52,10 +48,9 @@ var (
|
||||
)
|
||||
|
||||
var (
|
||||
kubeContext string
|
||||
settings helm_env.EnvSettings
|
||||
// TODO refactor out this global var
|
||||
kubeContext string
|
||||
tillerTunnel *kube.Tunnel
|
||||
settings helm_env.EnvSettings
|
||||
)
|
||||
|
||||
var globalUsage = `The Kubernetes package manager
|
||||
@@ -82,34 +77,57 @@ Environment:
|
||||
$KUBECONFIG set an alternative Kubernetes configuration file (default "~/.kube/config")
|
||||
`
|
||||
|
||||
func setFlagFromEnv(name, envar string, cmd *cobra.Command) {
|
||||
if cmd.Flags().Changed(name) {
|
||||
return
|
||||
}
|
||||
if v, ok := os.LookupEnv(envar); ok {
|
||||
cmd.Flags().Set(name, v)
|
||||
}
|
||||
}
|
||||
|
||||
func setFlagsFromEnv(flags map[string]string, cmd *cobra.Command) {
|
||||
for name, envar := range flags {
|
||||
setFlagFromEnv(name, envar, cmd)
|
||||
}
|
||||
}
|
||||
|
||||
func addRootFlags(cmd *cobra.Command) {
|
||||
pf := cmd.PersistentFlags()
|
||||
pf.StringVar((*string)(&settings.Home), "home", helm_env.DefaultHelmHome, "location of your Helm config. Overrides $HELM_HOME")
|
||||
pf.StringVar(&settings.TillerHost, "host", "", "address of tiller. Overrides $HELM_HOST")
|
||||
pf.StringVar(&kubeContext, "kube-context", "", "name of the kubeconfig context to use")
|
||||
pf.BoolVar(&settings.Debug, "debug", false, "enable verbose output")
|
||||
pf.StringVar(&settings.TillerNamespace, "tiller-namespace", tiller_env.DefaultTillerNamespace, "namespace of tiller")
|
||||
}
|
||||
|
||||
func initRootFlags(cmd *cobra.Command) {
|
||||
setFlagsFromEnv(map[string]string{
|
||||
"debug": helm_env.DebugEnvVar,
|
||||
"home": helm_env.HomeEnvVar,
|
||||
"host": helm_env.HostEnvVar,
|
||||
"tiller-namespace": tiller_env.TillerNamespaceEnvVar,
|
||||
}, cmd.Root())
|
||||
|
||||
tlsCaCertFile = os.ExpandEnv(tlsCaCertFile)
|
||||
tlsCertFile = os.ExpandEnv(tlsCertFile)
|
||||
tlsKeyFile = os.ExpandEnv(tlsKeyFile)
|
||||
}
|
||||
|
||||
func newRootCmd(out io.Writer) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "helm",
|
||||
Short: "The Helm package manager for Kubernetes.",
|
||||
Long: globalUsage,
|
||||
SilenceUsage: true,
|
||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||
tlsCaCertFile = os.ExpandEnv(tlsCaCertFile)
|
||||
tlsCertFile = os.ExpandEnv(tlsCertFile)
|
||||
tlsKeyFile = os.ExpandEnv(tlsKeyFile)
|
||||
PersistentPreRun: func(cmd *cobra.Command, _ []string) {
|
||||
initRootFlags(cmd)
|
||||
},
|
||||
PersistentPostRun: func(cmd *cobra.Command, args []string) {
|
||||
PersistentPostRun: func(_ *cobra.Command, _ []string) {
|
||||
teardown()
|
||||
},
|
||||
}
|
||||
p := cmd.PersistentFlags()
|
||||
p.StringVar((*string)(&settings.Home), "home", helm_env.DefaultHelmHome(), "location of your Helm config. Overrides $HELM_HOME")
|
||||
p.StringVar(&settings.TillerHost, "host", helm_env.DefaultHelmHost(), "address of tiller. Overrides $HELM_HOST")
|
||||
p.StringVar(&kubeContext, "kube-context", "", "name of the kubeconfig context to use")
|
||||
p.BoolVar(&settings.Debug, "debug", false, "enable verbose output")
|
||||
p.StringVar(&settings.TillerNamespace, "tiller-namespace", tiller_env.GetTillerNamespace(), "namespace of tiller")
|
||||
|
||||
if os.Getenv(helm_env.PluginDisableEnvVar) != "1" {
|
||||
settings.PlugDirs = os.Getenv(helm_env.PluginEnvVar)
|
||||
if settings.PlugDirs == "" {
|
||||
settings.PlugDirs = settings.Home.Plugins()
|
||||
}
|
||||
}
|
||||
addRootFlags(cmd)
|
||||
|
||||
cmd.AddCommand(
|
||||
// chart commands
|
||||
|
||||
@@ -321,7 +321,7 @@ func ensureTestHome(home helmpath.Home, t *testing.T) error {
|
||||
}
|
||||
}
|
||||
|
||||
localRepoIndexFile := home.LocalRepository(localRepoIndexFilePath)
|
||||
localRepoIndexFile := home.LocalRepository(localRepositoryIndexFile)
|
||||
if fi, err := os.Stat(localRepoIndexFile); err != nil {
|
||||
i := repo.NewIndexFile()
|
||||
if err := i.WriteFile(localRepoIndexFile, 0644); err != nil {
|
||||
|
||||
@@ -56,11 +56,11 @@ func newHistoryCmd(c helm.Interface, w io.Writer) *cobra.Command {
|
||||
his := &historyCmd{out: w, helmc: c}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "history [flags] RELEASE_NAME",
|
||||
Long: historyHelp,
|
||||
Short: "fetch release history",
|
||||
Aliases: []string{"hist"},
|
||||
PersistentPreRunE: setupConnection,
|
||||
Use: "history [flags] RELEASE_NAME",
|
||||
Long: historyHelp,
|
||||
Short: "fetch release history",
|
||||
Aliases: []string{"hist"},
|
||||
PreRunE: setupConnection,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
switch {
|
||||
case len(args) == 0:
|
||||
|
||||
@@ -54,8 +54,9 @@ To dump a manifest containing the Tiller deployment YAML, combine the
|
||||
`
|
||||
|
||||
const (
|
||||
stableRepository = "stable"
|
||||
localRepository = "local"
|
||||
stableRepository = "stable"
|
||||
localRepository = "local"
|
||||
localRepositoryIndexFile = "index.yaml"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -296,7 +297,7 @@ func ensureDefaultRepos(home helmpath.Home, out io.Writer, skipRefresh bool) err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lr, err := initLocalRepo(home.LocalRepository(localRepoIndexFilePath), home.CacheIndex("local"))
|
||||
lr, err := initLocalRepo(home.LocalRepository(localRepositoryIndexFile), home.CacheIndex("local"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -214,7 +214,7 @@ func TestEnsureHome(t *testing.T) {
|
||||
t.Errorf("%s should not be a directory", fi)
|
||||
}
|
||||
|
||||
if fi, err := os.Stat(hh.LocalRepository(localRepoIndexFilePath)); err != nil {
|
||||
if fi, err := os.Stat(hh.LocalRepository(localRepositoryIndexFile)); err != nil {
|
||||
t.Errorf("%s", err)
|
||||
} else if fi.IsDir() {
|
||||
t.Errorf("%s should not be a directory", fi)
|
||||
|
||||
@@ -148,10 +148,10 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command {
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "install [CHART]",
|
||||
Short: "install a chart archive",
|
||||
Long: installDesc,
|
||||
PersistentPreRunE: setupConnection,
|
||||
Use: "install [CHART]",
|
||||
Short: "install a chart archive",
|
||||
Long: installDesc,
|
||||
PreRunE: setupConnection,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := checkArgsLength(len(args), "chart name"); err != nil {
|
||||
return err
|
||||
|
||||
@@ -82,11 +82,11 @@ func newListCmd(client helm.Interface, out io.Writer) *cobra.Command {
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "list [flags] [FILTER]",
|
||||
Short: "list releases",
|
||||
Long: listHelp,
|
||||
Aliases: []string{"ls"},
|
||||
PersistentPreRunE: setupConnection,
|
||||
Use: "list [flags] [FILTER]",
|
||||
Short: "list releases",
|
||||
Long: listHelp,
|
||||
Aliases: []string{"ls"},
|
||||
PreRunE: setupConnection,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) > 0 {
|
||||
list.filter = strings.Join(args, " ")
|
||||
|
||||
@@ -25,19 +25,10 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
helm_env "k8s.io/helm/pkg/helm/environment"
|
||||
"k8s.io/helm/pkg/plugin"
|
||||
)
|
||||
|
||||
const pluginEnvVar = "HELM_PLUGIN"
|
||||
|
||||
func pluginDirs(home helmpath.Home) string {
|
||||
if dirs := os.Getenv(pluginEnvVar); dirs != "" {
|
||||
return dirs
|
||||
}
|
||||
return home.Plugins()
|
||||
}
|
||||
|
||||
// loadPlugins loads plugins into the command list.
|
||||
//
|
||||
// This follows a different pattern than the other commands because it has
|
||||
@@ -46,16 +37,25 @@ func pluginDirs(home helmpath.Home) string {
|
||||
func loadPlugins(baseCmd *cobra.Command, out io.Writer) {
|
||||
|
||||
// If HELM_NO_PLUGINS is set to 1, do not load plugins.
|
||||
if settings.PlugDirs == "" {
|
||||
if os.Getenv(helm_env.PluginDisableEnvVar) == "1" {
|
||||
return
|
||||
}
|
||||
|
||||
found, err := findPlugins(settings.PlugDirs)
|
||||
found, err := findPlugins(settings.PluginDirs())
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed to load plugins: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
processParent := func(cmd *cobra.Command, args []string) ([]string, error) {
|
||||
k, u := manuallyProcessArgs(args)
|
||||
if err := cmd.Parent().ParseFlags(k); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
initRootFlags(cmd)
|
||||
return u, nil
|
||||
}
|
||||
|
||||
// Now we create commands for all of these.
|
||||
for _, plug := range found {
|
||||
plug := plug
|
||||
@@ -69,9 +69,8 @@ func loadPlugins(baseCmd *cobra.Command, out io.Writer) {
|
||||
Short: md.Usage,
|
||||
Long: md.Description,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
k, u := manuallyProcessArgs(args)
|
||||
if err := cmd.Parent().ParseFlags(k); err != nil {
|
||||
u, err := processParent(cmd, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -99,10 +98,9 @@ func loadPlugins(baseCmd *cobra.Command, out io.Writer) {
|
||||
}
|
||||
|
||||
if md.UseTunnel {
|
||||
c.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
|
||||
c.PreRunE = func(cmd *cobra.Command, args []string) error {
|
||||
// Parse the parent flag, but not the local flags.
|
||||
k, _ := manuallyProcessArgs(args)
|
||||
if err := c.Parent().ParseFlags(k); err != nil {
|
||||
if _, err := processParent(cmd, args); err != nil {
|
||||
return err
|
||||
}
|
||||
return setupConnection(cmd, args)
|
||||
|
||||
@@ -44,11 +44,8 @@ func newPluginListCmd(out io.Writer) *cobra.Command {
|
||||
}
|
||||
|
||||
func (pcmd *pluginListCmd) run() error {
|
||||
plugdirs := pluginDirs(pcmd.home)
|
||||
|
||||
debug("pluginDirs: %s", plugdirs)
|
||||
|
||||
plugins, err := findPlugins(plugdirs)
|
||||
debug("pluginDirs: %s", settings.PluginDirs())
|
||||
plugins, err := findPlugins(settings.PluginDirs())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -59,9 +59,8 @@ func (pcmd *pluginRemoveCmd) complete(args []string) error {
|
||||
}
|
||||
|
||||
func (pcmd *pluginRemoveCmd) run() error {
|
||||
plugdirs := pluginDirs(pcmd.home)
|
||||
debug("loading installed plugins from %s", plugdirs)
|
||||
plugins, err := findPlugins(plugdirs)
|
||||
debug("loading installed plugins from %s", settings.PluginDirs())
|
||||
plugins, err := findPlugins(settings.PluginDirs())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
helm_env "k8s.io/helm/pkg/helm/environment"
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/plugin"
|
||||
|
||||
@@ -71,7 +72,6 @@ func TestLoadPlugins(t *testing.T) {
|
||||
settings.Home = old
|
||||
}()
|
||||
hh := settings.Home
|
||||
settings.PlugDirs = hh.Plugins()
|
||||
|
||||
out := bytes.NewBuffer(nil)
|
||||
cmd := &cobra.Command{}
|
||||
@@ -139,12 +139,11 @@ func TestLoadPlugins(t *testing.T) {
|
||||
func TestLoadPlugins_HelmNoPlugins(t *testing.T) {
|
||||
// Set helm home to point to testdata
|
||||
old := settings.Home
|
||||
oldPlugDirs := settings.PlugDirs
|
||||
settings.Home = "testdata/helmhome"
|
||||
settings.PlugDirs = ""
|
||||
os.Setenv(helm_env.PluginDisableEnvVar, "1")
|
||||
defer func() {
|
||||
settings.Home = old
|
||||
settings.PlugDirs = oldPlugDirs
|
||||
os.Unsetenv(helm_env.PluginDisableEnvVar)
|
||||
}()
|
||||
|
||||
out := bytes.NewBuffer(nil)
|
||||
@@ -161,7 +160,6 @@ func TestSetupEnv(t *testing.T) {
|
||||
name := "pequod"
|
||||
settings.Home = helmpath.Home("testdata/helmhome")
|
||||
base := filepath.Join(settings.Home.Plugins(), name)
|
||||
settings.PlugDirs = settings.Home.Plugins()
|
||||
settings.Debug = true
|
||||
defer func() {
|
||||
settings.Debug = false
|
||||
|
||||
@@ -61,9 +61,8 @@ func (pcmd *pluginUpdateCmd) complete(args []string) error {
|
||||
|
||||
func (pcmd *pluginUpdateCmd) run() error {
|
||||
installer.Debug = settings.Debug
|
||||
plugdirs := pluginDirs(pcmd.home)
|
||||
debug("loading installed plugins from %s", plugdirs)
|
||||
plugins, err := findPlugins(plugdirs)
|
||||
debug("loading installed plugins from %s", settings.PluginDirs())
|
||||
plugins, err := findPlugins(settings.PluginDirs())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -48,10 +48,10 @@ func newReleaseTestCmd(c helm.Interface, out io.Writer) *cobra.Command {
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "test [RELEASE]",
|
||||
Short: "test a release",
|
||||
Long: releaseTestDesc,
|
||||
PersistentPreRunE: setupConnection,
|
||||
Use: "test [RELEASE]",
|
||||
Short: "test a release",
|
||||
Long: releaseTestDesc,
|
||||
PreRunE: setupConnection,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := checkArgsLength(len(args), "release name"); err != nil {
|
||||
return err
|
||||
|
||||
@@ -57,7 +57,7 @@ func newResetCmd(client helm.Interface, out io.Writer) *cobra.Command {
|
||||
Use: "reset",
|
||||
Short: "uninstalls Tiller from a cluster",
|
||||
Long: resetDesc,
|
||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := setupConnection(cmd, args); !d.force && err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ const rollbackDesc = `
|
||||
This command rolls back a release to a previous revision.
|
||||
|
||||
The first argument of the rollback command is the name of a release, and the
|
||||
second is a revision (version) number. To see revision numbers, run
|
||||
second is a revision (version) number. To see revision numbers, run
|
||||
'helm history RELEASE'.
|
||||
`
|
||||
|
||||
@@ -54,10 +54,10 @@ func newRollbackCmd(c helm.Interface, out io.Writer) *cobra.Command {
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "rollback [flags] [RELEASE] [REVISION]",
|
||||
Short: "roll back a release to a previous revision",
|
||||
Long: rollbackDesc,
|
||||
PersistentPreRunE: setupConnection,
|
||||
Use: "rollback [flags] [RELEASE] [REVISION]",
|
||||
Short: "roll back a release to a previous revision",
|
||||
Long: rollbackDesc,
|
||||
PreRunE: setupConnection,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := checkArgsLength(len(args), "release name", "revision number"); err != nil {
|
||||
return err
|
||||
|
||||
@@ -57,10 +57,10 @@ func newStatusCmd(client helm.Interface, out io.Writer) *cobra.Command {
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "status [flags] RELEASE_NAME",
|
||||
Short: "displays the status of the named release",
|
||||
Long: statusHelp,
|
||||
PersistentPreRunE: setupConnection,
|
||||
Use: "status [flags] RELEASE_NAME",
|
||||
Short: "displays the status of the named release",
|
||||
Long: statusHelp,
|
||||
PreRunE: setupConnection,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) == 0 {
|
||||
return errReleaseRequired
|
||||
|
||||
@@ -91,10 +91,10 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command {
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "upgrade [RELEASE] [CHART]",
|
||||
Short: "upgrade a release",
|
||||
Long: upgradeDesc,
|
||||
PersistentPreRunE: setupConnection,
|
||||
Use: "upgrade [RELEASE] [CHART]",
|
||||
Short: "upgrade a release",
|
||||
Long: upgradeDesc,
|
||||
PreRunE: setupConnection,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := checkArgsLength(len(args), "release name", "chart path"); err != nil {
|
||||
return err
|
||||
|
||||
Reference in New Issue
Block a user