From c312376bc264cf36979cf2b04fa8cd419245449e Mon Sep 17 00:00:00 2001 From: Brian Date: Mon, 22 Jul 2024 14:26:42 -0600 Subject: [PATCH] Bensky/kubeconfig flag (#545) * feat/kubeconfig flag * kubeconfig clientcmd option * setting kubeconfig env var * updating doc and package * adding flag option --- cmd/root.go | 10 +++-- docs/advanced.md | 4 +- pkg/discovery-api/discovery_api.go | 4 +- pkg/helm/helm.go | 4 +- pkg/kube/kube.go | 19 +++++++--- pkg/kube/kube_test.go | 59 +++++++++++++++++++++--------- 6 files changed, 68 insertions(+), 32 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index 6faa65ea..52a741c6 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -39,7 +39,7 @@ import ( discoveryapi "github.com/fairwindsops/pluto/v5/pkg/discovery-api" "github.com/fairwindsops/pluto/v5/pkg/finder" "github.com/fairwindsops/pluto/v5/pkg/helm" - "github.com/rogpeppe/go-internal/semver" + "golang.org/x/mod/semver" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -67,6 +67,7 @@ var ( noHeaders bool exitCode int noFooter bool + kubeConfigPath string ) const ( @@ -100,14 +101,17 @@ func init() { detectFilesCmd.PersistentFlags().StringVarP(&directory, "directory", "d", "", "The directory to scan. If blank, defaults to current working dir.") rootCmd.AddCommand(detectHelmCmd) + detectHelmCmd.PersistentFlags().StringVarP(&kubeConfigPath, "kubeconfig", "", "", "The path to the kubeconfig file to use. If blank, defaults to current kubeconfig.") detectHelmCmd.PersistentFlags().StringVarP(&namespace, "namespace", "n", "", "Only detect releases in a specific namespace.") detectHelmCmd.PersistentFlags().StringVar(&kubeContext, "kube-context", "", "The kube context to use. If blank, defaults to current context.") rootCmd.AddCommand(detectApiResourceCmd) + detectApiResourceCmd.PersistentFlags().StringVarP(&kubeConfigPath, "kubeconfig", "", "", "The path to the kubeconfig file to use. If blank, defaults to current kubeconfig.") detectApiResourceCmd.PersistentFlags().StringVarP(&namespace, "namespace", "n", "", "Only detect resources in a specific namespace.") detectApiResourceCmd.PersistentFlags().StringVar(&kubeContext, "kube-context", "", "The kube context to use. If blank, defaults to current context.") rootCmd.AddCommand(detectAllInClusterCmd) + detectAllInClusterCmd.PersistentFlags().StringVarP(&kubeConfigPath, "kubeconfig", "", "", "The path to the kubeconfig file to use. If blank, defaults to current kubeconfig.") detectAllInClusterCmd.PersistentFlags().StringVarP(&namespace, "namespace", "n", "", "Only detect resources in a specific namespace.") detectAllInClusterCmd.PersistentFlags().StringVar(&kubeContext, "kube-context", "", "The kube context to use. If blank, defaults to current context.") @@ -475,7 +479,7 @@ func Execute(VERSION string, COMMIT string, versionsFile []byte) { } func detectHelm() error { - h, err := helm.NewHelm(namespace, kubeContext, apiInstance) + h, err := helm.NewHelm(namespace, kubeContext, apiInstance, kubeConfigPath) if err != nil { return fmt.Errorf("error getting helm configuration: %v", err) } @@ -487,7 +491,7 @@ func detectHelm() error { } func detectAPIResources() error { - disCl, err := discoveryapi.NewDiscoveryClient(namespace, kubeContext, apiInstance) + disCl, err := discoveryapi.NewDiscoveryClient(namespace, kubeContext, apiInstance, kubeConfigPath) if err != nil { return fmt.Errorf("Error creating Discovery REST Client: %v", err) } diff --git a/docs/advanced.md b/docs/advanced.md index 9916465c..46a06f31 100644 --- a/docs/advanced.md +++ b/docs/advanced.md @@ -197,9 +197,9 @@ The `target-versions` field in this custom file will set the default target vers Please note that we do not allow overriding anything contained in the default `versions.yaml` that Pluto uses. -## Kube Context +## Kube Context or kubeconfig -When doing helm detection, you may want to use the `--kube-context` to specify a particular context you wish to use in your kubeconfig. +When doing helm or apiVersion detection, you may want to use the `--kube-context` or `--kubeconfig` flags to specify a particular context, or a specific file path, that you wish to use for your kubeconfig. ## Environment Variables diff --git a/pkg/discovery-api/discovery_api.go b/pkg/discovery-api/discovery_api.go index d574835a..46b656f6 100644 --- a/pkg/discovery-api/discovery_api.go +++ b/pkg/discovery-api/discovery_api.go @@ -54,13 +54,13 @@ type DiscoveryClient struct { } // NewDiscoveryClient returns a new struct with config portions complete. -func NewDiscoveryClient(namespace string, kubeContext string, instance *api.Instance) (*DiscoveryClient, error) { +func NewDiscoveryClient(namespace string, kubeContext string, instance *api.Instance, kubeConfigPath string) (*DiscoveryClient, error) { cl := &DiscoveryClient{ Instance: instance, } var err error - cl.restConfig, err = kube.GetConfig(kubeContext) + cl.restConfig, err = kube.GetConfig(kubeContext, kubeConfigPath) if err != nil { return nil, err } diff --git a/pkg/helm/helm.go b/pkg/helm/helm.go index c0444178..80f30252 100644 --- a/pkg/helm/helm.go +++ b/pkg/helm/helm.go @@ -74,8 +74,8 @@ type ChartMeta struct { } // NewHelm returns a basic helm struct with the version of helm requested -func NewHelm(namespace string, kubeContext string, instance *api.Instance) (*Helm, error) { - config, err := kube.GetConfigInstance(kubeContext) +func NewHelm(namespace string, kubeContext string, instance *api.Instance, kubeConfigPath string) (*Helm, error) { + config, err := kube.GetConfigInstance(kubeContext, kubeConfigPath) if err != nil { return nil, err } diff --git a/pkg/kube/kube.go b/pkg/kube/kube.go index e184bf87..902fc05e 100644 --- a/pkg/kube/kube.go +++ b/pkg/kube/kube.go @@ -29,6 +29,7 @@ package kube import ( + "flag" "sync" "k8s.io/client-go/kubernetes" @@ -48,15 +49,15 @@ var kubeClient *Kube var once sync.Once // GetConfigInstance returns a Pluto Kubernetes interface based on the current configuration -func GetConfigInstance(kubeContext string) (*Kube, error) { +func GetConfigInstance(kubeContext string, kubeConfigPath string) (*Kube, error) { var err error var client kubernetes.Interface var kubeConfig *rest.Config - kubeConfig, err = GetConfig(kubeContext) - if err != nil { - return nil, err - } + kubeConfig, err = GetConfig(kubeContext, kubeConfigPath) + if err != nil { + return nil, err + } once.Do(func() { if kubeClient == nil { @@ -74,15 +75,21 @@ func GetConfigInstance(kubeContext string) (*Kube, error) { } // GetConfig returns the current kube config with a specific context -func GetConfig(kubeContext string) (*rest.Config, error) { +func GetConfig(kubeContext string, kubeConfigPath string) (*rest.Config, error) { + if kubeContext != "" { klog.V(3).Infof("using kube context: %s", kubeContext) } + fs := flag.NewFlagSet("fs", flag.ContinueOnError) + fs.String("kubeconfig", kubeConfigPath, "") + config.RegisterFlags(fs) + kubeConfig, err := config.GetConfigWithContext(kubeContext) if err != nil { return nil, err } + return kubeConfig, nil } diff --git a/pkg/kube/kube_test.go b/pkg/kube/kube_test.go index 7acc514f..9cbb42f9 100644 --- a/pkg/kube/kube_test.go +++ b/pkg/kube/kube_test.go @@ -24,34 +24,59 @@ import ( func Test_getKubeClient(t *testing.T) { tests := []struct { - name string - kubeContext string - kubeConfig string - wantErr bool + name string + kubeContext string + kubeConfig string + kubeConfigPath string + wantErr bool }{ { - name: "context does not exist", - kubeContext: "farglebargle", - kubeConfig: "testdata/kubeconfig", - wantErr: true, + name: "context does not exist", + kubeContext: "farglebargle", + kubeConfig: "testdata/kubeconfig", + kubeConfigPath: "", + wantErr: true, }, { - name: "context exists", - kubeContext: "kind-kind", - kubeConfig: "testdata/kubeconfig", - wantErr: false, + name: "context exists", + kubeContext: "kind-kind", + kubeConfig: "testdata/kubeconfig", + kubeConfigPath: "", + wantErr: false, }, { - name: "invalid kubeconfig", - kubeContext: "kind-kind", - kubeConfig: "testdata/kubeconfig_invalid", - wantErr: true, + name: "invalid kubeconfig", + kubeContext: "kind-kind", + kubeConfig: "testdata/kubeconfig_invalid", + kubeConfigPath: "", + wantErr: true, + }, + { + name: "invalid kubeconfig", + kubeContext: "kind-kind", + kubeConfig: "testdata/kubeconfig_invalid", + kubeConfigPath: "", + wantErr: true, + }, + { + name: "valid kubeconfig path", + kubeContext: "kind-kind", + kubeConfig: "testdata/kubeconfig_invalid", + kubeConfigPath: "testdata/kubeconfig", + wantErr: false, + }, + { + name: "invalid kubeconfig path", + kubeContext: "kind-kind", + kubeConfig: "testdata/kubeconfig", + kubeConfigPath: "testdata/kubeconfig_invalid", + wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { os.Setenv("KUBECONFIG", tt.kubeConfig) - _, err := GetConfig(tt.kubeContext) + _, err := GetConfig(tt.kubeContext, tt.kubeConfigPath) if tt.wantErr { assert.Error(t, err) } else {