diff --git a/README.md b/README.md
index 31aec8e9..3432a392 100644
--- a/README.md
+++ b/README.md
@@ -125,6 +125,7 @@ Kor provides various subcommands to identify and list unused resources. The avai
- `daemonset`- Gets unused DaemonSets for the specified namespace or all namespaces.
- `finalizer` - Gets unused pending deletion resources for the specified namespace or all namespaces.
- `networkpolicy` - Gets unused NetworkPolicies for the specified namespace or all namespaces.
+- `argorollouts` - Gets unused Argo Rolouts for the specified namespace or all namespaces.
- `exporter` - Export Prometheus metrics.
- `version` - Print kor version information.
@@ -169,7 +170,7 @@ kor [subcommand] --help
| ConfigMaps | ConfigMaps not used in the following places:
- Pods
- Containers
- ConfigMaps used through Volumes
- ConfigMaps used through environment variables | ConfigMaps used by resources which don't explicitly state them in the config.
e.g Grafana dashboards loaded dynamically OPA policies fluentd configs CRD configs |
| Secrets | Secrets not used in the following places:
- Pods
- Containers
- Secrets used through volumes
- Secrets used through environment variables
- Secrets used by Ingress TLS
- Secrets used by ServiceAccounts | Secrets used by resources which don't explicitly state them in the config e.g. secrets used by CRDs |
| Services | Services with no endpoints | |
-| Deployments | Deployments with no Replicas | |
+| Deployments | Deployments with no Replicas and non existent ArgoRollout Owner (WorkloadRef) | |
| ServiceAccounts | ServiceAccounts unused by Pods
ServiceAccounts unused by roleBinding or clusterRoleBinding | |
| StatefulSets | Statefulsets with no Replicas | |
| Roles | Roles not used in roleBinding | |
@@ -181,11 +182,12 @@ kor [subcommand] --help
| CRDs | CRDs not used the cluster | |
| Pvs | PVs not bound to a PVC | |
| Pdbs | PDBs not used in Deployments
PDBs not used in StatefulSets | |
-| Jobs | Jobs status is completed
Jobs status is suspended
Jobs failed with backoff limit exceeded (including indexed jobs)
Jobs failed with dedaline exceeded | |
-| ReplicaSets | replicaSets that specify replicas to 0 and has already completed it's work |
-| DaemonSets | DaemonSets not scheduled on any nodes |
-| StorageClasses | StorageClasses not used by any PVs/PVCs |
-| NetworkPolicies | NetworkPolicies with no Pods selected by podSelector or Ingress/Egress rules |
+| Jobs | Jobs status is completed
Jobs status is suspended
Jobs failed with backoff limit exceeded (including indexed jobs)
Jobs failed with dedaline exceeded | |
+| ReplicaSets | replicaSets that specify replicas to 0 and has already completed it's work | |
+| DaemonSets | DaemonSets not scheduled on any nodes | |
+| StorageClasses | StorageClasses not used by any PVs/PVCs | |
+| NetworkPolicies | NetworkPolicies with no Pods selected by podSelector or Ingress/Egress rules | |
+| ArgoRollout | Argo Rollouts with WorkloadRef deployment non existent in cluster | When deployment from Argo Rollouts has deleted, replicasets keep pods alive, attention to this |
### Deleting Unused resources
@@ -250,6 +252,7 @@ Unused resources in namespace: "test"
| 6 | ConfigMap | unused-cm | ConfigMap is not used in any pod or container |
| 7 | ServiceAccount | my-service-account2 | ServiceAccount is not in use |
| 8 | Pdb | my-pdb | Pdb is not referencing any deployments or statefulsets |
+| 9 | ArgoRollout | rollout-ref-deployment | Rollout has no deployments |
+---+----------------+----------------------------------------------+--------------------------------------------------------+
```
@@ -299,13 +302,14 @@ Unused resources in namespace: "ns1"
| 4 | Deployment | deploy1 |
+---+---------------+--------------------+
Unused resources in namespace: "ns2"
-+---+---------------+--------------------+
-| # | RESOURCE TYPE | RESOURCE NAME |
-+---+---------------+--------------------+
-| 1 | ReplicaSet | deploy2-79f48888c6 |
-| 2 | ConfigMap | cm3 |
-| 3 | Deployment | deploy2 |
-+---+---------------+--------------------+
++---+---------------+-------------------------+
+| # | RESOURCE TYPE | RESOURCE NAME |
++---+---------------+-------------------------+
+| 1 | ReplicaSet | deploy2-79f48888c6 |
+| 2 | ConfigMap | cm3 |
+| 3 | Deployment | deploy2 |
+| 4 | ArgoRollout | rollout-ref-deployment |
++---+---------------+-------------------------+
```
## In Cluster Usage
diff --git a/charts/kor/templates/role.yaml b/charts/kor/templates/role.yaml
index a1d73f12..0cb46b22 100644
--- a/charts/kor/templates/role.yaml
+++ b/charts/kor/templates/role.yaml
@@ -26,6 +26,7 @@ rules:
- replicasets
- daemonsets
- networkpolicies
+ - rollouts
verbs:
- get
- list
@@ -58,6 +59,7 @@ rules:
- replicasets
- daemonsets
- networkpolicies
+ - rollouts
{{/* cluster-scoped resources */}}
- namespaces
- clusterroles
diff --git a/cmd/kor/all.go b/cmd/kor/all.go
index 3478c9be..ac12a7a4 100644
--- a/cmd/kor/all.go
+++ b/cmd/kor/all.go
@@ -15,10 +15,11 @@ var allCmd = &cobra.Command{
Args: cobra.ExactArgs(0),
Run: func(cmd *cobra.Command, args []string) {
clientset := kor.GetKubeClient(kubeconfig)
+ clientsetargorollouts := kor.GetKubeClientArgoRollouts(kubeconfig)
apiExtClient := kor.GetAPIExtensionsClient(kubeconfig)
dynamicClient := kor.GetDynamicClient(kubeconfig)
- if response, err := kor.GetUnusedAll(filterOptions, clientset, apiExtClient, dynamicClient, outputFormat, opts); err != nil {
+ if response, err := kor.GetUnusedAll(filterOptions, clientset, clientsetargorollouts, apiExtClient, dynamicClient, outputFormat, opts); err != nil {
fmt.Println(err)
} else {
utils.PrintLogo(outputFormat)
diff --git a/cmd/kor/argorollouts.go b/cmd/kor/argorollouts.go
new file mode 100644
index 00000000..14f84682
--- /dev/null
+++ b/cmd/kor/argorollouts.go
@@ -0,0 +1,31 @@
+package kor
+
+import (
+ "fmt"
+
+ "github.com/spf13/cobra"
+
+ "github.com/yonahd/kor/pkg/kor"
+ "github.com/yonahd/kor/pkg/utils"
+)
+
+var argoRolloutsCmd = &cobra.Command{
+ Use: "argorollouts",
+ Aliases: []string{"argorollouts"},
+ Short: "Gets unused argo rollouts",
+ Args: cobra.ExactArgs(0),
+ Run: func(cmd *cobra.Command, args []string) {
+ clientset := kor.GetKubeClient(kubeconfig)
+ clientsetargorollouts := kor.GetKubeClientArgoRollouts(kubeconfig)
+ if response, err := kor.GetUnusedArgoRollouts(filterOptions, clientset, clientsetargorollouts, outputFormat, opts); err != nil {
+ fmt.Println(err)
+ } else {
+ utils.PrintLogo(outputFormat)
+ fmt.Println(response)
+ }
+ },
+}
+
+func init() {
+ rootCmd.AddCommand(argoRolloutsCmd)
+}
diff --git a/cmd/kor/deployments.go b/cmd/kor/deployments.go
index 4eb16d6d..621dfaab 100644
--- a/cmd/kor/deployments.go
+++ b/cmd/kor/deployments.go
@@ -16,7 +16,8 @@ var deployCmd = &cobra.Command{
Args: cobra.ExactArgs(0),
Run: func(cmd *cobra.Command, args []string) {
clientset := kor.GetKubeClient(kubeconfig)
- if response, err := kor.GetUnusedDeployments(filterOptions, clientset, outputFormat, opts); err != nil {
+ clientsetargorollouts := kor.GetKubeClientArgoRollouts(kubeconfig)
+ if response, err := kor.GetUnusedDeployments(filterOptions, clientset, clientsetargorollouts, outputFormat, opts); err != nil {
fmt.Println(err)
} else {
utils.PrintLogo(outputFormat)
diff --git a/cmd/kor/exporter.go b/cmd/kor/exporter.go
index bb245124..d053a108 100644
--- a/cmd/kor/exporter.go
+++ b/cmd/kor/exporter.go
@@ -14,10 +14,11 @@ var exporterCmd = &cobra.Command{
Args: cobra.ExactArgs(0),
Run: func(cmd *cobra.Command, args []string) {
clientset := kor.GetKubeClient(kubeconfig)
+ clientsetargorollouts := kor.GetKubeClientArgoRollouts(kubeconfig)
apiExtClient := kor.GetAPIExtensionsClient(kubeconfig)
dynamicClient := kor.GetDynamicClient(kubeconfig)
- kor.Exporter(filterOptions, clientset, apiExtClient, dynamicClient, "json", opts, resourceList)
+ kor.Exporter(filterOptions, clientset, clientsetargorollouts, apiExtClient, dynamicClient, "json", opts, resourceList)
},
}
diff --git a/cmd/kor/root.go b/cmd/kor/root.go
index 17c818e9..bc139407 100644
--- a/cmd/kor/root.go
+++ b/cmd/kor/root.go
@@ -32,10 +32,11 @@ var rootCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
resourceNames := args[0]
clientset := kor.GetKubeClient(kubeconfig)
+ clientsetargorollouts := kor.GetKubeClientArgoRollouts(kubeconfig)
apiExtClient := kor.GetAPIExtensionsClient(kubeconfig)
dynamicClient := kor.GetDynamicClient(kubeconfig)
- if response, err := kor.GetUnusedMulti(resourceNames, filterOptions, clientset, apiExtClient, dynamicClient, outputFormat, opts); err != nil {
+ if response, err := kor.GetUnusedMulti(resourceNames, filterOptions, clientset, clientsetargorollouts, apiExtClient, dynamicClient, outputFormat, opts); err != nil {
fmt.Println(err)
} else {
utils.PrintLogo(outputFormat)
diff --git a/go.mod b/go.mod
index 37bc1300..88832649 100644
--- a/go.mod
+++ b/go.mod
@@ -5,6 +5,7 @@ go 1.22.0
toolchain go1.22.2
require (
+ github.com/argoproj/argo-rollouts v1.7.2
github.com/fatih/color v1.17.0
github.com/olekukonko/tablewriter v0.0.5
github.com/prometheus/client_golang v1.20.4
diff --git a/go.sum b/go.sum
index b384cd58..cffa2d6f 100644
--- a/go.sum
+++ b/go.sum
@@ -1,3 +1,5 @@
+github.com/argoproj/argo-rollouts v1.7.2 h1:faDUH/qePerYRwsrHfVzNQkhjGBgXIiVYdVK8824kMo=
+github.com/argoproj/argo-rollouts v1.7.2/go.mod h1:Te4HrUELxKiBpK8lgk77o4gTa3mv8pXCd8xdPprKrbs=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
diff --git a/pkg/kor/all.go b/pkg/kor/all.go
index ffaad22b..8b22c1cd 100644
--- a/pkg/kor/all.go
+++ b/pkg/kor/all.go
@@ -6,6 +6,7 @@ import (
"fmt"
"os"
+ "github.com/argoproj/argo-rollouts/pkg/client/clientset/versioned"
apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
@@ -72,8 +73,8 @@ func getUnusedServiceAccounts(clientset kubernetes.Interface, namespace string,
return namespaceSADiff
}
-func getUnusedDeployments(clientset kubernetes.Interface, namespace string, filterOpts *filters.Options) ResourceDiff {
- deployDiff, err := processNamespaceDeployments(clientset, namespace, filterOpts)
+func getUnusedDeployments(clientset kubernetes.Interface, clientsetargorollouts versioned.Interface, namespace string, filterOpts *filters.Options) ResourceDiff {
+ deployDiff, err := processNamespaceDeployments(clientset, clientsetargorollouts, namespace, filterOpts)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to get %s namespace %s: %v\n", "deployments", namespace, err)
}
@@ -277,7 +278,19 @@ func getUnusedRoleBindings(clientset kubernetes.Interface, namespace string, fil
return namespaceRoleBindingDiff
}
-func GetUnusedAllNamespaced(filterOpts *filters.Options, clientset kubernetes.Interface, outputFormat string, opts common.Opts) (string, error) {
+func getUnusedArgoRollouts(clientset kubernetes.Interface, clientsetargorollouts versioned.Interface, namespace string, filterOpts *filters.Options) ResourceDiff {
+ argoRolloutsDiff, err := processNamespaceArgoRollouts(clientset, clientsetargorollouts, namespace, filterOpts)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Failed to get %s namespace %s: %v\n", "argorollouts", namespace, err)
+ }
+ namespaceSADiff := ResourceDiff{
+ "ArgoRollout",
+ argoRolloutsDiff,
+ }
+ return namespaceSADiff
+}
+
+func GetUnusedAllNamespaced(filterOpts *filters.Options, clientset kubernetes.Interface, clientsetargorollouts versioned.Interface, outputFormat string, opts common.Opts) (string, error) {
resources := make(map[string]map[string][]ResourceInfo)
for _, namespace := range filterOpts.Namespaces(clientset) {
switch opts.GroupBy {
@@ -287,7 +300,8 @@ func GetUnusedAllNamespaced(filterOpts *filters.Options, clientset kubernetes.In
resources[namespace]["Service"] = getUnusedSVCs(clientset, namespace, filterOpts).diff
resources[namespace]["Secret"] = getUnusedSecrets(clientset, namespace, filterOpts).diff
resources[namespace]["ServiceAccount"] = getUnusedServiceAccounts(clientset, namespace, filterOpts).diff
- resources[namespace]["Deployment"] = getUnusedDeployments(clientset, namespace, filterOpts).diff
+ resources[namespace]["Deployment"] = getUnusedDeployments(clientset, clientsetargorollouts, namespace, filterOpts).diff
+ resources[namespace]["ArgoRollout"] = getUnusedArgoRollouts(clientset, clientsetargorollouts, namespace, filterOpts).diff
resources[namespace]["StatefulSet"] = getUnusedStatefulSets(clientset, namespace, filterOpts).diff
resources[namespace]["Role"] = getUnusedRoles(clientset, namespace, filterOpts).diff
resources[namespace]["Hpa"] = getUnusedHpas(clientset, namespace, filterOpts).diff
@@ -305,7 +319,8 @@ func GetUnusedAllNamespaced(filterOpts *filters.Options, clientset kubernetes.In
appendResources(resources, "Service", namespace, getUnusedSVCs(clientset, namespace, filterOpts).diff)
appendResources(resources, "Secret", namespace, getUnusedSecrets(clientset, namespace, filterOpts).diff)
appendResources(resources, "ServiceAccount", namespace, getUnusedServiceAccounts(clientset, namespace, filterOpts).diff)
- appendResources(resources, "Deployment", namespace, getUnusedDeployments(clientset, namespace, filterOpts).diff)
+ appendResources(resources, "Deployment", namespace, getUnusedDeployments(clientset, clientsetargorollouts, namespace, filterOpts).diff)
+ appendResources(resources, "ArgoRollout", namespace, getUnusedArgoRollouts(clientset, clientsetargorollouts, namespace, filterOpts).diff)
appendResources(resources, "StatefulSet", namespace, getUnusedStatefulSets(clientset, namespace, filterOpts).diff)
appendResources(resources, "Role", namespace, getUnusedRoles(clientset, namespace, filterOpts).diff)
appendResources(resources, "Hpa", namespace, getUnusedHpas(clientset, namespace, filterOpts).diff)
@@ -377,8 +392,8 @@ func GetUnusedAllNonNamespaced(filterOpts *filters.Options, clientset kubernetes
return unusedAllNonNamespaced, nil
}
-func GetUnusedAll(filterOpts *filters.Options, clientset kubernetes.Interface, apiExtClient apiextensionsclientset.Interface, dynamicClient dynamic.Interface, outputFormat string, opts common.Opts) (string, error) {
- unusedAllNamespaced, err := GetUnusedAllNamespaced(filterOpts, clientset, outputFormat, opts)
+func GetUnusedAll(filterOpts *filters.Options, clientset kubernetes.Interface, clientsetargorollouts versioned.Interface, apiExtClient apiextensionsclientset.Interface, dynamicClient dynamic.Interface, outputFormat string, opts common.Opts) (string, error) {
+ unusedAllNamespaced, err := GetUnusedAllNamespaced(filterOpts, clientset, clientsetargorollouts, outputFormat, opts)
if err != nil {
fmt.Printf("err: %v\n", err)
}
diff --git a/pkg/kor/argorollouts.go b/pkg/kor/argorollouts.go
new file mode 100644
index 00000000..8b040570
--- /dev/null
+++ b/pkg/kor/argorollouts.go
@@ -0,0 +1,92 @@
+package kor
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "fmt"
+ "os"
+
+ "github.com/argoproj/argo-rollouts/pkg/client/clientset/versioned"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/client-go/kubernetes"
+
+ "github.com/yonahd/kor/pkg/common"
+ "github.com/yonahd/kor/pkg/filters"
+)
+
+func processNamespaceArgoRollouts(clientset kubernetes.Interface, clientsetrollout versioned.Interface, namespace string, filterOpts *filters.Options) ([]ResourceInfo, error) {
+ argoRolloutList, err := clientsetrollout.ArgoprojV1alpha1().Rollouts(namespace).List(context.TODO(), metav1.ListOptions{LabelSelector: filterOpts.IncludeLabels})
+
+ if err != nil {
+ return nil, err
+ }
+
+ var argoRolloutWithoutReplicas []ResourceInfo
+
+ for _, argoRollout := range argoRolloutList.Items {
+ if pass, _ := filter.SetObject(&argoRollout).Run(filterOpts); pass {
+ continue
+ }
+ if argoRollout.Labels["kor/used"] == "false" {
+ reason := "Marked with unused label"
+ argoRolloutWithoutReplicas = append(argoRolloutWithoutReplicas, ResourceInfo{Name: argoRollout.Name, Reason: reason})
+ continue
+ }
+ deploymentWorkLoadRef := argoRollout.Spec.WorkloadRef
+
+ if deploymentWorkLoadRef.Kind == "Deployment" {
+ deploymentItem, _ := clientset.AppsV1().Deployments(namespace).Get(context.TODO(), deploymentWorkLoadRef.Name, metav1.GetOptions{})
+
+ if deploymentItem.GetName() == "" {
+ reason := "Rollout has no deployments"
+ argoRolloutWithoutReplicas = append(argoRolloutWithoutReplicas, ResourceInfo{Name: argoRollout.Name, Reason: reason})
+ }
+ }
+ }
+
+ return argoRolloutWithoutReplicas, nil
+}
+
+func GetUnusedArgoRollouts(filterOpts *filters.Options, clientset kubernetes.Interface, clientsetargorollouts versioned.Interface, outputFormat string, opts common.Opts) (string, error) {
+ resources := make(map[string]map[string][]ResourceInfo)
+ for _, namespace := range filterOpts.Namespaces(clientset) {
+ diff, err := processNamespaceArgoRollouts(clientset, clientsetargorollouts, namespace, filterOpts)
+
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Failed to process namespace %s: %v\n", namespace, err)
+ continue
+ }
+ if opts.DeleteFlag {
+ if diff, err = DeleteArgoRolloutsResource(diff, clientsetargorollouts, namespace, "ArgoRollout", opts.NoInteractive); err != nil {
+ fmt.Fprintf(os.Stderr, "Failed to delete ArgoRollout %s in namespace %s: %v\n", diff, namespace, err)
+ }
+ }
+ switch opts.GroupBy {
+ case "namespace":
+ resources[namespace] = make(map[string][]ResourceInfo)
+ resources[namespace]["ArgoRollout"] = diff
+ case "resource":
+ appendResources(resources, "ArgoRollout", namespace, diff)
+ }
+ }
+
+ var outputBuffer bytes.Buffer
+ var jsonResponse []byte
+ switch outputFormat {
+ case "table":
+ outputBuffer = FormatOutput(resources, opts)
+ case "json", "yaml":
+ var err error
+ if jsonResponse, err = json.MarshalIndent(resources, "", " "); err != nil {
+ return "", err
+ }
+ }
+
+ unusedDeployments, err := unusedResourceFormatter(outputFormat, outputBuffer, opts, jsonResponse)
+ if err != nil {
+ fmt.Printf("err: %v\n", err)
+ }
+
+ return unusedDeployments, nil
+}
diff --git a/pkg/kor/argorollouts_test.go b/pkg/kor/argorollouts_test.go
new file mode 100644
index 00000000..96e3bad4
--- /dev/null
+++ b/pkg/kor/argorollouts_test.go
@@ -0,0 +1,87 @@
+package kor
+
+import (
+ "context"
+ "encoding/json"
+ "reflect"
+ "testing"
+
+ appsv1 "k8s.io/api/apps/v1"
+ corev1 "k8s.io/api/core/v1"
+ v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/client-go/kubernetes/fake"
+ "k8s.io/client-go/kubernetes/scheme"
+
+ "github.com/yonahd/kor/pkg/common"
+ "github.com/yonahd/kor/pkg/filters"
+)
+
+func TestGetUnusedArgoRolloutsStructured(t *testing.T) {
+ clientset := fake.NewSimpleClientset()
+ clientsetargorollouts := createClientSetTestArgoRollouts(t)
+
+ opts := common.Opts{
+ WebhookURL: "",
+ Channel: "",
+ Token: "",
+ DeleteFlag: false,
+ NoInteractive: true,
+ GroupBy: "namespace",
+ }
+
+ _, err := clientset.CoreV1().Namespaces().Create(context.TODO(), &corev1.Namespace{
+ ObjectMeta: v1.ObjectMeta{Name: testNamespace},
+ }, v1.CreateOptions{})
+
+ if err != nil {
+ t.Fatalf("Error creating namespace %s: %v", testNamespace, err)
+ }
+
+ deploymentName := "test-deployment1"
+ deployment1 := CreateTestDeployment(testNamespace, deploymentName, 0, AppLabels)
+ _, err = clientset.AppsV1().Deployments(testNamespace).Create(context.TODO(), deployment1, v1.CreateOptions{})
+
+ if err != nil {
+ t.Fatalf("Error creating fake deployment: %v", err)
+ }
+
+ rollout1 := CreateTestArgoRolloutWithDeployment(testNamespace, "test-rollout", deployment1, AppLabels)
+ _, err = clientsetargorollouts.ArgoprojV1alpha1().Rollouts(testNamespace).Create(context.TODO(), rollout1, v1.CreateOptions{})
+ if err != nil {
+ t.Fatalf("Error creating fake argo rollout: %v", err)
+ }
+
+ err = clientset.AppsV1().Deployments(testNamespace).Delete(context.TODO(), deploymentName, v1.DeleteOptions{})
+ if err != nil {
+ t.Fatalf("Error creating fake argo rollout: %v", err)
+ }
+
+ output, err := GetUnusedArgoRollouts(&filters.Options{}, clientset, clientsetargorollouts, "json", opts)
+
+ if err != nil {
+ t.Fatalf("Error calling GetUnusedArgoRolloutsStructured: %v", err)
+ }
+
+ expectedOutput := map[string]map[string][]string{
+ testNamespace: {
+ "ArgoRollout": {
+ "test-rollout",
+ },
+ },
+ }
+
+ var actualOutput map[string]map[string][]string
+ if err := json.Unmarshal([]byte(output), &actualOutput); err != nil {
+ t.Fatalf("Error unmarshaling actual output: %v", err)
+ }
+
+ if !reflect.DeepEqual(expectedOutput, actualOutput) {
+ t.Errorf("Expected output does not match actual output")
+ }
+}
+
+func init() {
+ scheme.Scheme = runtime.NewScheme()
+ _ = appsv1.AddToScheme(scheme.Scheme)
+}
diff --git a/pkg/kor/create_test_resources.go b/pkg/kor/create_test_resources.go
index bfcd22f0..d99d79da 100644
--- a/pkg/kor/create_test_resources.go
+++ b/pkg/kor/create_test_resources.go
@@ -1,6 +1,7 @@
package kor
import (
+ argorollouts "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1"
appsv1 "k8s.io/api/apps/v1"
autoscalingv2 "k8s.io/api/autoscaling/v2"
batchv1 "k8s.io/api/batch/v1"
@@ -421,3 +422,20 @@ func CreateTestNetworkPolicy(name, namespace string, labels map[string]string, p
},
}
}
+
+func CreateTestArgoRolloutWithDeployment(namespace, name string, deplomentWorkLoadRef *appsv1.Deployment, labels map[string]string) *argorollouts.Rollout {
+ return &argorollouts.Rollout{
+ ObjectMeta: v1.ObjectMeta{
+ Namespace: namespace,
+ Name: name,
+ Labels: labels,
+ },
+ Spec: argorollouts.RolloutSpec{
+ WorkloadRef: &argorollouts.ObjectRef{
+ APIVersion: "apps/v1",
+ Kind: "Deployment",
+ Name: deplomentWorkLoadRef.GetName(),
+ },
+ },
+ }
+}
diff --git a/pkg/kor/delete.go b/pkg/kor/delete.go
index 7efcec3e..9e07c449 100644
--- a/pkg/kor/delete.go
+++ b/pkg/kor/delete.go
@@ -7,6 +7,8 @@ import (
"reflect"
"strings"
+ "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1"
+ "github.com/argoproj/argo-rollouts/pkg/client/clientset/versioned"
appsv1 "k8s.io/api/apps/v1"
autoscalingv1 "k8s.io/api/autoscaling/v1"
batchv1 "k8s.io/api/batch/v1"
@@ -81,8 +83,15 @@ func DeleteResourceCmd() map[string]func(clientset kubernetes.Interface, namespa
"NetworkPolicy": func(clientset kubernetes.Interface, namespace, name string) error {
return clientset.NetworkingV1().NetworkPolicies(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{})
},
- "RoleBinding": func(clientset kubernetes.Interface, namespace, name string) error {
- return clientset.RbacV1().RoleBindings(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{})
+ }
+
+ return deleteResourceApiMap
+}
+
+func DeleteArgoRolloutsResourceCmd() map[string]func(clientset versioned.Interface, namespace, name string) error {
+ var deleteResourceApiMap = map[string]func(clientset versioned.Interface, namespace, name string) error{
+ "ArgoRollout": func(clientset versioned.Interface, namespace, name string) error {
+ return clientset.ArgoprojV1alpha1().Rollouts(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{})
},
}
@@ -133,6 +142,28 @@ func FlagResource(clientset kubernetes.Interface, namespace, resourceType, resou
return err
}
+func FlagArgoRolloutsResource(clientset versioned.Interface, namespace, resourceType, resourceName string) error {
+ resource, err := getArgoRolloutsResource(clientset, namespace, resourceType, resourceName)
+ if err != nil {
+ return err
+ }
+
+ labelField := reflect.ValueOf(resource).Elem().FieldByName("Labels")
+ if labelField.IsValid() {
+ labels := labelField.Interface().(map[string]string)
+ if labels == nil {
+ labels = make(map[string]string)
+ }
+ labels["kor/used"] = "true"
+ labelField.Set(reflect.ValueOf(labels))
+ } else {
+ return fmt.Errorf("unable to set labels for resource type: %s", resourceType)
+ }
+
+ _, err = updateArgoRolloutsResource(clientset, namespace, resourceType, resource)
+ return err
+}
+
func updateResource(clientset kubernetes.Interface, namespace, resourceType string, resource interface{}) (interface{}, error) {
switch resourceType {
case "ConfigMap":
@@ -176,6 +207,15 @@ func updateResource(clientset kubernetes.Interface, namespace, resourceType stri
case "RoleBinding":
return clientset.RbacV1().RoleBindings(namespace).Update(context.TODO(), resource.(*rbacv1.RoleBinding), metav1.UpdateOptions{})
}
+
+ return nil, fmt.Errorf("resource type '%s' is not supported", resourceType)
+}
+
+func updateArgoRolloutsResource(clientset versioned.Interface, namespace, resourceType string, resource interface{}) (interface{}, error) {
+ switch resourceType {
+ case "ArgoRollout":
+ return clientset.ArgoprojV1alpha1().Rollouts(namespace).Update(context.TODO(), resource.(*v1alpha1.Rollout), metav1.UpdateOptions{})
+ }
return nil, fmt.Errorf("resource type '%s' is not supported", resourceType)
}
@@ -225,6 +265,14 @@ func getResource(clientset kubernetes.Interface, namespace, resourceType, resour
return nil, fmt.Errorf("resource type '%s' is not supported", resourceType)
}
+func getArgoRolloutsResource(clientset versioned.Interface, namespace, resourceType, resourceName string) (interface{}, error) {
+ switch resourceType {
+ case "ArgoRollout":
+ return clientset.ArgoprojV1alpha1().Rollouts(namespace).Get(context.TODO(), resourceName, metav1.GetOptions{})
+ }
+ return nil, fmt.Errorf("resource type '%s' is not supported", resourceType)
+}
+
func DeleteResourceWithFinalizer(resources []ResourceInfo, dynamicClient dynamic.Interface, namespace string, gvr schema.GroupVersionResource, noInteractive bool) ([]ResourceInfo, error) {
var remainingResources []ResourceInfo
for _, resource := range resources {
@@ -329,3 +377,56 @@ func DeleteResource(diff []ResourceInfo, clientset kubernetes.Interface, namespa
return deletedDiff, nil
}
+
+func DeleteArgoRolloutsResource(diff []ResourceInfo, clientset versioned.Interface, namespace, resourceType string, noInteractive bool) ([]ResourceInfo, error) {
+ deletedDiff := []ResourceInfo{}
+
+ for _, resource := range diff {
+ deleteFunc, exists := DeleteArgoRolloutsResourceCmd()[resourceType]
+ if !exists {
+ fmt.Printf("Resource type '%s' is not supported\n", resource.Name)
+ continue
+ }
+
+ if !noInteractive {
+ fmt.Printf("Do you want to delete %s %s in namespace %s? (Y/N): ", resourceType, resource.Name, namespace)
+ var confirmation string
+ _, err := fmt.Scanf("%s\n", &confirmation)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Failed to read input: %v\n", err)
+ continue
+ }
+
+ if strings.ToLower(confirmation) != "y" && strings.ToLower(confirmation) != "yes" {
+ deletedDiff = append(deletedDiff, resource)
+
+ fmt.Printf("Do you want flag the resource %s %s in namespace %s as In Use? (Y/N): ", resourceType, resource.Name, namespace)
+ var inUse string
+ _, err := fmt.Scanf("%s\n", &inUse)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Failed to read input: %v\n", err)
+ continue
+ }
+
+ if strings.ToLower(inUse) == "y" || strings.ToLower(inUse) == "yes" {
+ if err := FlagArgoRolloutsResource(clientset, namespace, resourceType, resource.Name); err != nil {
+ fmt.Fprintf(os.Stderr, "Failed to flag resource %s %s in namespace %s as In Use: %v\n", resourceType, resource.Name, namespace, err)
+ }
+ continue
+ }
+ continue
+ }
+ }
+
+ fmt.Printf("Deleting %s %s in namespace %s\n", resourceType, resource.Name, namespace)
+ if err := deleteFunc(clientset, namespace, resource.Name); err != nil {
+ fmt.Fprintf(os.Stderr, "Failed to delete %s %s in namespace %s: %v\n", resourceType, resource.Name, namespace, err)
+ continue
+ }
+ deletedResource := resource
+ deletedResource.Name += "-DELETED"
+ deletedDiff = append(deletedDiff, deletedResource)
+ }
+
+ return deletedDiff, nil
+}
diff --git a/pkg/kor/deployments.go b/pkg/kor/deployments.go
index efc7c05d..d700f5c8 100644
--- a/pkg/kor/deployments.go
+++ b/pkg/kor/deployments.go
@@ -7,6 +7,7 @@ import (
"fmt"
"os"
+ "github.com/argoproj/argo-rollouts/pkg/client/clientset/versioned"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
@@ -14,7 +15,7 @@ import (
"github.com/yonahd/kor/pkg/filters"
)
-func processNamespaceDeployments(clientset kubernetes.Interface, namespace string, filterOpts *filters.Options) ([]ResourceInfo, error) {
+func processNamespaceDeployments(clientset kubernetes.Interface, clientsetrollout versioned.Interface, namespace string, filterOpts *filters.Options) ([]ResourceInfo, error) {
deploymentsList, err := clientset.AppsV1().Deployments(namespace).List(context.TODO(), metav1.ListOptions{LabelSelector: filterOpts.IncludeLabels})
if err != nil {
return nil, err
@@ -29,23 +30,30 @@ func processNamespaceDeployments(clientset kubernetes.Interface, namespace strin
if deployment.Labels["kor/used"] == "false" {
reason := "Marked with unused label"
- deploymentsWithoutReplicas = append(deploymentsWithoutReplicas, ResourceInfo{Name: deployment.Name, Reason: reason})
- continue
+
+ rolloutWithSameNameDeployment, _ := RetrieveArgoRolloutsWithDeploymentWithSameName(clientsetrollout, deployment.Name, namespace)
+ if rolloutWithSameNameDeployment == "" {
+ deploymentsWithoutReplicas = append(deploymentsWithoutReplicas, ResourceInfo{Name: deployment.Name, Reason: reason})
+ continue
+ }
}
if *deployment.Spec.Replicas == 0 {
- reason := "Deployment has no replicas"
- deploymentsWithoutReplicas = append(deploymentsWithoutReplicas, ResourceInfo{Name: deployment.Name, Reason: reason})
+ rolloutWithSameNameDeployment, _ := RetrieveArgoRolloutsWithDeploymentWithSameName(clientsetrollout, deployment.Name, namespace)
+ if rolloutWithSameNameDeployment == "" {
+ reason := "Deployment has no replicas"
+ deploymentsWithoutReplicas = append(deploymentsWithoutReplicas, ResourceInfo{Name: deployment.Name, Reason: reason})
+ }
}
}
return deploymentsWithoutReplicas, nil
}
-func GetUnusedDeployments(filterOpts *filters.Options, clientset kubernetes.Interface, outputFormat string, opts common.Opts) (string, error) {
+func GetUnusedDeployments(filterOpts *filters.Options, clientset kubernetes.Interface, clientsetargorollouts versioned.Interface, outputFormat string, opts common.Opts) (string, error) {
resources := make(map[string]map[string][]ResourceInfo)
for _, namespace := range filterOpts.Namespaces(clientset) {
- diff, err := processNamespaceDeployments(clientset, namespace, filterOpts)
+ diff, err := processNamespaceDeployments(clientset, clientsetargorollouts, namespace, filterOpts)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to process namespace %s: %v\n", namespace, err)
continue
@@ -83,3 +91,12 @@ func GetUnusedDeployments(filterOpts *filters.Options, clientset kubernetes.Inte
return unusedDeployments, nil
}
+
+func RetrieveArgoRolloutsWithDeploymentWithSameName(clientset versioned.Interface, rolloutname string, namespace string) (string, error) {
+ rollout, err := clientset.ArgoprojV1alpha1().Rollouts(namespace).Get(context.TODO(), rolloutname, metav1.GetOptions{})
+ if err != nil {
+ return "", err
+ }
+
+ return rollout.GetName(), nil
+}
diff --git a/pkg/kor/deployments_test.go b/pkg/kor/deployments_test.go
index ca4c3f8d..a5a7568d 100644
--- a/pkg/kor/deployments_test.go
+++ b/pkg/kor/deployments_test.go
@@ -6,9 +6,10 @@ import (
"reflect"
"testing"
+ fakeargorollouts "github.com/argoproj/argo-rollouts/pkg/client/clientset/versioned/fake"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
- "k8s.io/apimachinery/pkg/apis/meta/v1"
+ v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/kubernetes/scheme"
@@ -55,10 +56,15 @@ func createTestDeployments(t *testing.T) *fake.Clientset {
return clientset
}
+func createClientSetTestArgoRollouts(t *testing.T) *fakeargorollouts.Clientset {
+ return fakeargorollouts.NewSimpleClientset()
+}
+
func TestProcessNamespaceDeployments(t *testing.T) {
clientset := createTestDeployments(t)
+ clientsetargorollouts := createClientSetTestArgoRollouts(t)
- deploymentsWithoutReplicas, err := processNamespaceDeployments(clientset, testNamespace, &filters.Options{})
+ deploymentsWithoutReplicas, err := processNamespaceDeployments(clientset, clientsetargorollouts, testNamespace, &filters.Options{})
if err != nil {
t.Errorf("Expected no error, got %v", err)
}
@@ -74,6 +80,7 @@ func TestProcessNamespaceDeployments(t *testing.T) {
func TestGetUnusedDeploymentsStructured(t *testing.T) {
clientset := createTestDeployments(t)
+ clientsetargorollouts := createClientSetTestArgoRollouts(t)
opts := common.Opts{
WebhookURL: "",
@@ -84,7 +91,7 @@ func TestGetUnusedDeploymentsStructured(t *testing.T) {
GroupBy: "namespace",
}
- output, err := GetUnusedDeployments(&filters.Options{}, clientset, "json", opts)
+ output, err := GetUnusedDeployments(&filters.Options{}, clientset, clientsetargorollouts, "json", opts)
if err != nil {
t.Fatalf("Error calling GetUnusedDeploymentsStructured: %v", err)
}
@@ -108,6 +115,57 @@ func TestGetUnusedDeploymentsStructured(t *testing.T) {
}
}
+func TestGetUnusedDeploymentsWithArgoRolloutStructured(t *testing.T) {
+ clientset := fake.NewSimpleClientset()
+
+ opts := common.Opts{
+ WebhookURL: "",
+ Channel: "",
+ Token: "",
+ DeleteFlag: false,
+ NoInteractive: true,
+ GroupBy: "namespace",
+ }
+
+ _, err := clientset.CoreV1().Namespaces().Create(context.TODO(), &corev1.Namespace{
+ ObjectMeta: v1.ObjectMeta{Name: testNamespace},
+ }, v1.CreateOptions{})
+
+ if err != nil {
+ t.Fatalf("Error creating namespace %s: %v", testNamespace, err)
+ }
+ deploymentName := "test-deployment1"
+ deplomentWorkLoadRefNoReplicas := CreateTestDeployment(testNamespace, deploymentName, 0, AppLabels)
+ _, err = clientset.AppsV1().Deployments(testNamespace).Create(context.TODO(), deplomentWorkLoadRefNoReplicas, v1.CreateOptions{})
+ if err != nil {
+ t.Fatalf("Error creating fake deployment: %v", err)
+ }
+
+ clientsetargorollouts := createClientSetTestArgoRollouts(t)
+ CreateTestArgoRolloutWithDeployment(testNamespace, deploymentName, deplomentWorkLoadRefNoReplicas, AppLabels)
+
+ output, err := GetUnusedDeployments(&filters.Options{}, clientset, clientsetargorollouts, "json", opts)
+ if err != nil {
+ t.Fatalf("Error calling GetUnusedDeploymentsStructured: %v", err)
+ }
+
+ expectedOutput := map[string]map[string][]string{
+ testNamespace: {
+ "Deployment": {
+ deploymentName,
+ },
+ },
+ }
+
+ var actualOutput map[string]map[string][]string
+ if err := json.Unmarshal([]byte(output), &actualOutput); err != nil {
+ t.Fatalf("Error unmarshaling actual output: %v", err)
+ }
+ if !reflect.DeepEqual(expectedOutput, actualOutput) {
+ t.Errorf("Expected output does not match actual output")
+ }
+}
+
func init() {
scheme.Scheme = runtime.NewScheme()
_ = appsv1.AddToScheme(scheme.Scheme)
diff --git a/pkg/kor/exceptions/rolebindings/rolebindings.json b/pkg/kor/exceptions/rolebindings/rolebindings.json
index df1b8619..60aaead4 100644
--- a/pkg/kor/exceptions/rolebindings/rolebindings.json
+++ b/pkg/kor/exceptions/rolebindings/rolebindings.json
@@ -31,4 +31,4 @@
"MatchRegex": true
}
]
-}
+}
\ No newline at end of file
diff --git a/pkg/kor/exporter.go b/pkg/kor/exporter.go
index 39b66c3b..fb6b44db 100644
--- a/pkg/kor/exporter.go
+++ b/pkg/kor/exporter.go
@@ -9,6 +9,7 @@ import (
"strings"
"time"
+ "github.com/argoproj/argo-rollouts/pkg/client/clientset/versioned"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
@@ -34,16 +35,16 @@ func init() {
}
// TODO: add option to change port / url !?
-func Exporter(filterOptions *filters.Options, clientset kubernetes.Interface, apiExtClient apiextensionsclientset.Interface, dynamicClient dynamic.Interface, outputFormat string, opts common.Opts, resourceList []string) {
+func Exporter(filterOptions *filters.Options, clientset kubernetes.Interface, clientsetargorollouts versioned.Interface, apiExtClient apiextensionsclientset.Interface, dynamicClient dynamic.Interface, outputFormat string, opts common.Opts, resourceList []string) {
http.Handle("/metrics", promhttp.Handler())
fmt.Println("Server listening on :8080")
- go exportMetrics(filterOptions, clientset, apiExtClient, dynamicClient, outputFormat, opts, resourceList) // Start exporting metrics in the background
+ go exportMetrics(filterOptions, clientset, clientsetargorollouts, apiExtClient, dynamicClient, outputFormat, opts, resourceList) // Start exporting metrics in the background
if err := http.ListenAndServe(":8080", nil); err != nil {
fmt.Println(err)
}
}
-func exportMetrics(filterOptions *filters.Options, clientset kubernetes.Interface, apiExtClient apiextensionsclientset.Interface, dynamicClient dynamic.Interface, outputFormat string, opts common.Opts, resourceList []string) {
+func exportMetrics(filterOptions *filters.Options, clientset kubernetes.Interface, clientsetargorollouts versioned.Interface, apiExtClient apiextensionsclientset.Interface, dynamicClient dynamic.Interface, outputFormat string, opts common.Opts, resourceList []string) {
exporterInterval := os.Getenv("EXPORTER_INTERVAL")
if exporterInterval == "" {
exporterInterval = "10"
@@ -56,7 +57,7 @@ func exportMetrics(filterOptions *filters.Options, clientset kubernetes.Interfac
for {
fmt.Println("collecting unused resources")
- if korOutput, err := getUnusedResources(filterOptions, clientset, apiExtClient, dynamicClient, outputFormat, opts, resourceList); err != nil {
+ if korOutput, err := getUnusedResources(filterOptions, clientset, clientsetargorollouts, apiExtClient, dynamicClient, outputFormat, opts, resourceList); err != nil {
fmt.Println(err)
os.Exit(1)
} else {
@@ -80,10 +81,10 @@ func exportMetrics(filterOptions *filters.Options, clientset kubernetes.Interfac
}
}
-func getUnusedResources(filterOptions *filters.Options, clientset kubernetes.Interface, apiExtClient apiextensionsclientset.Interface, dynamicClient dynamic.Interface, outputFormat string, opts common.Opts, resourceList []string) (string, error) {
+func getUnusedResources(filterOptions *filters.Options, clientset kubernetes.Interface, clientsetargorollouts versioned.Interface, apiExtClient apiextensionsclientset.Interface, dynamicClient dynamic.Interface, outputFormat string, opts common.Opts, resourceList []string) (string, error) {
if len(resourceList) == 0 || (len(resourceList) == 1 && resourceList[0] == "all") {
- return GetUnusedAll(filterOptions, clientset, apiExtClient, dynamicClient, outputFormat, opts)
+ return GetUnusedAll(filterOptions, clientset, clientsetargorollouts, apiExtClient, dynamicClient, outputFormat, opts)
}
- return GetUnusedMulti(strings.Join(resourceList, ","), filterOptions, clientset, apiExtClient, dynamicClient, outputFormat, opts)
+ return GetUnusedMulti(strings.Join(resourceList, ","), filterOptions, clientset, clientsetargorollouts, apiExtClient, dynamicClient, outputFormat, opts)
}
diff --git a/pkg/kor/kor.go b/pkg/kor/kor.go
index be6968c0..7a85f505 100644
--- a/pkg/kor/kor.go
+++ b/pkg/kor/kor.go
@@ -8,6 +8,7 @@ import (
"regexp"
"sort"
+ clientargorollouts "github.com/argoproj/argo-rollouts/pkg/client/clientset/versioned"
apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
@@ -91,6 +92,21 @@ func GetKubeClient(kubeconfig string) *kubernetes.Clientset {
return clientset
}
+func GetKubeClientArgoRollouts(kubeconfig string) *clientargorollouts.Clientset {
+ config, err := GetConfig(kubeconfig)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Failed to load kubeconfig: %v\n", err)
+ os.Exit(1)
+ }
+
+ clientset, err := clientargorollouts.NewForConfig(config)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Failed to create Kubernetes client for argo rollouts: %v\n", err)
+ os.Exit(1)
+ }
+ return clientset
+}
+
func GetAPIExtensionsClient(kubeconfig string) *apiextensionsclientset.Clientset {
config, err := GetConfig(kubeconfig)
if err != nil {
diff --git a/pkg/kor/multi.go b/pkg/kor/multi.go
index ef2dfd94..fed71724 100644
--- a/pkg/kor/multi.go
+++ b/pkg/kor/multi.go
@@ -7,6 +7,7 @@ import (
"os"
"strings"
+ "github.com/argoproj/argo-rollouts/pkg/client/clientset/versioned"
apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
@@ -52,7 +53,7 @@ func retrieveNoNamespaceDiff(clientset kubernetes.Interface, apiExtClient apiext
return noNamespaceDiff, clearedResourceList
}
-func retrieveNamespaceDiffs(clientset kubernetes.Interface, namespace string, resourceList []string, filterOpts *filters.Options) []ResourceDiff {
+func retrieveNamespaceDiffs(clientset kubernetes.Interface, clientsetargorollouts versioned.Interface, namespace string, resourceList []string, filterOpts *filters.Options) []ResourceDiff {
var allDiffs []ResourceDiff
for _, resource := range resourceList {
var diffResult ResourceDiff
@@ -66,7 +67,7 @@ func retrieveNamespaceDiffs(clientset kubernetes.Interface, namespace string, re
case "sa", "serviceaccount", "serviceaccounts":
diffResult = getUnusedServiceAccounts(clientset, namespace, filterOpts)
case "deploy", "deployment", "deployments":
- diffResult = getUnusedDeployments(clientset, namespace, filterOpts)
+ diffResult = getUnusedDeployments(clientset, clientsetargorollouts, namespace, filterOpts)
case "sts", "statefulset", "statefulsets":
diffResult = getUnusedStatefulSets(clientset, namespace, filterOpts)
case "role", "roles":
@@ -91,6 +92,8 @@ func retrieveNamespaceDiffs(clientset kubernetes.Interface, namespace string, re
diffResult = getUnusedNetworkPolicies(clientset, namespace, filterOpts)
case "rolebinding", "rolebindings":
diffResult = getUnusedNetworkPolicies(clientset, namespace, filterOpts)
+ case "argorollouts":
+ diffResult = getUnusedArgoRollouts(clientset, clientsetargorollouts, namespace, filterOpts)
default:
fmt.Printf("resource type %q is not supported\n", resource)
}
@@ -99,7 +102,7 @@ func retrieveNamespaceDiffs(clientset kubernetes.Interface, namespace string, re
return allDiffs
}
-func GetUnusedMulti(resourceNames string, filterOpts *filters.Options, clientset kubernetes.Interface, apiExtClient apiextensionsclientset.Interface, dynamicClient dynamic.Interface, outputFormat string, opts common.Opts) (string, error) {
+func GetUnusedMulti(resourceNames string, filterOpts *filters.Options, clientset kubernetes.Interface, clientsetargorollouts versioned.Interface, apiExtClient apiextensionsclientset.Interface, dynamicClient dynamic.Interface, outputFormat string, opts common.Opts) (string, error) {
resourceList := strings.Split(resourceNames, ",")
namespaces := filterOpts.Namespaces(clientset)
resources := make(map[string]map[string][]ResourceInfo)
@@ -117,6 +120,9 @@ func GetUnusedMulti(resourceNames string, filterOpts *filters.Options, clientset
if diff.diff, err = DeleteResource(diff.diff, clientset, "", diff.resourceType, opts.NoInteractive); err != nil {
fmt.Fprintf(os.Stderr, "Failed to delete %s %s: %v\n", diff.resourceType, diff.diff, err)
}
+ if diff.diff, err = DeleteArgoRolloutsResource(diff.diff, clientsetargorollouts, "", diff.resourceType, opts.NoInteractive); err != nil {
+ fmt.Fprintf(os.Stderr, "Failed to delete %s %s: %v\n", diff.resourceType, diff.diff, err)
+ }
}
switch opts.GroupBy {
case "namespace":
@@ -129,16 +135,19 @@ func GetUnusedMulti(resourceNames string, filterOpts *filters.Options, clientset
}
for _, namespace := range namespaces {
- allDiffs := retrieveNamespaceDiffs(clientset, namespace, resourceList, filterOpts)
+ allDiffs := retrieveNamespaceDiffs(clientset, clientsetargorollouts, namespace, resourceList, filterOpts)
if opts.GroupBy == "namespace" {
resources[namespace] = make(map[string][]ResourceInfo)
}
-
for _, diff := range allDiffs {
if opts.DeleteFlag {
if diff.diff, err = DeleteResource(diff.diff, clientset, namespace, diff.resourceType, opts.NoInteractive); err != nil {
fmt.Fprintf(os.Stderr, "Failed to delete %s %s in namespace %s: %v\n", diff.resourceType, diff.diff, namespace, err)
}
+
+ if diff.diff, err = DeleteArgoRolloutsResource(diff.diff, clientsetargorollouts, namespace, diff.resourceType, opts.NoInteractive); err != nil {
+ fmt.Fprintf(os.Stderr, "Failed to delete %s %s: %v\n", diff.resourceType, diff.diff, err)
+ }
}
switch opts.GroupBy {
case "namespace":
diff --git a/pkg/kor/multi_test.go b/pkg/kor/multi_test.go
index 3c4828f2..611a6ec4 100644
--- a/pkg/kor/multi_test.go
+++ b/pkg/kor/multi_test.go
@@ -43,10 +43,11 @@ func createTestMultiResources(t *testing.T) *fake.Clientset {
func TestRetrieveNamespaceDiff(t *testing.T) {
clientset := createTestMultiResources(t)
+ clientsetargorollouts := createClientSetTestArgoRollouts(t)
resourceList := []string{"cm", "pdb", "deployment"}
filterOpts := &filters.Options{}
- namespaceDiff := retrieveNamespaceDiffs(clientset, testNamespace, resourceList, filterOpts)
+ namespaceDiff := retrieveNamespaceDiffs(clientset, clientsetargorollouts, testNamespace, resourceList, filterOpts)
if len(namespaceDiff) != 3 {
t.Fatalf("Expected 3 diffs, got %d", len(namespaceDiff))
@@ -68,6 +69,7 @@ func TestRetrieveNamespaceDiff(t *testing.T) {
func TestGetUnusedMulti(t *testing.T) {
clientset := createTestMultiResources(t)
+ clientsetargorollouts := createClientSetTestArgoRollouts(t)
resourceList := "cm,pdb,deployment"
opts := common.Opts{
@@ -79,7 +81,7 @@ func TestGetUnusedMulti(t *testing.T) {
GroupBy: "namespace",
}
- output, err := GetUnusedMulti(resourceList, &filters.Options{}, clientset, nil, nil, "json", opts)
+ output, err := GetUnusedMulti(resourceList, &filters.Options{}, clientset, clientsetargorollouts, nil, nil, "json", opts)
if err != nil {
t.Fatalf("Error calling GetUnusedMulti: %v", err)