From ab78d3dbd03aa41c34182fbc8df57cc0fadbb16b Mon Sep 17 00:00:00 2001 From: Yonah Dissen <47282577+yonahd@users.noreply.github.com> Date: Tue, 1 Aug 2023 10:43:43 +0300 Subject: [PATCH] Support json response and kubeconfig flag (#13) * Support JSON return in roles command * Support JSON return in cms command * Support JSON return in all command * Support JSON return in all commands * Small fix Readme * Reformat JSON response * Support optional kubeconfig flag --------- Co-authored-by: Yonah Dissen --- README.md | 37 +++++++++++++++++++++-- cmd/kor/all.go | 8 ++++- cmd/kor/configmaps.go | 8 ++++- cmd/kor/deployments.go | 8 ++++- cmd/kor/roles.go | 9 ++++-- cmd/kor/root.go | 3 +- cmd/kor/secrets.go | 8 ++++- cmd/kor/serviceaccounts.go | 8 ++++- cmd/kor/services.go | 8 ++++- cmd/kor/statefulsets.go | 8 ++++- pkg/kor/all.go | 62 ++++++++++++++++++++++++++++++++++++-- pkg/kor/confimgmaps.go | 32 ++++++++++++++++++-- pkg/kor/deployments.go | 32 ++++++++++++++++++-- pkg/kor/kor.go | 7 +++-- pkg/kor/roles.go | 33 ++++++++++++++++++-- pkg/kor/secrets.go | 32 ++++++++++++++++++-- pkg/kor/serviceaccounts.go | 32 ++++++++++++++++++-- pkg/kor/services.go | 32 ++++++++++++++++++-- pkg/kor/statefulsets.go | 32 ++++++++++++++++++-- 19 files changed, 367 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index d1efd0e4..895ca5ba 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Kor - Kubernetes Orphaned Resources Finder -Kor is a CLI tool to discover unused Kubernetes resources. Currently, Kor can identify and list unused: +Kor is a tool to discover unused Kubernetes resources. Currently, Kor can identify and list unused: - ConfigMaps - Secrets. - Services @@ -28,6 +28,14 @@ Kor provides various subcommands to identify and list unused resources. The avai - `statefulsets`: Gets unused service accounts for the specified namespace or all namespaces. - `role`: Gets unused roles for the specified namespace or all namespaces. +### Supported Flags +``` +-h, --help help for role +-k, --kubeconfig string Path to kubeconfig file (optional) +-n, --namespace string Namespace to run on +--output string Output format (table or json) (default "table") +``` + To use a specific subcommand, run `kor [subcommand] [flags]`. ```sh @@ -49,10 +57,35 @@ kor [subcommand] --help | Services | Services with no endpoints | | | Deployments | Deployments with 0 Replicas | | | ServiceAccounts | ServiceAccounts unused by pods
ServiceAccounts unused by roleBinding or clusterRoleBinding | | -| Statefulsets | Statefulsets with no endpoints | | +| Statefulsets | Statefulsets with 0 Replicas | | | Roles | Roles not used in roleBinding | | +## Import Option +You can also use kor as a Go library to programmatically discover unused resources. By importing the github.com/yonahd/kor/pkg/kor package, you can call the relevant functions to retrieve unused resources. The library provides the option to get the results in JSON format by specifying the outputFormat parameter. + +```go +import ( +"github.com/yonahd/kor/pkg/kor" +) + +func main() { +namespace := "my-namespace" +outputFormat := "json" // Set to "json" for JSON output + + if outputFormat == "json" { + jsonResponse, err := kor.GetUnusedDeploymentsJSON(namespace) + if err != nil { + // Handle error + } + // Process the JSON response + // ... + } else { + kor.GetUnusedDeployments(namespace) + } +} +``` + ## Contributing diff --git a/cmd/kor/all.go b/cmd/kor/all.go index 40c86e09..dacd4b90 100644 --- a/cmd/kor/all.go +++ b/cmd/kor/all.go @@ -10,12 +10,18 @@ var allCmd = &cobra.Command{ Short: "Gets unused resources", Args: cobra.ExactArgs(0), Run: func(cmd *cobra.Command, args []string) { - kor.GetUnusedAll(namespace) + if outputFormat == "json" { + kor.GetUnusedAllJSON(namespace, kubeconfig) + } else { + kor.GetUnusedAll(namespace, kubeconfig) + } }, } func init() { + allCmd.PersistentFlags().StringVarP(&kubeconfig, "kubeconfig", "k", "", "Path to kubeconfig file (optional)") allCmd.PersistentFlags().StringVarP(&namespace, "namespace", "n", "", "Namespace to run on") + allCmd.PersistentFlags().StringVar(&outputFormat, "output", "table", "Output format (table or json)") rootCmd.AddCommand(allCmd) } diff --git a/cmd/kor/configmaps.go b/cmd/kor/configmaps.go index 490dfd84..d935e535 100644 --- a/cmd/kor/configmaps.go +++ b/cmd/kor/configmaps.go @@ -11,12 +11,18 @@ var configmapCmd = &cobra.Command{ Short: "Gets unused configmaps", Args: cobra.ExactArgs(0), Run: func(cmd *cobra.Command, args []string) { - kor.GetUnusedConfigmaps(namespace) + if outputFormat == "json" { + kor.GetUnusedConfigmapsJSON(namespace, kubeconfig) + } else { + kor.GetUnusedConfigmaps(namespace, kubeconfig) + } }, } func init() { + configmapCmd.PersistentFlags().StringVarP(&kubeconfig, "kubeconfig", "k", "", "Path to kubeconfig file (optional)") configmapCmd.PersistentFlags().StringVarP(&namespace, "namespace", "n", "", "Namespace to run on") + configmapCmd.PersistentFlags().StringVar(&outputFormat, "output", "table", "Output format (table or json)") rootCmd.AddCommand(configmapCmd) } diff --git a/cmd/kor/deployments.go b/cmd/kor/deployments.go index a1ab37fc..05e27eac 100644 --- a/cmd/kor/deployments.go +++ b/cmd/kor/deployments.go @@ -11,12 +11,18 @@ var deployCmd = &cobra.Command{ Short: "Gets unused deployments", Args: cobra.ExactArgs(0), Run: func(cmd *cobra.Command, args []string) { - kor.GetUnusedDeployments(namespace) + if outputFormat == "json" { + kor.GetUnusedDeploymentsJSON(namespace, kubeconfig) + } else { + kor.GetUnusedDeployments(namespace, kubeconfig) + } }, } func init() { + deployCmd.PersistentFlags().StringVarP(&kubeconfig, "kubeconfig", "k", "", "Path to kubeconfig file (optional)") deployCmd.PersistentFlags().StringVarP(&namespace, "namespace", "n", "", "Namespace to run on") + deployCmd.PersistentFlags().StringVar(&outputFormat, "output", "table", "Output format (table or json)") rootCmd.AddCommand(deployCmd) } diff --git a/cmd/kor/roles.go b/cmd/kor/roles.go index 415970ba..2f1200fd 100644 --- a/cmd/kor/roles.go +++ b/cmd/kor/roles.go @@ -10,12 +10,17 @@ var roleCmd = &cobra.Command{ Short: "Gets unused roles", Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { - kor.GetUnusedRoles(namespace) - + if outputFormat == "json" { + kor.GetUnusedRolesJSON(namespace, kubeconfig) + } else { + kor.GetUnusedRoles(namespace, kubeconfig) + } }, } func init() { + roleCmd.PersistentFlags().StringVarP(&kubeconfig, "kubeconfig", "k", "", "Path to kubeconfig file (optional)") roleCmd.PersistentFlags().StringVarP(&namespace, "namespace", "n", "", "Namespace to run on") + roleCmd.PersistentFlags().StringVar(&outputFormat, "output", "table", "Output format (table or json)") rootCmd.AddCommand(roleCmd) } diff --git a/cmd/kor/root.go b/cmd/kor/root.go index 84978976..acd23392 100644 --- a/cmd/kor/root.go +++ b/cmd/kor/root.go @@ -17,9 +17,10 @@ var rootCmd = &cobra.Command{ } var namespace string +var outputFormat string +var kubeconfig string func Execute() { - if err := rootCmd.Execute(); err != nil { fmt.Fprintf(os.Stderr, "Error while executing your CLI '%s'", err) os.Exit(1) diff --git a/cmd/kor/secrets.go b/cmd/kor/secrets.go index ef78f290..ef54e606 100644 --- a/cmd/kor/secrets.go +++ b/cmd/kor/secrets.go @@ -11,12 +11,18 @@ var secretCmd = &cobra.Command{ Short: "Gets unused secrets", Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { - kor.GetUnusedSecrets(namespace) + if outputFormat == "json" { + kor.GetUnusedSecretsJSON(namespace, kubeconfig) + } else { + kor.GetUnusedSecrets(namespace, kubeconfig) + } }, } func init() { + secretCmd.PersistentFlags().StringVarP(&kubeconfig, "kubeconfig", "k", "", "Path to kubeconfig file (optional)") secretCmd.PersistentFlags().StringVarP(&namespace, "namespace", "n", "", "Namespace to run on") + secretCmd.PersistentFlags().StringVar(&outputFormat, "output", "table", "Output format (table or json)") rootCmd.AddCommand(secretCmd) } diff --git a/cmd/kor/serviceaccounts.go b/cmd/kor/serviceaccounts.go index 46b25811..436ffbf8 100644 --- a/cmd/kor/serviceaccounts.go +++ b/cmd/kor/serviceaccounts.go @@ -11,12 +11,18 @@ var serviceAccountCmd = &cobra.Command{ Short: "Gets unused service accounts", Args: cobra.ExactArgs(0), Run: func(cmd *cobra.Command, args []string) { - kor.GetUnusedServiceAccounts(namespace) + if outputFormat == "json" { + kor.GetUnusedServiceAccountsJSON(namespace, kubeconfig) + } else { + kor.GetUnusedServiceAccounts(namespace, kubeconfig) + } }, } func init() { + serviceAccountCmd.PersistentFlags().StringVarP(&kubeconfig, "kubeconfig", "k", "", "Path to kubeconfig file (optional)") serviceAccountCmd.PersistentFlags().StringVarP(&namespace, "namespace", "n", "", "Namespace to run on") + serviceAccountCmd.PersistentFlags().StringVar(&outputFormat, "output", "table", "Output format (table or json)") rootCmd.AddCommand(serviceAccountCmd) } diff --git a/cmd/kor/services.go b/cmd/kor/services.go index 1ec30196..a233a296 100644 --- a/cmd/kor/services.go +++ b/cmd/kor/services.go @@ -11,12 +11,18 @@ var serviceCmd = &cobra.Command{ Short: "Gets unused services", Args: cobra.ExactArgs(0), Run: func(cmd *cobra.Command, args []string) { - kor.GetUnusedServices(namespace) + if outputFormat == "json" { + kor.GetUnusedServicesJSON(namespace, kubeconfig) + } else { + kor.GetUnusedServices(namespace, kubeconfig) + } }, } func init() { + serviceCmd.PersistentFlags().StringVarP(&kubeconfig, "kubeconfig", "k", "", "Path to kubeconfig file (optional)") serviceCmd.PersistentFlags().StringVarP(&namespace, "namespace", "n", "", "Namespace to run on") + serviceCmd.PersistentFlags().StringVar(&outputFormat, "output", "table", "Output format (table or json)") rootCmd.AddCommand(serviceCmd) } diff --git a/cmd/kor/statefulsets.go b/cmd/kor/statefulsets.go index 81f66b57..e15018b7 100644 --- a/cmd/kor/statefulsets.go +++ b/cmd/kor/statefulsets.go @@ -11,12 +11,18 @@ var stsCmd = &cobra.Command{ Short: "Gets unused statefulsets", Args: cobra.ExactArgs(0), Run: func(cmd *cobra.Command, args []string) { - kor.GetUnusedStatefulsets(namespace) + if outputFormat == "json" { + kor.GetUnusedStatefulsetsJSON(namespace, kubeconfig) + } else { + kor.GetUnusedStatefulsets(namespace, kubeconfig) + } }, } func init() { + stsCmd.PersistentFlags().StringVarP(&kubeconfig, "kubeconfig", "k", "", "Path to kubeconfig file (optional)") stsCmd.PersistentFlags().StringVarP(&namespace, "namespace", "n", "", "Namespace to run on") + stsCmd.PersistentFlags().StringVar(&outputFormat, "output", "table", "Output format (table or json)") rootCmd.AddCommand(stsCmd) } diff --git a/pkg/kor/all.go b/pkg/kor/all.go index a3134507..d2ea7fba 100644 --- a/pkg/kor/all.go +++ b/pkg/kor/all.go @@ -1,11 +1,17 @@ package kor import ( + "encoding/json" "fmt" "k8s.io/client-go/kubernetes" "os" ) +type GetUnusedResourceJSONResponse struct { + ResourceType string `json:"resourceType"` + Namespaces map[string][]string `json:"namespaces"` +} + type ResourceDiff struct { resourceType string diff []string @@ -74,12 +80,12 @@ func getUnusedRoles(kubeClient *kubernetes.Clientset, namespace string) Resource return namespaceSADiff } -func GetUnusedAll(namespace string) { +func GetUnusedAll(namespace string, kubeconfig string) { var kubeClient *kubernetes.Clientset var namespaces []string var allDiffs []ResourceDiff - kubeClient = GetKubeClient() + kubeClient = GetKubeClient(kubeconfig) namespaces = SetNamespaceList(namespace, kubeClient) for _, namespace := range namespaces { @@ -102,3 +108,55 @@ func GetUnusedAll(namespace string) { fmt.Println() } } + +func GetUnusedAllJSON(namespace string, kubeconfig string) (string, error) { + var kubeClient *kubernetes.Clientset + var namespaces []string + + kubeClient = GetKubeClient(kubeconfig) + + namespaces = SetNamespaceList(namespace, kubeClient) + + // Create the JSON response object + response := make(map[string]map[string][]string) + + for _, namespace := range namespaces { + var allDiffs []ResourceDiff + + namespaceCMDiff := getUnusedCMs(kubeClient, namespace) + allDiffs = append(allDiffs, namespaceCMDiff) + + namespaceSVCDiff := getUnusedSVCs(kubeClient, namespace) + allDiffs = append(allDiffs, namespaceSVCDiff) + + namespaceSecretDiff := getUnusedSecrets(kubeClient, namespace) + allDiffs = append(allDiffs, namespaceSecretDiff) + + namespaceSADiff := getUnusedServiceAccounts(kubeClient, namespace) + allDiffs = append(allDiffs, namespaceSADiff) + + namespaceDeploymentDiff := getUnusedDeployments(kubeClient, namespace) + allDiffs = append(allDiffs, namespaceDeploymentDiff) + + namespaceStatefulsetDiff := getUnusedStatefulsets(kubeClient, namespace) + allDiffs = append(allDiffs, namespaceStatefulsetDiff) + + namespaceRoleDiff := getUnusedRoles(kubeClient, namespace) + allDiffs = append(allDiffs, namespaceRoleDiff) + + // Store the unused resources for each resource type in the JSON response + resourceMap := make(map[string][]string) + for _, diff := range allDiffs { + resourceMap[diff.resourceType] = diff.diff + } + response[namespace] = resourceMap + } + + // Convert the response object to JSON + jsonResponse, err := json.MarshalIndent(response, "", " ") + if err != nil { + return "", err + } + + return string(jsonResponse), nil +} diff --git a/pkg/kor/confimgmaps.go b/pkg/kor/confimgmaps.go index 289f9f78..9b297585 100644 --- a/pkg/kor/confimgmaps.go +++ b/pkg/kor/confimgmaps.go @@ -2,6 +2,7 @@ package kor import ( "context" + "encoding/json" "fmt" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" @@ -121,11 +122,11 @@ func processNamespaceCM(kubeClient *kubernetes.Clientset, namespace string) ([]s } -func GetUnusedConfigmaps(namespace string) { +func GetUnusedConfigmaps(namespace string, kubeconfig string) { var kubeClient *kubernetes.Clientset var namespaces []string - kubeClient = GetKubeClient() + kubeClient = GetKubeClient(kubeconfig) namespaces = SetNamespaceList(namespace, kubeClient) @@ -140,3 +141,30 @@ func GetUnusedConfigmaps(namespace string) { fmt.Println() } } + +func GetUnusedConfigmapsJSON(namespace string, kubeconfig string) (string, error) { + var kubeClient *kubernetes.Clientset + var namespaces []string + + kubeClient = GetKubeClient(kubeconfig) + namespaces = SetNamespaceList(namespace, kubeClient) + response := make(map[string]map[string][]string) + + for _, namespace := range namespaces { + diff, err := processNamespaceCM(kubeClient, namespace) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to process namespace %s: %v\n", namespace, err) + continue + } + resourceMap := make(map[string][]string) + resourceMap["ConfigMap"] = diff + response[namespace] = resourceMap + } + + jsonResponse, err := json.MarshalIndent(response, "", " ") + if err != nil { + return "", err + } + + return string(jsonResponse), nil +} diff --git a/pkg/kor/deployments.go b/pkg/kor/deployments.go index aa454f60..d3f13a52 100644 --- a/pkg/kor/deployments.go +++ b/pkg/kor/deployments.go @@ -2,6 +2,7 @@ package kor import ( "context" + "encoding/json" "fmt" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" @@ -35,11 +36,11 @@ func ProcessNamespaceDeployments(clientset *kubernetes.Clientset, namespace stri } -func GetUnusedDeployments(namespace string) { +func GetUnusedDeployments(namespace string, kubeconfig string) { var kubeClient *kubernetes.Clientset var namespaces []string - kubeClient = GetKubeClient() + kubeClient = GetKubeClient(kubeconfig) namespaces = SetNamespaceList(namespace, kubeClient) @@ -54,3 +55,30 @@ func GetUnusedDeployments(namespace string) { fmt.Println() } } + +func GetUnusedDeploymentsJSON(namespace string, kubeconfig string) (string, error) { + var kubeClient *kubernetes.Clientset + var namespaces []string + + kubeClient = GetKubeClient(kubeconfig) + namespaces = SetNamespaceList(namespace, kubeClient) + response := make(map[string]map[string][]string) + + for _, namespace := range namespaces { + diff, err := ProcessNamespaceDeployments(kubeClient, namespace) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to process namespace %s: %v\n", namespace, err) + continue + } + resourceMap := make(map[string][]string) + resourceMap["Deployments"] = diff + response[namespace] = resourceMap + } + + jsonResponse, err := json.MarshalIndent(response, "", " ") + if err != nil { + return "", err + } + + return string(jsonResponse), nil +} diff --git a/pkg/kor/kor.go b/pkg/kor/kor.go index b51b4f2f..87ea3eba 100644 --- a/pkg/kor/kor.go +++ b/pkg/kor/kor.go @@ -37,9 +37,10 @@ func GetKubeConfigPath() string { return filepath.Join(home, ".kube", "config") } -func GetKubeClient() *kubernetes.Clientset { - var kubeconfig string - kubeconfig = GetKubeConfigPath() +func GetKubeClient(kubeconfig string) *kubernetes.Clientset { + if kubeconfig == "" { + kubeconfig = GetKubeConfigPath() + } config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) if err != nil { fmt.Fprintf(os.Stderr, "Failed to load kubeconfig: %v\n", err) diff --git a/pkg/kor/roles.go b/pkg/kor/roles.go index 6c4f2649..e13ea31e 100644 --- a/pkg/kor/roles.go +++ b/pkg/kor/roles.go @@ -2,6 +2,7 @@ package kor import ( "context" + "encoding/json" "fmt" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" @@ -82,12 +83,11 @@ func processNamespaceRoles(kubeClient *kubernetes.Clientset, namespace string) ( } -func GetUnusedRoles(namespace string) { +func GetUnusedRoles(namespace string, kubeconfig string) { var kubeClient *kubernetes.Clientset var namespaces []string - kubeClient = GetKubeClient() - + kubeClient = GetKubeClient(kubeconfig) namespaces = SetNamespaceList(namespace, kubeClient) for _, namespace := range namespaces { @@ -101,3 +101,30 @@ func GetUnusedRoles(namespace string) { fmt.Println() } } + +func GetUnusedRolesJSON(namespace string, kubeconfig string) (string, error) { + var kubeClient *kubernetes.Clientset + var namespaces []string + + kubeClient = GetKubeClient(kubeconfig) + namespaces = SetNamespaceList(namespace, kubeClient) + response := make(map[string]map[string][]string) + + for _, namespace := range namespaces { + diff, err := processNamespaceRoles(kubeClient, namespace) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to process namespace %s: %v\n", namespace, err) + continue + } + resourceMap := make(map[string][]string) + resourceMap["Roles"] = diff + response[namespace] = resourceMap + } + + jsonResponse, err := json.MarshalIndent(response, "", " ") + if err != nil { + return "", err + } + + return string(jsonResponse), nil +} diff --git a/pkg/kor/secrets.go b/pkg/kor/secrets.go index 857d2ef6..18919e06 100644 --- a/pkg/kor/secrets.go +++ b/pkg/kor/secrets.go @@ -2,6 +2,7 @@ package kor import ( "context" + "encoding/json" "fmt" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" @@ -150,11 +151,11 @@ func processNamespaceSecret(kubeClient *kubernetes.Clientset, namespace string) } -func GetUnusedSecrets(namespace string) { +func GetUnusedSecrets(namespace string, kubeconfig string) { var kubeClient *kubernetes.Clientset var namespaces []string - kubeClient = GetKubeClient() + kubeClient = GetKubeClient(kubeconfig) namespaces = SetNamespaceList(namespace, kubeClient) @@ -169,3 +170,30 @@ func GetUnusedSecrets(namespace string) { fmt.Println() } } + +func GetUnusedSecretsJSON(namespace string, kubeconfig string) (string, error) { + var kubeClient *kubernetes.Clientset + var namespaces []string + + kubeClient = GetKubeClient(kubeconfig) + namespaces = SetNamespaceList(namespace, kubeClient) + response := make(map[string]map[string][]string) + + for _, namespace := range namespaces { + diff, err := processNamespaceSecret(kubeClient, namespace) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to process namespace %s: %v\n", namespace, err) + continue + } + resourceMap := make(map[string][]string) + resourceMap["Secrets"] = diff + response[namespace] = resourceMap + } + + jsonResponse, err := json.MarshalIndent(response, "", " ") + if err != nil { + return "", err + } + + return string(jsonResponse), nil +} diff --git a/pkg/kor/serviceaccounts.go b/pkg/kor/serviceaccounts.go index d2d1d6a5..5a148b55 100644 --- a/pkg/kor/serviceaccounts.go +++ b/pkg/kor/serviceaccounts.go @@ -2,6 +2,7 @@ package kor import ( "context" + "encoding/json" "fmt" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" @@ -118,11 +119,11 @@ func processNamespaceSA(kubeClient *kubernetes.Clientset, namespace string) ([]s } -func GetUnusedServiceAccounts(namespace string) { +func GetUnusedServiceAccounts(namespace string, kubeconfig string) { var kubeClient *kubernetes.Clientset var namespaces []string - kubeClient = GetKubeClient() + kubeClient = GetKubeClient(kubeconfig) namespaces = SetNamespaceList(namespace, kubeClient) @@ -137,3 +138,30 @@ func GetUnusedServiceAccounts(namespace string) { fmt.Println() } } + +func GetUnusedServiceAccountsJSON(namespace string, kubeconfig string) (string, error) { + var kubeClient *kubernetes.Clientset + var namespaces []string + + kubeClient = GetKubeClient(kubeconfig) + namespaces = SetNamespaceList(namespace, kubeClient) + response := make(map[string]map[string][]string) + + for _, namespace := range namespaces { + diff, err := processNamespaceSA(kubeClient, namespace) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to process namespace %s: %v\n", namespace, err) + continue + } + resourceMap := make(map[string][]string) + resourceMap["ServiceAccounts"] = diff + response[namespace] = resourceMap + } + + jsonResponse, err := json.MarshalIndent(response, "", " ") + if err != nil { + return "", err + } + + return string(jsonResponse), nil +} diff --git a/pkg/kor/services.go b/pkg/kor/services.go index adb51ec1..3db1e837 100644 --- a/pkg/kor/services.go +++ b/pkg/kor/services.go @@ -2,6 +2,7 @@ package kor import ( "context" + "encoding/json" "fmt" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" @@ -35,11 +36,11 @@ func ProcessNamespaceServices(clientset *kubernetes.Clientset, namespace string) } -func GetUnusedServices(namespace string) { +func GetUnusedServices(namespace string, kubeconfig string) { var kubeClient *kubernetes.Clientset var namespaces []string - kubeClient = GetKubeClient() + kubeClient = GetKubeClient(kubeconfig) namespaces = SetNamespaceList(namespace, kubeClient) @@ -54,3 +55,30 @@ func GetUnusedServices(namespace string) { fmt.Println() } } + +func GetUnusedServicesJSON(namespace string, kubeconfig string) (string, error) { + var kubeClient *kubernetes.Clientset + var namespaces []string + + kubeClient = GetKubeClient(kubeconfig) + namespaces = SetNamespaceList(namespace, kubeClient) + response := make(map[string]map[string][]string) + + for _, namespace := range namespaces { + diff, err := ProcessNamespaceServices(kubeClient, namespace) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to process namespace %s: %v\n", namespace, err) + continue + } + resourceMap := make(map[string][]string) + resourceMap["Services"] = diff + response[namespace] = resourceMap + } + + jsonResponse, err := json.MarshalIndent(response, "", " ") + if err != nil { + return "", err + } + + return string(jsonResponse), nil +} diff --git a/pkg/kor/statefulsets.go b/pkg/kor/statefulsets.go index b9c38008..ce763357 100644 --- a/pkg/kor/statefulsets.go +++ b/pkg/kor/statefulsets.go @@ -2,6 +2,7 @@ package kor import ( "context" + "encoding/json" "fmt" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" @@ -35,11 +36,11 @@ func ProcessNamespaceStatefulsets(clientset *kubernetes.Clientset, namespace str } -func GetUnusedStatefulsets(namespace string) { +func GetUnusedStatefulsets(namespace string, kubeconfig string) { var kubeClient *kubernetes.Clientset var namespaces []string - kubeClient = GetKubeClient() + kubeClient = GetKubeClient(kubeconfig) namespaces = SetNamespaceList(namespace, kubeClient) @@ -54,3 +55,30 @@ func GetUnusedStatefulsets(namespace string) { fmt.Println() } } + +func GetUnusedStatefulsetsJSON(namespace string, kubeconfig string) (string, error) { + var kubeClient *kubernetes.Clientset + var namespaces []string + + kubeClient = GetKubeClient(kubeconfig) + namespaces = SetNamespaceList(namespace, kubeClient) + response := make(map[string]map[string][]string) + + for _, namespace := range namespaces { + diff, err := ProcessNamespaceStatefulsets(kubeClient, namespace) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to process namespace %s: %v\n", namespace, err) + continue + } + resourceMap := make(map[string][]string) + resourceMap["Statefulsets"] = diff + response[namespace] = resourceMap + } + + jsonResponse, err := json.MarshalIndent(response, "", " ") + if err != nil { + return "", err + } + + return string(jsonResponse), nil +}