Skip to content

Commit

Permalink
feat: add namespace and context to API Resources method (#410)
Browse files Browse the repository at this point in the history
* feat: Add namespace and context to API Resources method

Signed-off-by: GitHub <noreply@github.com>

* Update root.go

* Update helm.go

---------

Signed-off-by: GitHub <noreply@github.com>
Co-authored-by: Robert Brennan <accounts@rbren.io>
Co-authored-by: Stevie <4719798+transient1@users.noreply.github.com>
  • Loading branch information
3 people authored Feb 3, 2023
1 parent 4c80d3a commit 89514e7
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 92 deletions.
60 changes: 29 additions & 31 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,13 @@ func init() {
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(&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(listVersionsCmd)
rootCmd.AddCommand(detectCmd)

rootCmd.AddCommand(detectApiResourceCmd)

klog.InitFlags(nil)
pflag.CommandLine.AddGoFlag(flag.CommandLine.Lookup("v"))
}
Expand Down Expand Up @@ -286,14 +288,12 @@ var detectFilesCmd = &cobra.Command{
Short: "detect-files",
Long: `Detect Kubernetes apiVersions in a directory.`,
Run: func(cmd *cobra.Command, args []string) {

dir := finder.NewFinder(directory, apiInstance)
err := dir.FindVersions()
if err != nil {
fmt.Println("Error running finder:", err)
os.Exit(1)
}

err = apiInstance.DisplayOutput()
if err != nil {
fmt.Println("Error Parsing Output:", err)
Expand All @@ -319,7 +319,32 @@ var detectHelmCmd = &cobra.Command{
fmt.Println("Error running helm-detect:", err)
os.Exit(1)
}
err = apiInstance.DisplayOutput()
if err != nil {
fmt.Println("Error Parsing Output:", err)
os.Exit(1)
}
retCode := apiInstance.GetReturnCode()
klog.V(5).Infof("retCode: %d", retCode)
os.Exit(retCode)
},
}

var detectApiResourceCmd = &cobra.Command{
Use: "detect-api-resources",
Short: "detect-api-resources",
Long: `Detect Kubernetes apiVersions from an active cluster (using last-applied-configuration annotation)`,
Run: func(cmd *cobra.Command, args []string) {
disCl, err := discoveryapi.NewDiscoveryClient(namespace, kubeContext, apiInstance)
if err != nil {
fmt.Println("Error creating Discovery REST Client: ", err)
os.Exit(1)
}
err = disCl.GetApiResources()
if err != nil {
fmt.Println("Error getting API resources using discovery client:", err)
os.Exit(1)
}
err = apiInstance.DisplayOutput()
if err != nil {
fmt.Println("Error Parsing Output:", err)
Expand Down Expand Up @@ -403,33 +428,6 @@ var listVersionsCmd = &cobra.Command{
},
}

var detectApiResourceCmd = &cobra.Command{
Use: "detect-api-resources",
Short: "detect-api-resources",
Long: `Detect Kubernetes apiVersions from an active cluster.`,
Run: func(cmd *cobra.Command, args []string) {

disCl, err := discoveryapi.NewDiscoveryClient(apiInstance)
if err != nil {
fmt.Println("Error creating Discovery REST Client: ", err)
os.Exit(1)
}
err = disCl.GetApiResources()
if err != nil {
fmt.Println("Error getting API resources using discovery client:", err)
os.Exit(1)
}

err = apiInstance.DisplayOutput()
if err != nil {
fmt.Println("Error Parsing Output:", err)
os.Exit(1)
}
exitCode = apiInstance.GetReturnCode()
klog.V(5).Infof("retCode: %d", exitCode)
},
}

// Execute the stuff
func Execute(VERSION string, COMMIT string, versionsFile []byte) {
version = VERSION
Expand Down
48 changes: 14 additions & 34 deletions pkg/discovery-api/discovery_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ import (
"k8s.io/client-go/discovery"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/klog/v2"

"github.com/fairwindsops/pluto/v5/pkg/api"
kube "github.com/fairwindsops/pluto/v5/pkg/kube"
)

// DiscoveryClient is the declaration to hold objects needed for client-go/discovery.
Expand All @@ -50,16 +50,17 @@ type DiscoveryClient struct {
restConfig *rest.Config
DiscoveryClient discovery.DiscoveryInterface
Instance *api.Instance
namespace string
}

// NewDiscoveryClient returns a new struct with config portions complete.
func NewDiscoveryClient(instance *api.Instance) (*DiscoveryClient, error) {
func NewDiscoveryClient(namespace string, kubeContext string, instance *api.Instance) (*DiscoveryClient, error) {
cl := &DiscoveryClient{
Instance: instance,
}

var err error
cl.restConfig, err = NewRestClientConfig(rest.InClusterConfig)
cl.ClientSet, cl.restConfig, err = kube.GetKubeDynamicClient(kubeContext)
if err != nil {
return nil, err
}
Expand All @@ -68,41 +69,13 @@ func NewDiscoveryClient(instance *api.Instance) (*DiscoveryClient, error) {
return nil, err
}

cl.ClientSet, err = dynamic.NewForConfig(cl.restConfig)
if err != nil {
return nil, err
}
return cl, nil
}

// NewRestClientConfig returns a new Rest Client config portions complete.
func NewRestClientConfig(inClusterFn func() (*rest.Config, error)) (*rest.Config, error) {
cl.namespace = namespace

if restConfig, err := inClusterFn(); err == nil {
return restConfig, nil
}

pathOptions := clientcmd.NewDefaultPathOptions()

config, err := pathOptions.GetStartingConfig()
if err != nil {
return nil, err
}

configOverrides := clientcmd.ConfigOverrides{}

clientConfig := clientcmd.NewDefaultClientConfig(*config, &configOverrides)
restConfig, err := clientConfig.ClientConfig()
if err != nil {
return nil, err
}

return restConfig, nil
return cl, nil
}

// GetApiResources discovers the api-resources for a cluster
func (cl *DiscoveryClient) GetApiResources() error {

resourcelist, err := cl.DiscoveryClient.ServerPreferredResources()
if err != nil {
if apierrors.IsNotFound(err) {
Expand All @@ -117,6 +90,9 @@ func (cl *DiscoveryClient) GetApiResources() error {
gvrs := []schema.GroupVersionResource{}
for _, rl := range resourcelist {
for i := range rl.APIResources {
if cl.namespace != "" && !rl.APIResources[i].Namespaced {
continue
}
gv, _ := schema.ParseGroupVersion(rl.GroupVersion)
ResourceName := rl.APIResources[i].Name
g := schema.GroupVersionResource{Group: gv.Group, Version: gv.Version, Resource: ResourceName}
Expand All @@ -126,7 +102,11 @@ func (cl *DiscoveryClient) GetApiResources() error {

var results []map[string]interface{}
for _, g := range gvrs {
ri := cl.ClientSet.Resource(g)
nri := cl.ClientSet.Resource(g)
var ri dynamic.ResourceInterface = nri
if cl.namespace != "" {
ri = nri.Namespace(cl.namespace)
}
klog.V(2).Infof("Retrieving : %s.%s.%s", g.Resource, g.Version, g.Group)
rs, err := ri.List(context.TODO(), metav1.ListOptions{})
if err != nil {
Expand Down
11 changes: 6 additions & 5 deletions pkg/helm/helm.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,13 @@ import (
"k8s.io/klog/v2"

"github.com/fairwindsops/pluto/v5/pkg/api"
kube "github.com/fairwindsops/pluto/v5/pkg/kube"
)

// Helm represents all current releases that we can find in the cluster
type Helm struct {
Releases []*Release
Kube *kube
Kube *kube.Kube
Namespace string
Instance *api.Instance
}
Expand All @@ -73,8 +74,8 @@ type ChartMeta struct {
}

// NewHelm returns a basic helm struct with the version of helm requested
func NewHelm(namespace, kubeContext string, instance *api.Instance) (*Helm, error) {
config, err := getConfigInstance(kubeContext)
func NewHelm(namespace string, kubeContext string, instance *api.Instance) (*Helm, error) {
config, err := kube.GetConfigInstance(kubeContext)
if err != nil {
return nil, err
}
Expand All @@ -88,9 +89,9 @@ func NewHelm(namespace, kubeContext string, instance *api.Instance) (*Helm, erro

// NewHelmWithKubeClient returns a helm struct with version of helm requested
// and uses the passed in kube client as the cluster to operate on
func NewHelmWithKubeClient(version, store, namespace string, instance *api.Instance, kubeClient kubernetes.Interface) *Helm {
func NewHelmWithKubeClient(version string, store string, namespace string, instance *api.Instance, kubeClient kubernetes.Interface) *Helm {
return &Helm{
Kube: &kube{
Kube: &kube.Kube{
Client: kubeClient,
},
Namespace: namespace,
Expand Down
14 changes: 12 additions & 2 deletions pkg/helm/helm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,13 @@ import (
"testing"

"github.com/fairwindsops/pluto/v5/pkg/api"
kube "github.com/fairwindsops/pluto/v5/pkg/kube"
"github.com/stretchr/testify/assert"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
testclient "k8s.io/client-go/kubernetes/fake"
_ "k8s.io/client-go/plugin/pkg/client/auth"
"k8s.io/client-go/rest"
)

Expand Down Expand Up @@ -150,6 +153,13 @@ var (
}
)

func getMockConfigInstance() *kube.Kube {
kubeClient := &kube.Kube{
Client: testclient.NewSimpleClientset(),
}
return kubeClient
}

func newMockHelm(namespace string) *Helm {
return &Helm{
Namespace: namespace,
Expand Down Expand Up @@ -185,10 +195,10 @@ func newMockHelm(namespace string) *Helm {
}
}

func newBadKubeClient() (k *kube) {
func newBadKubeClient() (k *kube.Kube) {
conf := new(rest.Config)
conf.Host = "127.0.0.1:9999"
k = new(kube)
k = new(kube.Kube)
k.Client, _ = kubernetes.NewForConfig(conf)
return
}
Expand Down
41 changes: 31 additions & 10 deletions pkg/helm/kube.go → pkg/kube/kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,31 +31,33 @@ package helm
import (
"sync"

"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/klog/v2"

// This is required to auth to cloud providers (i.e. GKE)
_ "k8s.io/client-go/plugin/pkg/client/auth"
"sigs.k8s.io/controller-runtime/pkg/client/config"
)

type kube struct {
type Kube struct {
Client kubernetes.Interface
}

var kubeClient *kube
var kubeClient *Kube
var once sync.Once

// GetConfigInstance returns a Kubernetes interface based on the current configuration
func getConfigInstance(kubeContext string) (*kube, error) {
// GetConfigInstance returns a Pluto Kubernetes interface based on the current configuration
func GetConfigInstance(kubeContext string) (*Kube, error) {
var err error
var client kubernetes.Interface

once.Do(func() {
if kubeClient == nil {
client, err = getKubeClient(kubeContext)
client, err = GetKubeClient(kubeContext)

kubeClient = &kube{
kubeClient = &Kube{
Client: client,
}
}
Expand All @@ -66,19 +68,38 @@ func getConfigInstance(kubeContext string) (*kube, error) {
return kubeClient, nil
}

func getKubeClient(kubeContext string) (kubernetes.Interface, error) {
// GetKubeClient returns a Kubernetes.Interface based on the current configuration
func GetKubeClient(kubeContext string) (kubernetes.Interface, error) {
if kubeContext != "" {
klog.V(3).Infof("using kube context: %s", kubeContext)
}

kubeConfig, err := config.GetConfigWithContext(kubeContext)

config, err := config.GetConfigWithContext(kubeContext)
if err != nil {
return nil, err
}
clientset, err := kubernetes.NewForConfig(kubeConfig)

clientset, err := kubernetes.NewForConfig(config)
if err != nil {
return nil, err
}
return clientset, nil
}

// GetKubeDynamicClient returns a dynamic.Interface, rest.Config based on the current configuration
func GetKubeDynamicClient(kubeContext string) (dynamic.Interface, *rest.Config, error) {
if kubeContext != "" {
klog.V(3).Infof("using kube context: %s", kubeContext)
}

config, err := config.GetConfigWithContext(kubeContext)
if err != nil {
return nil, nil, err
}

clientset, err := dynamic.NewForConfig(config)
if err != nil {
return nil, nil, err
}
return clientset, config, nil
}
11 changes: 1 addition & 10 deletions pkg/helm/kube_test.go → pkg/kube/kube_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,8 @@ import (
"testing"

"github.com/stretchr/testify/assert"
testclient "k8s.io/client-go/kubernetes/fake"
_ "k8s.io/client-go/plugin/pkg/client/auth"
)

func getMockConfigInstance() *kube {
kubeClient = &kube{
Client: testclient.NewSimpleClientset(),
}
return kubeClient
}

func Test_getKubeClient(t *testing.T) {
tests := []struct {
name string
Expand Down Expand Up @@ -59,7 +50,7 @@ func Test_getKubeClient(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
os.Setenv("KUBECONFIG", tt.kubeConfig)
_, err := getKubeClient(tt.kubeContext)
_, err := GetKubeClient(tt.kubeContext)
if tt.wantErr {
assert.Error(t, err)
} else {
Expand Down
File renamed without changes.
File renamed without changes.

0 comments on commit 89514e7

Please sign in to comment.