From 6f780bb7502cab2b1eddcdb3a127a15a5b2579f9 Mon Sep 17 00:00:00 2001 From: leigh capili Date: Wed, 9 Sep 2020 14:44:40 -0600 Subject: [PATCH 1/2] Document all env vars for CLI help Signed-off-by: leigh capili --- cmd/helm/root.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cmd/helm/root.go b/cmd/helm/root.go index 904f11a21..0135048a6 100644 --- a/cmd/helm/root.go +++ b/cmd/helm/root.go @@ -48,11 +48,20 @@ Environment variables: | $HELM_CACHE_HOME | set an alternative location for storing cached files. | | $HELM_CONFIG_HOME | set an alternative location for storing Helm configuration. | | $HELM_DATA_HOME | set an alternative location for storing Helm data. | +| $HELM_DEBUG | indicate whether or not Helm is running in Debug mode | | $HELM_DRIVER | set the backend storage driver. Values are: configmap, secret, memory, postgres | | $HELM_DRIVER_SQL_CONNECTION_STRING | set the connection string the SQL storage driver should use. | | $HELM_MAX_HISTORY | set the maximum number of helm release history. | +| $HELM_NAMESPACE | set the namespace used for the helm operations. | | $HELM_NO_PLUGINS | disable plugins. Set HELM_NO_PLUGINS=1 to disable plugins. | +| $HELM_PLUGINS | set the path to the plugins directory | +| $HELM_REGISTRY_CONFIG | set the path to the registry config file. | +| $HELM_REPOSITORY_CACHE | set the path to the repository cache directory | +| $HELM_REPOSITORY_CONFIG | set the path to the repositories file. | | $KUBECONFIG | set an alternative Kubernetes configuration file (default "~/.kube/config") | +| $HELM_KUBEAPISERVER | set the Kubernetes API Server Endpoint for authentication | +| $HELM_KUBECONTEXT | set the name of the kubeconfig context. | +| $HELM_KUBETOKEN | set the Bearer KubeToken used for authentication. | Helm stores cache, configuration, and data based on the following configuration order: From 9429af8b39c6888a41ffa2945d7c73676afb577e Mon Sep 17 00:00:00 2001 From: leigh capili Date: Sat, 5 Sep 2020 21:01:00 -0600 Subject: [PATCH 2/2] Support impersonation via flags similar to kubectl --as="user" Signed-off-by: leigh capili --- cmd/helm/load_plugins.go | 2 +- cmd/helm/plugin_test.go | 6 ++++++ cmd/helm/root.go | 2 ++ cmd/helm/testdata/output/env-comp.txt | 2 ++ pkg/cli/environment.go | 31 ++++++++++++++++++++++----- pkg/cli/environment_test.go | 25 ++++++++++++++++----- 6 files changed, 57 insertions(+), 11 deletions(-) diff --git a/cmd/helm/load_plugins.go b/cmd/helm/load_plugins.go index a6e0c4eae..e4aac6c0f 100644 --- a/cmd/helm/load_plugins.go +++ b/cmd/helm/load_plugins.go @@ -154,7 +154,7 @@ func callPluginExecutable(pluginName string, main string, argv []string, out io. func manuallyProcessArgs(args []string) ([]string, []string) { known := []string{} unknown := []string{} - kvargs := []string{"--kube-context", "--namespace", "-n", "--kubeconfig", "--kube-apiserver", "--kube-token", "--registry-config", "--repository-cache", "--repository-config"} + kvargs := []string{"--kube-context", "--namespace", "-n", "--kubeconfig", "--kube-apiserver", "--kube-token", "--kube-as-user", "--kube-as-group", "--registry-config", "--repository-cache", "--repository-config"} knownArg := func(a string) bool { for _, pre := range kvargs { if strings.HasPrefix(a, pre+"=") { diff --git a/cmd/helm/plugin_test.go b/cmd/helm/plugin_test.go index cf21d8460..0bf867f2a 100644 --- a/cmd/helm/plugin_test.go +++ b/cmd/helm/plugin_test.go @@ -37,6 +37,9 @@ func TestManuallyProcessArgs(t *testing.T) { "--kubeconfig", "/home/foo", "--kube-context=test1", "--kube-context", "test1", + "--kube-as-user", "pikachu", + "--kube-as-group", "teatime", + "--kube-as-group", "admins", "-n=test2", "-n", "test2", "--namespace=test2", @@ -51,6 +54,9 @@ func TestManuallyProcessArgs(t *testing.T) { "--kubeconfig", "/home/foo", "--kube-context=test1", "--kube-context", "test1", + "--kube-as-user", "pikachu", + "--kube-as-group", "teatime", + "--kube-as-group", "admins", "-n=test2", "-n", "test2", "--namespace=test2", diff --git a/cmd/helm/root.go b/cmd/helm/root.go index 0135048a6..82c87bd7d 100644 --- a/cmd/helm/root.go +++ b/cmd/helm/root.go @@ -60,6 +60,8 @@ Environment variables: | $HELM_REPOSITORY_CONFIG | set the path to the repositories file. | | $KUBECONFIG | set an alternative Kubernetes configuration file (default "~/.kube/config") | | $HELM_KUBEAPISERVER | set the Kubernetes API Server Endpoint for authentication | +| $HELM_KUBEASGROUPS | set the Username to impersonate for the operation. | +| $HELM_KUBEASUSER | set the Groups to use for impoersonation using a comma-separated list. | | $HELM_KUBECONTEXT | set the name of the kubeconfig context. | | $HELM_KUBETOKEN | set the Bearer KubeToken used for authentication. | diff --git a/cmd/helm/testdata/output/env-comp.txt b/cmd/helm/testdata/output/env-comp.txt index c4b46ae6b..3739d8bc1 100644 --- a/cmd/helm/testdata/output/env-comp.txt +++ b/cmd/helm/testdata/output/env-comp.txt @@ -4,6 +4,8 @@ HELM_CONFIG_HOME HELM_DATA_HOME HELM_DEBUG HELM_KUBEAPISERVER +HELM_KUBEASGROUPS +HELM_KUBEASUSER HELM_KUBECONTEXT HELM_KUBETOKEN HELM_MAX_HISTORY diff --git a/pkg/cli/environment.go b/pkg/cli/environment.go index a9994f03d..4f3abc08b 100644 --- a/pkg/cli/environment.go +++ b/pkg/cli/environment.go @@ -26,6 +26,7 @@ import ( "fmt" "os" "strconv" + "strings" "github.com/spf13/pflag" "k8s.io/cli-runtime/pkg/genericclioptions" @@ -47,6 +48,10 @@ type EnvSettings struct { KubeContext string // Bearer KubeToken used for authentication KubeToken string + // Username to impersonate for the operation + KubeAsUser string + // Groups to impersonate for the operation, multiple groups parsed from a comma delimited list + KubeAsGroups []string // Kubernetes API Server Endpoint for authentication KubeAPIServer string // Debug indicates whether or not Helm is running in Debug mode. @@ -69,6 +74,8 @@ func New() *EnvSettings { MaxHistory: envIntOr("HELM_MAX_HISTORY", defaultMaxHistory), KubeContext: os.Getenv("HELM_KUBECONTEXT"), KubeToken: os.Getenv("HELM_KUBETOKEN"), + KubeAsUser: os.Getenv("HELM_KUBEASUSER"), + KubeAsGroups: envCSV("HELM_KUBEASGROUPS"), KubeAPIServer: os.Getenv("HELM_KUBEAPISERVER"), PluginsDirectory: envOr("HELM_PLUGINS", helmpath.DataPath("plugins")), RegistryConfig: envOr("HELM_REGISTRY_CONFIG", helmpath.ConfigPath("registry.json")), @@ -79,11 +86,13 @@ func New() *EnvSettings { // bind to kubernetes config flags env.config = &genericclioptions.ConfigFlags{ - Namespace: &env.namespace, - Context: &env.KubeContext, - BearerToken: &env.KubeToken, - APIServer: &env.KubeAPIServer, - KubeConfig: &env.KubeConfig, + Namespace: &env.namespace, + Context: &env.KubeContext, + BearerToken: &env.KubeToken, + APIServer: &env.KubeAPIServer, + KubeConfig: &env.KubeConfig, + Impersonate: &env.KubeAsUser, + ImpersonateGroup: &env.KubeAsGroups, } return env } @@ -94,6 +103,8 @@ func (s *EnvSettings) AddFlags(fs *pflag.FlagSet) { fs.StringVar(&s.KubeConfig, "kubeconfig", "", "path to the kubeconfig file") 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") + 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.KubeAPIServer, "kube-apiserver", s.KubeAPIServer, "the address and the port for the Kubernetes API server") fs.BoolVar(&s.Debug, "debug", s.Debug, "enable verbose output") fs.StringVar(&s.RegistryConfig, "registry-config", s.RegistryConfig, "path to the registry config file") @@ -120,6 +131,14 @@ func envIntOr(name string, def int) int { return ret } +func envCSV(name string) (ls []string) { + trimmed := strings.Trim(os.Getenv(name), ", ") + if trimmed != "" { + ls = strings.Split(trimmed, ",") + } + return +} + func (s *EnvSettings) EnvVars() map[string]string { envvars := map[string]string{ "HELM_BIN": os.Args[0], @@ -137,6 +156,8 @@ func (s *EnvSettings) EnvVars() map[string]string { // broken, these are populated from helm flags and not kubeconfig. "HELM_KUBECONTEXT": s.KubeContext, "HELM_KUBETOKEN": s.KubeToken, + "HELM_KUBEASUSER": s.KubeAsUser, + "HELM_KUBEASGROUPS": strings.Join(s.KubeAsGroups, ","), "HELM_KUBEAPISERVER": s.KubeAPIServer, } if s.KubeConfig != "" { diff --git a/pkg/cli/environment_test.go b/pkg/cli/environment_test.go index 3234a133b..ffdbce68b 100644 --- a/pkg/cli/environment_test.go +++ b/pkg/cli/environment_test.go @@ -18,6 +18,7 @@ package cli import ( "os" + "reflect" "strings" "testing" @@ -36,6 +37,8 @@ func TestEnvSettings(t *testing.T) { ns, kcontext string debug bool maxhistory int + kAsUser string + kAsGroups []string }{ { name: "defaults", @@ -44,25 +47,31 @@ func TestEnvSettings(t *testing.T) { }, { name: "with flags set", - args: "--debug --namespace=myns", + args: "--debug --namespace=myns --kube-as-user=poro --kube-as-group=admins --kube-as-group=teatime --kube-as-group=snackeaters", ns: "myns", debug: true, maxhistory: defaultMaxHistory, + kAsUser: "poro", + kAsGroups: []string{"admins", "teatime", "snackeaters"}, }, { name: "with envvars set", - envvars: map[string]string{"HELM_DEBUG": "1", "HELM_NAMESPACE": "yourns", "HELM_MAX_HISTORY": "5"}, + envvars: map[string]string{"HELM_DEBUG": "1", "HELM_NAMESPACE": "yourns", "HELM_KUBEASUSER": "pikachu", "HELM_KUBEASGROUPS": ",,,operators,snackeaters,partyanimals", "HELM_MAX_HISTORY": "5"}, ns: "yourns", maxhistory: 5, debug: true, + kAsUser: "pikachu", + kAsGroups: []string{"operators", "snackeaters", "partyanimals"}, }, { name: "with flags and envvars set", - args: "--debug --namespace=myns", - envvars: map[string]string{"HELM_DEBUG": "1", "HELM_NAMESPACE": "yourns"}, + args: "--debug --namespace=myns --kube-as-user=poro --kube-as-group=admins --kube-as-group=teatime --kube-as-group=snackeaters", + envvars: map[string]string{"HELM_DEBUG": "1", "HELM_NAMESPACE": "yourns", "HELM_KUBEASUSER": "pikachu", "HELM_KUBEASGROUPS": ",,,operators,snackeaters,partyanimals", "HELM_MAX_HISTORY": "5"}, ns: "myns", debug: true, - maxhistory: defaultMaxHistory, + maxhistory: 5, + kAsUser: "poro", + kAsGroups: []string{"admins", "teatime", "snackeaters"}, }, } @@ -92,6 +101,12 @@ func TestEnvSettings(t *testing.T) { if settings.MaxHistory != tt.maxhistory { t.Errorf("expected maxHistory %d, got %d", tt.maxhistory, settings.MaxHistory) } + if tt.kAsUser != settings.KubeAsUser { + t.Errorf("expected kAsUser %q, got %q", tt.kAsUser, settings.KubeAsUser) + } + if !reflect.DeepEqual(tt.kAsGroups, settings.KubeAsGroups) { + t.Errorf("expected kAsGroups %+v, got %+v", len(tt.kAsGroups), len(settings.KubeAsGroups)) + } }) } }