ref(*): refactor flag and environment settings

Signed-off-by: Adam Reese <adam@reese.io>
pull/6508/head
Adam Reese 6 years ago
parent 3fa4465eec
commit f021ad48c6
No known key found for this signature in database
GPG Key ID: 06F35E60A7A18DD6

@ -22,11 +22,10 @@ import (
"log" "log"
"os" "os"
"strings" "strings"
"sync"
"github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/klog" "k8s.io/klog"
// Import to initialize client auth plugins. // Import to initialize client auth plugins.
@ -43,11 +42,7 @@ import (
// FeatureGateOCI is the feature gate for checking if `helm chart` and `helm registry` commands should work // FeatureGateOCI is the feature gate for checking if `helm chart` and `helm registry` commands should work
const FeatureGateOCI = gates.Gate("HELM_EXPERIMENTAL_OCI") const FeatureGateOCI = gates.Gate("HELM_EXPERIMENTAL_OCI")
var ( var settings = cli.New()
settings = cli.New()
config genericclioptions.RESTClientGetter
configOnce sync.Once
)
func init() { func init() {
log.SetFlags(log.Lshortfile) log.SetFlags(log.Lshortfile)
@ -74,9 +69,6 @@ func main() {
actionConfig := new(action.Configuration) actionConfig := new(action.Configuration)
cmd := newRootCmd(actionConfig, os.Stdout, os.Args[1:]) cmd := newRootCmd(actionConfig, os.Stdout, os.Args[1:])
// Initialize the rest of the actionConfig
initActionConfig(actionConfig, false)
if err := cmd.Execute(); err != nil { if err := cmd.Execute(); err != nil {
debug("%+v", err) debug("%+v", err)
switch e := err.(type) { switch e := err.(type) {
@ -88,60 +80,52 @@ func main() {
} }
} }
func initActionConfig(actionConfig *action.Configuration, allNamespaces bool) { func initActionConfig(ac *action.Configuration, allNamespaces bool) error {
kc := kube.New(kubeConfig()) if ac.Log == nil {
kc.Log = debug ac.Log = debug
clientset, err := kc.Factory.KubernetesClientSet()
if err != nil {
// TODO return error
log.Fatal(err)
} }
var namespace string if ac.RESTClientGetter == nil {
if !allNamespaces { ac.RESTClientGetter = settings.KubeConfig
namespace = getNamespace()
} }
var store *storage.Storage if ac.KubeClient == nil {
switch os.Getenv("HELM_DRIVER") { kc := kube.New(ac.RESTClientGetter)
case "secret", "secrets", "": kc.Log = debug
d := driver.NewSecrets(clientset.CoreV1().Secrets(namespace)) ac.KubeClient = kc
d.Log = debug
store = storage.Init(d)
case "configmap", "configmaps":
d := driver.NewConfigMaps(clientset.CoreV1().ConfigMaps(namespace))
d.Log = debug
store = storage.Init(d)
case "memory":
d := driver.NewMemory()
store = storage.Init(d)
default:
// Not sure what to do here.
panic("Unknown driver in HELM_DRIVER: " + os.Getenv("HELM_DRIVER"))
} }
actionConfig.RESTClientGetter = kubeConfig() if ac.Releases == nil {
actionConfig.KubeClient = kc kc := kube.New(ac.RESTClientGetter)
actionConfig.Releases = store kc.Log = debug
actionConfig.Log = debug ac.KubeClient = kc
} clientset, err := kc.Factory.KubernetesClientSet()
if err != nil {
func kubeConfig() genericclioptions.RESTClientGetter { return err
configOnce.Do(func() { }
config = kube.GetConfig(settings.KubeConfig, settings.KubeContext, settings.Namespace) var namespace string
}) if !allNamespaces {
return config namespace = settings.Namespace()
} }
func getNamespace() string {
if settings.Namespace != "" {
return settings.Namespace
}
if ns, _, err := kubeConfig().ToRawKubeConfigLoader().Namespace(); err == nil { var store *storage.Storage
return ns switch os.Getenv("HELM_DRIVER") {
case "secret", "secrets", "":
d := driver.NewSecrets(clientset.CoreV1().Secrets(namespace))
d.Log = debug
store = storage.Init(d)
case "configmap", "configmaps":
d := driver.NewConfigMaps(clientset.CoreV1().ConfigMaps(namespace))
d.Log = debug
store = storage.Init(d)
case "memory":
d := driver.NewMemory()
store = storage.Init(d)
default:
return errors.New("Unknown driver in HELM_DRIVER: " + os.Getenv("HELM_DRIVER"))
}
ac.Releases = store
} }
return "default" return nil
} }
// wordSepNormalizeFunc changes all flags that contain "_" separators // wordSepNormalizeFunc changes all flags that contain "_" separators

@ -210,7 +210,7 @@ func runInstall(args []string, client *action.Install, valueOpts *values.Options
} }
} }
client.Namespace = getNamespace() client.Namespace = settings.Namespace()
return client.Run(chartRequested, vals) return client.Run(chartRequested, vals)
} }

@ -51,7 +51,7 @@ func newLintCmd(out io.Writer) *cobra.Command {
if len(args) > 0 { if len(args) > 0 {
paths = args paths = args
} }
client.Namespace = getNamespace() client.Namespace = settings.Namespace()
vals, err := valueOpts.MergeValues(getter.All(settings)) vals, err := valueOpts.MergeValues(getter.All(settings))
if err != nil { if err != nil {
return err return err

@ -65,6 +65,13 @@ func newListCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
Long: listHelp, Long: listHelp,
Aliases: []string{"ls"}, Aliases: []string{"ls"},
Args: require.NoArgs, Args: require.NoArgs,
PreRunE: func(cmd *cobra.Command, args []string) error {
if err := initActionConfig(cfg, client.AllNamespaces); err != nil {
return err
}
client.SetStateMask()
return nil
},
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
// validate the output format first so we don't waste time running a // validate the output format first so we don't waste time running a
// request that we'll throw away // request that we'll throw away
@ -72,12 +79,6 @@ func newListCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
if err != nil { if err != nil {
return err return err
} }
if client.AllNamespaces {
initActionConfig(cfg, true)
}
client.SetStateMask()
results, err := client.Run() results, err := client.Run()
if client.Short { if client.Short {

@ -82,8 +82,8 @@ func loadPlugins(baseCmd *cobra.Command, out io.Writer) {
// Call setupEnv before PrepareCommand because // Call setupEnv before PrepareCommand because
// PrepareCommand uses os.ExpandEnv and expects the // PrepareCommand uses os.ExpandEnv and expects the
// setupEnv vars. // setupEnv vars.
plugin.SetupPluginEnv(settings, md.Name, plug.Dir) penv := plugin.SetupPluginEnv(settings, md.Name, plug.Dir)
main, argv, prepCmdErr := plug.PrepareCommand(u) main, argv, prepCmdErr := plug.PrepareCommand(u, penv)
if prepCmdErr != nil { if prepCmdErr != nil {
os.Stderr.WriteString(prepCmdErr.Error()) os.Stderr.WriteString(prepCmdErr.Error())
return errors.Errorf("plugin %q exited with error", md.Name) return errors.Errorf("plugin %q exited with error", md.Name)
@ -96,9 +96,7 @@ func loadPlugins(baseCmd *cobra.Command, out io.Writer) {
prog := exec.Command(main, argv...) prog := exec.Command(main, argv...)
prog.Env = env prog.Env = env
prog.Stdin = os.Stdin prog.Stdin, prog.Stdout, prog.Stderr = os.Stdin, out, os.Stderr
prog.Stdout = out
prog.Stderr = os.Stderr
if err := prog.Run(); err != nil { if err := prog.Run(); err != nil {
if eerr, ok := err.(*exec.ExitError); ok { if eerr, ok := err.(*exec.ExitError); ok {
os.Stderr.Write(eerr.Stderr) os.Stderr.Write(eerr.Stderr)

@ -204,6 +204,10 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string
SilenceUsage: true, SilenceUsage: true,
Args: require.NoArgs, Args: require.NoArgs,
BashCompletionFunction: bashCompletionFunc, BashCompletionFunction: bashCompletionFunc,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
// Initialize the rest of the actionConfig
return initActionConfig(actionConfig, false)
},
} }
flags := cmd.PersistentFlags() flags := cmd.PersistentFlags()

@ -0,0 +1,6 @@
fullenv
testdata/helmhome/helm/plugins/fullenv
testdata/helmhome/helm/plugins
testdata/helmhome/helm/repositories.yaml
testdata/helmhome/helm/repository
/var/folders/sr/3jkq42_d5fgbvdfwh05rrnlw0000gp/T/go-build387531407/b001/helm.test

@ -75,8 +75,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
if err != nil { if err != nil {
return err return err
} }
client.Namespace = settings.Namespace()
client.Namespace = getNamespace()
if client.Version == "" && client.Devel { if client.Version == "" && client.Devel {
debug("setting version to >0.0.0-0") debug("setting version to >0.0.0-0")

@ -22,10 +22,9 @@ import (
"time" "time"
"github.com/pkg/errors" "github.com/pkg/errors"
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/client-go/discovery" "k8s.io/client-go/discovery"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"helm.sh/helm/internal/experimental/registry" "helm.sh/helm/internal/experimental/registry"
"helm.sh/helm/pkg/chartutil" "helm.sh/helm/pkg/chartutil"
@ -65,7 +64,7 @@ var ValidName = regexp.MustCompile("^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])+
// Configuration injects the dependencies that all actions share. // Configuration injects the dependencies that all actions share.
type Configuration struct { type Configuration struct {
// RESTClientGetter is an interface that loads Kuberbetes clients. // RESTClientGetter is an interface that loads Kuberbetes clients.
RESTClientGetter RESTClientGetter RESTClientGetter genericclioptions.RESTClientGetter
// Releases stores records of releases. // Releases stores records of releases.
Releases *storage.Storage Releases *storage.Storage
@ -196,9 +195,3 @@ func (c *Configuration) recordRelease(r *release.Release) {
c.Log("warning: Failed to update release %s: %s", r.Name, err) c.Log("warning: Failed to update release %s: %s", r.Name, err)
} }
} }
type RESTClientGetter interface {
ToRESTConfig() (*rest.Config, error)
ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error)
ToRESTMapper() (meta.RESTMapper, error)
}

@ -56,11 +56,12 @@ func NewPull() *Pull {
func (p *Pull) Run(chartRef string) (string, error) { func (p *Pull) Run(chartRef string) (string, error) {
var out strings.Builder var out strings.Builder
getters := getter.All(p.Settings)
c := downloader.ChartDownloader{ c := downloader.ChartDownloader{
Out: &out, Out: &out,
Keyring: p.Keyring, Keyring: p.Keyring,
Verify: downloader.VerifyNever, Verify: downloader.VerifyNever,
Getters: getter.All(p.Settings), Getters: getters,
Options: []getter.Option{ Options: []getter.Option{
getter.WithBasicAuth(p.Username, p.Password), getter.WithBasicAuth(p.Username, p.Password),
}, },
@ -87,7 +88,7 @@ func (p *Pull) Run(chartRef string) (string, error) {
} }
if p.RepoURL != "" { if p.RepoURL != "" {
chartURL, err := repo.FindChartInAuthRepoURL(p.RepoURL, p.Username, p.Password, chartRef, p.Version, p.CertFile, p.KeyFile, p.CaFile, getter.All(p.Settings)) chartURL, err := repo.FindChartInAuthRepoURL(p.RepoURL, p.Username, p.Password, chartRef, p.Version, p.CertFile, p.KeyFile, p.CaFile, getters)
if err != nil { if err != nil {
return out.String(), err return out.String(), err
} }

@ -28,21 +28,15 @@ import (
"strconv" "strconv"
"github.com/spf13/pflag" "github.com/spf13/pflag"
"k8s.io/cli-runtime/pkg/genericclioptions"
"helm.sh/helm/pkg/helmpath" "helm.sh/helm/pkg/helmpath"
) )
// EnvSettings describes all of the environment settings. // EnvSettings describes all of the environment settings.
type EnvSettings struct { type EnvSettings struct {
// Namespace is the namespace scope.
Namespace string
// KubeConfig is the path to the kubeconfig file.
KubeConfig string
// KubeContext is the name of the kubeconfig context.
KubeContext string
// Debug indicates whether or not Helm is running in Debug mode. // Debug indicates whether or not Helm is running in Debug mode.
Debug bool Debug bool
// RegistryConfig is the path to the registry config file. // RegistryConfig is the path to the registry config file.
RegistryConfig string RegistryConfig string
// RepositoryConfig is the path to the repositories file. // RepositoryConfig is the path to the repositories file.
@ -51,29 +45,29 @@ type EnvSettings struct {
RepositoryCache string RepositoryCache string
// PluginsDirectory is the path to the plugins directory. // PluginsDirectory is the path to the plugins directory.
PluginsDirectory string PluginsDirectory string
KubeConfig *genericclioptions.ConfigFlags
} }
func New() *EnvSettings { func New() *EnvSettings {
env := EnvSettings{ return &EnvSettings{
Namespace: os.Getenv("HELM_NAMESPACE"), // Namespace: os.Getenv("HELM_NAMESPACE"),
Debug: envBool("HELM_DEBUG"),
PluginsDirectory: envOr("HELM_PLUGINS", helmpath.DataPath("plugins")), PluginsDirectory: envOr("HELM_PLUGINS", helmpath.DataPath("plugins")),
RegistryConfig: envOr("HELM_REGISTRY_CONFIG", helmpath.ConfigPath("registry.json")), RegistryConfig: envOr("HELM_REGISTRY_CONFIG", helmpath.ConfigPath("registry.json")),
RepositoryConfig: envOr("HELM_REPOSITORY_CONFIG", helmpath.ConfigPath("repositories.yaml")), RepositoryConfig: envOr("HELM_REPOSITORY_CONFIG", helmpath.ConfigPath("repositories.yaml")),
RepositoryCache: envOr("HELM_REPOSITORY_CACHE", helmpath.CachePath("repository")), RepositoryCache: envOr("HELM_REPOSITORY_CACHE", helmpath.CachePath("repository")),
KubeConfig: genericclioptions.NewConfigFlags(true),
} }
env.Debug, _ = strconv.ParseBool(os.Getenv("HELM_DEBUG"))
return &env
} }
// AddFlags binds flags to the given flagset. // AddFlags binds flags to the given flagset.
func (s *EnvSettings) AddFlags(fs *pflag.FlagSet) { func (s *EnvSettings) AddFlags(fs *pflag.FlagSet) {
fs.StringVarP(&s.Namespace, "namespace", "n", s.Namespace, "namespace scope for this request")
fs.StringVar(&s.KubeConfig, "kubeconfig", "", "path to the kubeconfig file")
fs.StringVar(&s.KubeContext, "kube-context", "", "name of the kubeconfig context to use")
fs.BoolVar(&s.Debug, "debug", s.Debug, "enable verbose output") fs.BoolVar(&s.Debug, "debug", s.Debug, "enable verbose output")
fs.StringVar(&s.RegistryConfig, "registry-config", s.RegistryConfig, "path to the registry config file") fs.StringVar(&s.RegistryConfig, "registry-config", s.RegistryConfig, "path to the registry config file")
fs.StringVar(&s.RepositoryConfig, "repository-config", s.RepositoryConfig, "path to the file containing repository names and URLs") fs.StringVar(&s.RepositoryConfig, "repository-config", s.RepositoryConfig, "path to the file containing repository names and URLs")
fs.StringVar(&s.RepositoryCache, "repository-cache", s.RepositoryCache, "path to the file containing cached repository indexes") fs.StringVar(&s.RepositoryCache, "repository-cache", s.RepositoryCache, "path to the file containing cached repository indexes")
s.KubeConfig.AddFlags(fs)
} }
func envOr(name, def string) string { func envOr(name, def string) string {
@ -83,6 +77,22 @@ func envOr(name, def string) string {
return def return def
} }
func envBool(name string) bool {
var v bool
v, _ = strconv.ParseBool(os.Getenv(name))
return v
}
func (s *EnvSettings) Namespace() string {
if ns := os.Getenv("HELM_NAMESPACE"); ns != "" {
return ns
}
if ns, _, err := s.KubeConfig.ToRawKubeConfigLoader().Namespace(); err == nil {
return ns
}
return "default"
}
func (s *EnvSettings) EnvVars() map[string]string { func (s *EnvSettings) EnvVars() map[string]string {
return map[string]string{ return map[string]string{
"HELM_BIN": os.Args[0], "HELM_BIN": os.Args[0],
@ -91,5 +101,6 @@ func (s *EnvSettings) EnvVars() map[string]string {
"HELM_REGISTRY_CONFIG": s.RegistryConfig, "HELM_REGISTRY_CONFIG": s.RegistryConfig,
"HELM_REPOSITORY_CACHE": s.RepositoryCache, "HELM_REPOSITORY_CACHE": s.RepositoryCache,
"HELM_REPOSITORY_CONFIG": s.RepositoryConfig, "HELM_REPOSITORY_CONFIG": s.RepositoryConfig,
"HELM_NAMESPACE": s.Namespace(),
} }
} }

@ -78,12 +78,6 @@ func TestEnvSettings(t *testing.T) {
if settings.Debug != tt.debug { if settings.Debug != tt.debug {
t.Errorf("expected debug %t, got %t", tt.debug, settings.Debug) t.Errorf("expected debug %t, got %t", tt.debug, settings.Debug)
} }
if settings.Namespace != tt.ns {
t.Errorf("expected namespace %q, got %q", tt.ns, settings.Namespace)
}
if settings.KubeContext != tt.kcontext {
t.Errorf("expected kube-context %q, got %q", tt.kcontext, settings.KubeContext)
}
}) })
} }
} }

@ -33,6 +33,13 @@ const (
repoCache = "testdata/repository" repoCache = "testdata/repository"
) )
func testGetters() getter.Providers {
env := cli.New()
env.RepositoryConfig = repoConfig
env.RepositoryCache = repoCache
return getter.All(env)
}
func TestResolveChartRef(t *testing.T) { func TestResolveChartRef(t *testing.T) {
tests := []struct { tests := []struct {
name, ref, expect, version string name, ref, expect, version string
@ -59,10 +66,7 @@ func TestResolveChartRef(t *testing.T) {
Out: os.Stderr, Out: os.Stderr,
RepositoryConfig: repoConfig, RepositoryConfig: repoConfig,
RepositoryCache: repoCache, RepositoryCache: repoCache,
Getters: getter.All(&cli.EnvSettings{ Getters: testGetters(),
RepositoryConfig: repoConfig,
RepositoryCache: repoCache,
}),
} }
for _, tt := range tests { for _, tt := range tests {
@ -138,10 +142,7 @@ func TestDownloadTo(t *testing.T) {
Keyring: "testdata/helm-test-key.pub", Keyring: "testdata/helm-test-key.pub",
RepositoryConfig: repoConfig, RepositoryConfig: repoConfig,
RepositoryCache: repoCache, RepositoryCache: repoCache,
Getters: getter.All(&cli.EnvSettings{ Getters: testGetters(),
RepositoryConfig: repoConfig,
RepositoryCache: repoCache,
}),
Options: []getter.Option{ Options: []getter.Option{
getter.WithBasicAuth("username", "password"), getter.WithBasicAuth("username", "password"),
}, },
@ -186,10 +187,7 @@ func TestDownloadTo_VerifyLater(t *testing.T) {
Verify: VerifyLater, Verify: VerifyLater,
RepositoryConfig: repoConfig, RepositoryConfig: repoConfig,
RepositoryCache: repoCache, RepositoryCache: repoCache,
Getters: getter.All(&cli.EnvSettings{ Getters: testGetters(),
RepositoryConfig: repoConfig,
RepositoryCache: repoCache,
}),
} }
cname := "/signtest-0.1.0.tgz" cname := "/signtest-0.1.0.tgz"
where, _, err := c.DownloadTo(srv.URL()+cname, "", dest) where, _, err := c.DownloadTo(srv.URL()+cname, "", dest)
@ -215,10 +213,7 @@ func TestScanReposForURL(t *testing.T) {
Verify: VerifyLater, Verify: VerifyLater,
RepositoryConfig: repoConfig, RepositoryConfig: repoConfig,
RepositoryCache: repoCache, RepositoryCache: repoCache,
Getters: getter.All(&cli.EnvSettings{ Getters: testGetters(),
RepositoryConfig: repoConfig,
RepositoryCache: repoCache,
}),
} }
u := "http://example.com/alpine-0.2.0.tgz" u := "http://example.com/alpine-0.2.0.tgz"

@ -23,6 +23,12 @@ import (
const pluginDir = "testdata/plugins" const pluginDir = "testdata/plugins"
func testEnv() *cli.EnvSettings {
env := cli.New()
env.PluginsDirectory = pluginDir
return env
}
func TestProvider(t *testing.T) { func TestProvider(t *testing.T) {
p := Provider{ p := Provider{
[]string{"one", "three"}, []string{"one", "three"},
@ -53,9 +59,7 @@ func TestProviders(t *testing.T) {
} }
func TestAll(t *testing.T) { func TestAll(t *testing.T) {
all := All(&cli.EnvSettings{ all := All(testEnv())
PluginsDirectory: pluginDir,
})
if len(all) != 3 { if len(all) != 3 {
t.Errorf("expected 3 providers (default plus two plugins), got %d", len(all)) t.Errorf("expected 3 providers (default plus two plugins), got %d", len(all))
} }
@ -66,9 +70,7 @@ func TestAll(t *testing.T) {
} }
func TestByScheme(t *testing.T) { func TestByScheme(t *testing.T) {
g := All(&cli.EnvSettings{ g := All(testEnv())
PluginsDirectory: pluginDir,
})
if _, err := g.ByScheme("test"); err != nil { if _, err := g.ByScheme("test"); err != nil {
t.Error(err) t.Error(err)
} }

@ -104,7 +104,7 @@ func TestDownload(t *testing.T) {
})) }))
defer srv.Close() defer srv.Close()
g, err := All(new(cli.EnvSettings)).ByScheme("http") g, err := All(cli.New()).ByScheme("http")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

@ -19,15 +19,10 @@ import (
"runtime" "runtime"
"strings" "strings"
"testing" "testing"
"helm.sh/helm/pkg/cli"
) )
func TestCollectPlugins(t *testing.T) { func TestCollectPlugins(t *testing.T) {
env := &cli.EnvSettings{ p, err := collectPlugins(testEnv())
PluginsDirectory: pluginDir,
}
p, err := collectPlugins(env)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -54,10 +49,7 @@ func TestPluginGetter(t *testing.T) {
t.Skip("TODO: refactor this test to work on windows") t.Skip("TODO: refactor this test to work on windows")
} }
env := &cli.EnvSettings{ pg := NewPluginGetter("echo", testEnv(), "test", ".")
PluginsDirectory: pluginDir,
}
pg := NewPluginGetter("echo", env, "test", ".")
g, err := pg() g, err := pg()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -80,10 +72,7 @@ func TestPluginSubCommands(t *testing.T) {
t.Skip("TODO: refactor this test to work on windows") t.Skip("TODO: refactor this test to work on windows")
} }
env := &cli.EnvSettings{ pg := NewPluginGetter("echo -n", testEnv(), "test", ".")
PluginsDirectory: pluginDir,
}
pg := NewPluginGetter("echo -n", env, "test", ".")
g, err := pg() g, err := pg()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)

@ -108,15 +108,22 @@ type Plugin struct {
// - If both OS and Arch match the current platform, search will stop and the command will be prepared for execution // - If both OS and Arch match the current platform, search will stop and the command will be prepared for execution
// - If OS matches and there is no more specific match, the command will be prepared for execution // - If OS matches and there is no more specific match, the command will be prepared for execution
// - If no OS/Arch match is found, return nil // - If no OS/Arch match is found, return nil
func getPlatformCommand(cmds []PlatformCommand) []string { func getPlatformCommand(cmds []PlatformCommand, env map[string]string) []string {
getenv := func(name string) string {
if v, ok := env[name]; ok {
return v
}
return os.Getenv(name)
}
var command []string var command []string
eq := strings.EqualFold eq := strings.EqualFold
for _, c := range cmds { for _, c := range cmds {
if eq(c.OperatingSystem, runtime.GOOS) { if eq(c.OperatingSystem, runtime.GOOS) {
command = strings.Split(os.ExpandEnv(c.Command), " ") command = strings.Split(os.Expand(c.Command, getenv), " ")
} }
if eq(c.OperatingSystem, runtime.GOOS) && eq(c.Architecture, runtime.GOARCH) { if eq(c.OperatingSystem, runtime.GOOS) && eq(c.Architecture, runtime.GOARCH) {
return strings.Split(os.ExpandEnv(c.Command), " ") return strings.Split(os.Expand(c.Command, getenv), " ")
} }
} }
return command return command
@ -133,11 +140,11 @@ func getPlatformCommand(cmds []PlatformCommand) []string {
// returns the name of the command and an args array. // returns the name of the command and an args array.
// //
// The result is suitable to pass to exec.Command. // The result is suitable to pass to exec.Command.
func (p *Plugin) PrepareCommand(extraArgs []string) (string, []string, error) { func (p *Plugin) PrepareCommand(extraArgs []string, env map[string]string) (string, []string, error) {
var parts []string var parts []string
platCmdLen := len(p.Metadata.PlatformCommand) platCmdLen := len(p.Metadata.PlatformCommand)
if platCmdLen > 0 { if platCmdLen > 0 {
parts = getPlatformCommand(p.Metadata.PlatformCommand) parts = getPlatformCommand(p.Metadata.PlatformCommand, env)
} }
if platCmdLen == 0 || parts == nil { if platCmdLen == 0 || parts == nil {
parts = strings.Split(os.ExpandEnv(p.Metadata.Command), " ") parts = strings.Split(os.ExpandEnv(p.Metadata.Command), " ")
@ -215,11 +222,12 @@ func FindPlugins(plugdirs string) ([]*Plugin, error) {
// SetupPluginEnv prepares os.Env for plugins. It operates on os.Env because // SetupPluginEnv prepares os.Env for plugins. It operates on os.Env because
// the plugin subsystem itself needs access to the environment variables // the plugin subsystem itself needs access to the environment variables
// created here. // created here.
func SetupPluginEnv(settings *cli.EnvSettings, name, base string) { func SetupPluginEnv(settings *cli.EnvSettings, name, base string) map[string]string {
env := settings.EnvVars() env := settings.EnvVars()
env["HELM_PLUGIN_NAME"] = name env["HELM_PLUGIN_NAME"] = name
env["HELM_PLUGIN_DIR"] = base env["HELM_PLUGIN_DIR"] = base
for key, val := range env { for key, val := range env {
os.Setenv(key, val) os.Setenv(key, val)
} }
return env
} }

@ -26,7 +26,7 @@ import (
) )
func checkCommand(p *Plugin, extraArgs []string, osStrCmp string, t *testing.T) { func checkCommand(p *Plugin, extraArgs []string, osStrCmp string, t *testing.T) {
cmd, args, err := p.PrepareCommand(extraArgs) cmd, args, err := p.PrepareCommand(extraArgs, map[string]string{})
if err != nil { if err != nil {
t.Errorf(err.Error()) t.Errorf(err.Error())
} }
@ -47,7 +47,7 @@ func checkCommand(p *Plugin, extraArgs []string, osStrCmp string, t *testing.T)
// Test with IgnoreFlags. This should omit --debug, --foo, bar // Test with IgnoreFlags. This should omit --debug, --foo, bar
p.Metadata.IgnoreFlags = true p.Metadata.IgnoreFlags = true
cmd, args, err = p.PrepareCommand(extraArgs) cmd, args, err = p.PrepareCommand(extraArgs, map[string]string{})
if err != nil { if err != nil {
t.Errorf(err.Error()) t.Errorf(err.Error())
} }
@ -144,7 +144,7 @@ func TestNoPrepareCommand(t *testing.T) {
} }
argv := []string{"--debug", "--foo", "bar"} argv := []string{"--debug", "--foo", "bar"}
_, _, err := p.PrepareCommand(argv) _, _, err := p.PrepareCommand(argv, map[string]string{})
if err == nil { if err == nil {
t.Errorf("Expected error to be returned") t.Errorf("Expected error to be returned")
} }
@ -162,7 +162,7 @@ func TestNoMatchPrepareCommand(t *testing.T) {
} }
argv := []string{"--debug", "--foo", "bar"} argv := []string{"--debug", "--foo", "bar"}
if _, _, err := p.PrepareCommand(argv); err == nil { if _, _, err := p.PrepareCommand(argv, map[string]string{}); err == nil {
t.Errorf("Expected error to be returned") t.Errorf("Expected error to be returned")
} }
} }
@ -259,11 +259,10 @@ func TestSetupEnv(t *testing.T) {
name := "pequod" name := "pequod"
base := filepath.Join("testdata/helmhome/helm/plugins", name) base := filepath.Join("testdata/helmhome/helm/plugins", name)
s := &cli.EnvSettings{ env := cli.New()
PluginsDirectory: "testdata/helmhome/helm/plugins", env.PluginsDirectory = "testdata/helmhome/helm/plugins"
}
SetupPluginEnv(s, name, base) SetupPluginEnv(env, name, base)
for _, tt := range []struct { for _, tt := range []struct {
name, expect string name, expect string
}{ }{

@ -45,7 +45,7 @@ func TestLoadChartRepository(t *testing.T) {
r, err := NewChartRepository(&Entry{ r, err := NewChartRepository(&Entry{
Name: testRepository, Name: testRepository,
URL: testURL, URL: testURL,
}, getter.All(&cli.EnvSettings{})) }, getter.All(cli.New()))
if err != nil { if err != nil {
t.Errorf("Problem creating chart repository from %s: %v", testRepository, err) t.Errorf("Problem creating chart repository from %s: %v", testRepository, err)
} }
@ -78,7 +78,7 @@ func TestIndex(t *testing.T) {
r, err := NewChartRepository(&Entry{ r, err := NewChartRepository(&Entry{
Name: testRepository, Name: testRepository,
URL: testURL, URL: testURL,
}, getter.All(&cli.EnvSettings{})) }, getter.All(cli.New()))
if err != nil { if err != nil {
t.Errorf("Problem creating chart repository from %s: %v", testRepository, err) t.Errorf("Problem creating chart repository from %s: %v", testRepository, err)
} }
@ -283,7 +283,7 @@ func TestFindChartInRepoURL(t *testing.T) {
} }
defer srv.Close() defer srv.Close()
chartURL, err := FindChartInRepoURL(srv.URL, "nginx", "", "", "", "", getter.All(&cli.EnvSettings{})) chartURL, err := FindChartInRepoURL(srv.URL, "nginx", "", "", "", "", getter.All(cli.New()))
if err != nil { if err != nil {
t.Fatalf("%v", err) t.Fatalf("%v", err)
} }
@ -291,7 +291,7 @@ func TestFindChartInRepoURL(t *testing.T) {
t.Errorf("%s is not the valid URL", chartURL) t.Errorf("%s is not the valid URL", chartURL)
} }
chartURL, err = FindChartInRepoURL(srv.URL, "nginx", "0.1.0", "", "", "", getter.All(&cli.EnvSettings{})) chartURL, err = FindChartInRepoURL(srv.URL, "nginx", "0.1.0", "", "", "", getter.All(cli.New()))
if err != nil { if err != nil {
t.Errorf("%s", err) t.Errorf("%s", err)
} }
@ -302,9 +302,9 @@ func TestFindChartInRepoURL(t *testing.T) {
func TestErrorFindChartInRepoURL(t *testing.T) { func TestErrorFindChartInRepoURL(t *testing.T) {
g := getter.All(&cli.EnvSettings{ env := cli.New()
RepositoryCache: ensure.TempDir(t), env.RepositoryCache = ensure.TempDir(t)
}) g := getter.All(env)
if _, err := FindChartInRepoURL("http://someserver/something", "nginx", "", "", "", "", g); err == nil { if _, err := FindChartInRepoURL("http://someserver/something", "nginx", "", "", "", "", g); err == nil {
t.Errorf("Expected error for bad chart URL, but did not get any errors") t.Errorf("Expected error for bad chart URL, but did not get any errors")

@ -137,7 +137,7 @@ func TestDownloadIndexFile(t *testing.T) {
r, err := NewChartRepository(&Entry{ r, err := NewChartRepository(&Entry{
Name: testRepo, Name: testRepo,
URL: srv.URL, URL: srv.URL,
}, getter.All(&cli.EnvSettings{})) }, getter.All(cli.New()))
if err != nil { if err != nil {
t.Errorf("Problem creating chart repository from %s: %v", testRepo, err) t.Errorf("Problem creating chart repository from %s: %v", testRepo, err)
} }

Loading…
Cancel
Save