diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index cfe09a0b..7e848c45 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -1,4 +1,4 @@
-Please read [CONTRIBUTING.md](CONTRIBUTING.md) for additional information on contributing to this repository!
+
-## What this PR does / why we need it
+## What this PR does / why we need it?
## PR Checklist
- [ ] This PR adds K8s exceptions (false positives)
- [ ] This PR adds new code
-- [ ] This PR includes test for any new code
+- [ ] This PR includes tests for new/existing code
+- [ ] This PR adds docs
+
-## Github Issue
+## GitHub Issue
[XX-XX]
diff --git a/README.md b/README.md
index 0ffa09f3..57010692 100644
--- a/README.md
+++ b/README.md
@@ -170,7 +170,7 @@ 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 | Jobs status is completed
Jobs failed with no retries left | |
| 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 |
diff --git a/charts/kor/Chart.yaml b/charts/kor/Chart.yaml
index e1cb7918..0e91b296 100644
--- a/charts/kor/Chart.yaml
+++ b/charts/kor/Chart.yaml
@@ -1,24 +1,6 @@
apiVersion: v2
name: kor
-description: A Helm chart for Kubernetes
-
-# A chart can be either an 'application' or a 'library' chart.
-#
-# Application charts are a collection of templates that can be packaged into versioned archives
-# to be deployed.
-#
-# Library charts provide useful utilities or functions for the chart developer. They're included as
-# a dependency of application charts to inject those utilities and functions into the rendering
-# pipeline. Library charts do not define any templates and therefore cannot be deployed.
+description: A Kubernetes Helm Chart to discover orphaned resources using kor
type: application
-
-# This is the chart version. This version number should be incremented each time you make changes
-# to the chart and its templates, including the app version.
-# Versions are expected to follow Semantic Versioning (https://semver.org/)
-version: 0.1.5
-
-# This is the version number of the application being deployed. This version number should be
-# incremented each time you make changes to the application. Versions are not expected to
-# follow Semantic Versioning. They should reflect the version the application is using.
-# It is recommended to use it with quotes.
+version: 0.1.6
appVersion: "0.4.0"
diff --git a/charts/kor/README.md b/charts/kor/README.md
index fb01af6a..8808b803 100644
--- a/charts/kor/README.md
+++ b/charts/kor/README.md
@@ -1,8 +1,8 @@
# kor
-  
+  
-A Helm chart for Kubernetes
+A Kubernetes Helm Chart to discover orphaned resources using kor
## Values
@@ -39,21 +39,20 @@ A Helm chart for Kubernetes
| prometheusExporter.enabled | bool | `true` | |
| prometheusExporter.exporterInterval | string | `""` | |
| prometheusExporter.name | string | `"kor-exporter"` | |
-| prometheusExporter.service.enabled | bool | `true` | |
-| prometheusExporter.service.interval | string | `"30s"` | |
-| prometheusExporter.service.labels | object | `{}` | |
-| prometheusExporter.service.metricRelabelings | list | `[]` | |
-| prometheusExporter.service.namespace | string | `""` | |
| prometheusExporter.service.port | int | `8080` | |
-| prometheusExporter.service.relabelings | list | `[]` | |
-| prometheusExporter.service.serviceMonitor | string | `nil` | |
-| prometheusExporter.service.targetLabels | list | `[]` | |
-| prometheusExporter.service.telemetryPath | string | `"/metrics"` | |
-| prometheusExporter.service.timeout | string | `"10s"` | |
| prometheusExporter.service.type | string | `"ClusterIP"` | |
-| serviceAccount.annotations | object | `{}` | |
-| serviceAccount.create | bool | `true` | |
-| serviceAccount.name | string | `""` | |
+| prometheusExporter.serviceMonitor.enabled | bool | `true` | |
+| prometheusExporter.serviceMonitor.interval | string | `"30s"` | Set how frequently Prometheus should scrape |
+| prometheusExporter.serviceMonitor.labels | object | `{}` | Service monitor labels |
+| prometheusExporter.serviceMonitor.metricRelabelings | list | `[]` | |
+| prometheusExporter.serviceMonitor.namespace | string | `""` | Set the namespace the ServiceMonitor should be deployed, if empty namespace will be `.Release.Namespace` |
+| prometheusExporter.serviceMonitor.relabelings | list | `[]` | |
+| prometheusExporter.serviceMonitor.targetLabels | list | `[]` | Set of labels to transfer on the Kubernetes Service onto the target. |
+| prometheusExporter.serviceMonitor.telemetryPath | string | `"/metrics"` | |
+| prometheusExporter.serviceMonitor.timeout | string | `"10s"` | Set timeout for scrape |
+| serviceAccount.annotations | object | `{}` | Annotations to add to the service account |
+| serviceAccount.create | bool | `true` | Specifies whether a service account should be created |
+| serviceAccount.name | string | `""` | If not set and create is true, a name is generated using the fullname template |
----------------------------------------------
Autogenerated from chart metadata using [helm-docs v1.13.1](https://github.com/norwoodj/helm-docs/releases/v1.13.1)
diff --git a/charts/kor/values.yaml b/charts/kor/values.yaml
index 9ccd72a9..4a251c0b 100644
--- a/charts/kor/values.yaml
+++ b/charts/kor/values.yaml
@@ -61,28 +61,28 @@ prometheusExporter:
serviceMonitor:
enabled: true
- # Set the namespace the ServiceMonitor should be deployed, if empty namespace will be .Release.Namespace
+ # -- Set the namespace the ServiceMonitor should be deployed, if empty namespace will be `.Release.Namespace`
namespace: ""
- # Service monitor labels
+ # -- Service monitor labels
labels: {}
- # Set how frequently Prometheus should scrape
+ # -- Set how frequently Prometheus should scrape
interval: 30s
telemetryPath: /metrics
- # Set timeout for scrape
+ # -- Set timeout for scrape
timeout: 10s
relabelings: []
- # Set of labels to transfer on the Kubernetes Service onto the target.
+ # -- Set of labels to transfer on the Kubernetes Service onto the target.
targetLabels: []
metricRelabelings: []
-# Custom labels to add into metadata
+# -- Custom labels to add into metadata
additionalLabels: {}
serviceAccount:
- # Specifies whether a service account should be created
+ # -- Specifies whether a service account should be created
create: true
- # Annotations to add to the service account
+ # -- Annotations to add to the service account
annotations: {}
- # The name of the service account to use.
- # If not set and create is true, a name is generated using the fullname template
+ # -- The name of the service account to use.
+ # -- If not set and create is true, a name is generated using the fullname template
name: ""
diff --git a/kor b/kor
new file mode 100755
index 00000000..5db9950d
Binary files /dev/null and b/kor differ
diff --git a/pkg/kor/configmaps_test.go b/pkg/kor/configmaps_test.go
index e9d33ed8..065c4f01 100644
--- a/pkg/kor/configmaps_test.go
+++ b/pkg/kor/configmaps_test.go
@@ -168,7 +168,9 @@ func TestRetrieveUsedCM(t *testing.T) {
expectedVolumesCM := []string{
"configmap-1",
"kube-root-ca.crt",
+ "openshift-service-ca.crt",
}
+
if !equalSlices(volumesCM, expectedVolumesCM) {
t.Errorf("Expected volume configmaps %v, got %v", expectedVolumesCM, volumesCM)
}
diff --git a/pkg/kor/exceptions/clusterroles/clusterroles.json b/pkg/kor/exceptions/clusterroles/clusterroles.json
index e01578bc..bcf7fd59 100644
--- a/pkg/kor/exceptions/clusterroles/clusterroles.json
+++ b/pkg/kor/exceptions/clusterroles/clusterroles.json
@@ -83,6 +83,186 @@
{
"Namespace": "",
"ResourceName": "view"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "system:node-bootstrapper"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "system:aggregated-metrics-reader"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "alert-routing-edit"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "cluster-debugger"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "global-operators-admin"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "global-operators-edit"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "global-operators-view"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "monitoring-edit"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "monitoring-rules-edit"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "monitoring-rules-view"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "olm-operators-admin"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "olm-operators-edit"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "olm-operators-view"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "openshift-cluster-monitoring-admin"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "openshift-cluster-monitoring-edit"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "openshift-cluster-monitoring-view"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "openshift-csi-main-attacher-role"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "openshift-csi-main-provisioner-role"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "openshift-csi-main-resizer-role"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "openshift-csi-main-snapshotter-role"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "openshift-csi-provisioner-configmap-and-secret-reader-role"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "openshift-csi-provisioner-volumeattachment-reader-role"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "openshift-csi-provisioner-volumesnapshot-reader-role"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "openshift-csi-resizer-infrastructure-reader-role"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "openshift-csi-resizer-storageclass-reader-role"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "resource-metrics-server-resources"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "storage-admin"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "sudoer"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "system:build-strategy-custom"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "system:image-auditor"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "system:image-pusher"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "system:image-signer"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "system:node-reader"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "system:openshift:aggregate-snapshots-to-storage-admin"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "system:openshift:aggregate-to-storage-admin"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "system:openshift:scc:hostaccess"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "system:openshift:scc:hostmount"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "system:openshift:scc:hostnetwork"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "system:openshift:scc:nonroot"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "system:openshift:scc:nonroot-v2"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "system:openshift:scc:privileged"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "system:openshift:scc:restricted"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "system:openshift:templateservicebroker-client"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "system:router"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "system:sdn-manager"
}
]
}
diff --git a/pkg/kor/exceptions/configmaps/configmaps.json b/pkg/kor/exceptions/configmaps/configmaps.json
index 81d9cd5f..89d0b1c0 100644
--- a/pkg/kor/exceptions/configmaps/configmaps.json
+++ b/pkg/kor/exceptions/configmaps/configmaps.json
@@ -4,6 +4,10 @@
"Namespace": "*",
"ResourceName": "kube-root-ca.crt"
},
+ {
+ "Namespace": "*",
+ "ResourceName": "openshift-service-ca.crt"
+ },
{
"Namespace": "gmp-system",
"ResourceName": "config-images"
@@ -76,6 +80,22 @@
"Namespace": "kube-system",
"ResourceName": "overlay-upgrade-data"
},
+ {
+ "Namespace": "kube-system",
+ "ResourceName": "cluster-dns"
+ },
+ {
+ "Namespace": "kube-system",
+ "ResourceName": "bootstrap"
+ },
+ {
+ "Namespace": "kube-system",
+ "ResourceName": "cluster-config-v1"
+ },
+ {
+ "Namespace": "kube-system",
+ "ResourceName": "root-ca"
+ },
{
"Namespace": "kubernetes-dashboard",
"ResourceName": "kubernetes-dashboard-settings"
diff --git a/pkg/kor/exceptions/crds/crds.json b/pkg/kor/exceptions/crds/crds.json
index 6bac7012..9f176ab5 100644
--- a/pkg/kor/exceptions/crds/crds.json
+++ b/pkg/kor/exceptions/crds/crds.json
@@ -83,6 +83,286 @@
{
"Namespace": "",
"ResourceName": "volumesnapshots.snapshot.storage.k8s.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "etcdsnapshotfiles.k3s.cattle.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "helmchartconfigs.helm.cattle.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "ingressrouteudps.traefik.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "ingressrouteudps.traefik.containo.us"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "middlewaretcps.traefik.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "ingressroutetcps.traefik.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "serverstransports.traefik.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "middlewares.traefik.containo.us"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "serverstransports.traefik.containo.us"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "tlsstores.traefik.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "traefikservices.traefik.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "tlsstores.traefik.containo.us"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "tlsoptions.traefik.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "traefikservices.traefik.containo.us"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "serverstransporttcps.traefik.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "middlewaretcps.traefik.containo.us"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "ingressroutes.traefik.containo.us"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "tlsoptions.traefik.containo.us"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "ingressroutetcps.traefik.containo.us"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "middlewares.traefik.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "adminpolicybasedexternalroutes.k8s.ovn.org"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "alertingrules.monitoring.openshift.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "alertmanagerconfigs.monitoring.coreos.com"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "alertrelabelconfigs.monitoring.openshift.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "baremetalhosts.metal3.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "bmceventsubscriptions.metal3.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "clusterautoscalers.autoscaling.openshift.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "clustercsidrivers.operator.openshift.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "clusterresourcequotas.quota.openshift.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "consoleexternalloglinks.console.openshift.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "consolelinks.console.openshift.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "consolenotifications.console.openshift.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "consolesamples.console.openshift.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "consoleyamlsamples.console.openshift.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "containerruntimeconfigs.machineconfiguration.openshift.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "controlplanemachinesets.machine.openshift.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "dnsrecords.ingress.operator.openshift.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "egressfirewalls.k8s.ovn.org"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "egressips.k8s.ovn.org"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "egressqoses.k8s.ovn.org"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "egressrouters.network.operator.openshift.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "egressservices.k8s.ovn.org"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "firmwareschemas.metal3.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "hardwaredata.metal3.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "hostfirmwaresettings.metal3.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "imagecontentpolicies.config.openshift.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "imagecontentsourcepolicies.operator.openshift.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "imagedigestmirrorsets.config.openshift.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "imagetagmirrorsets.config.openshift.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "installplans.operators.coreos.com"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "ippools.whereabouts.cni.cncf.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "kubeletconfigs.machineconfiguration.openshift.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "machineautoscalers.autoscaling.openshift.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "machines.machine.openshift.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "machinesets.machine.openshift.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "metal3remediations.infrastructure.cluster.x-k8s.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "metal3remediationtemplates.infrastructure.cluster.x-k8s.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "network-attachment-definitions.k8s.cni.cncf.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "operators.operators.coreos.com"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "overlappingrangeipreservations.whereabouts.cni.cncf.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "performanceprofiles.performance.openshift.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "podmonitors.monitoring.coreos.com"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "preprovisioningimages.metal3.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "probes.monitoring.coreos.com"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "projecthelmchartrepositories.helm.openshift.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "provisionings.metal3.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "rolebindingrestrictions.authorization.openshift.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "storagestates.migration.k8s.io"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "subscriptions.operators.coreos.com"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "thanosrulers.monitoring.coreos.com"
}
]
}
diff --git a/pkg/kor/exceptions/jobs/jobs.json b/pkg/kor/exceptions/jobs/jobs.json
new file mode 100644
index 00000000..af4155e6
--- /dev/null
+++ b/pkg/kor/exceptions/jobs/jobs.json
@@ -0,0 +1,16 @@
+{
+ "exceptionJobs": [
+ {
+ "Namespace": "kube-system",
+ "ResourceName": "helm-install-traefik-crd"
+ },
+ {
+ "Namespace": "kube-system",
+ "ResourceName": "helm-install-traefik"
+ },
+ {
+ "Namespace": "assisted-installer",
+ "ResourceName": "assisted-installer-controller"
+ }
+ ]
+ }
diff --git a/pkg/kor/exceptions/secrets/secrets.json b/pkg/kor/exceptions/secrets/secrets.json
index a91be3fb..63a08ca1 100644
--- a/pkg/kor/exceptions/secrets/secrets.json
+++ b/pkg/kor/exceptions/secrets/secrets.json
@@ -4,6 +4,22 @@
"Namespace": "kube-system",
"ResourceName": "bootstrap-token-*"
},
+ {
+ "Namespace": "kube-system",
+ "ResourceName": "k3s-serving"
+ },
+ {
+ "Namespace": "kube-system",
+ "ResourceName": "*.node-password.k3s"
+ },
+ {
+ "Namespace": "kube-system",
+ "ResourceName": "kube-cloud-cfg"
+ },
+ {
+ "Namespace": "kube-system",
+ "ResourceName": "kubeadmin"
+ },
{
"Namespace": "kubernetes-dashboard",
"ResourceName": "kubernetes-dashboard-certs"
diff --git a/pkg/kor/exceptions/storageclasses/storageclasses.json b/pkg/kor/exceptions/storageclasses/storageclasses.json
index 6c1d97a8..7e2dd028 100644
--- a/pkg/kor/exceptions/storageclasses/storageclasses.json
+++ b/pkg/kor/exceptions/storageclasses/storageclasses.json
@@ -43,6 +43,10 @@
{
"Namespace": "",
"ResourceName": "standard-rwo"
+ },
+ {
+ "Namespace": "",
+ "ResourceName": "local-path"
}
]
}
diff --git a/pkg/kor/finalizers.go b/pkg/kor/finalizers.go
index 368ea992..a01a4bf7 100644
--- a/pkg/kor/finalizers.go
+++ b/pkg/kor/finalizers.go
@@ -99,7 +99,6 @@ func GetUnusedfinalizers(filterOpts *filters.Options, clientset kubernetes.Inter
output := formatOutputForNamespace(namespace, allDiffs, opts)
outputBuffer.WriteString(output)
- outputBuffer.WriteString("\n")
response[namespace] = allDiffs
}
diff --git a/pkg/kor/jobs.go b/pkg/kor/jobs.go
index c9ee3eba..3fb4cb4c 100644
--- a/pkg/kor/jobs.go
+++ b/pkg/kor/jobs.go
@@ -3,22 +3,32 @@ package kor
import (
"bytes"
"context"
+ _ "embed"
"encoding/json"
"fmt"
"os"
+ batchv1 "k8s.io/api/batch/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"github.com/yonahd/kor/pkg/filters"
)
+//go:embed exceptions/jobs/jobs.json
+var jobsConfig []byte
+
func processNamespaceJobs(clientset kubernetes.Interface, namespace string, filterOpts *filters.Options) ([]string, error) {
jobsList, err := clientset.BatchV1().Jobs(namespace).List(context.TODO(), metav1.ListOptions{LabelSelector: filterOpts.IncludeLabels})
if err != nil {
return nil, err
}
+ config, err := unmarshalConfig(jobsConfig)
+ if err != nil {
+ return nil, err
+ }
+
var unusedJobNames []string
for _, job := range jobsList.Items {
@@ -26,9 +36,22 @@ func processNamespaceJobs(clientset kubernetes.Interface, namespace string, filt
continue
}
+ if isResourceException(job.Name, job.Namespace, config.ExceptionJobs) {
+ continue
+ }
+
// if the job has completionTime and succeeded count greater than zero, think the job is completed
if job.Status.CompletionTime != nil && job.Status.Succeeded > 0 {
unusedJobNames = append(unusedJobNames, job.Name)
+ continue
+ } else {
+ // Check if the job has a condition indicating it has exceeded the backoff limit
+ for _, condition := range job.Status.Conditions {
+ if condition.Type == batchv1.JobFailed && condition.Reason == "BackoffLimitExceeded" {
+ unusedJobNames = append(unusedJobNames, job.Name)
+ break
+ }
+ }
}
}
diff --git a/pkg/kor/jobs_test.go b/pkg/kor/jobs_test.go
index 422fd533..6fe0235b 100644
--- a/pkg/kor/jobs_test.go
+++ b/pkg/kor/jobs_test.go
@@ -71,23 +71,44 @@ func createTestJobs(t *testing.T) *fake.Clientset {
t.Fatalf("Error creating fake job: %v", err)
}
+ job5 := CreateTestJob(testNamespace, "test-job5", &batchv1.JobStatus{
+ Succeeded: 0,
+ Failed: 1,
+ Conditions: []batchv1.JobCondition{
+ {
+ Type: batchv1.JobFailed,
+ Status: corev1.ConditionTrue,
+ Reason: "BackoffLimitExceeded",
+ Message: "Job has reached the specified backoff limit",
+ },
+ },
+ }, AppLabels)
+
+ _, err = clientset.BatchV1().Jobs(testNamespace).Create(context.TODO(), job5, v1.CreateOptions{})
+ if err != nil {
+ t.Fatalf("Error creating fake job: %v", err)
+ }
+
return clientset
}
func TestProcessNamespaceJobs(t *testing.T) {
clientset := createTestJobs(t)
- completedJobs, err := processNamespaceJobs(clientset, testNamespace, &filters.Options{})
+ unusedJobs, err := processNamespaceJobs(clientset, testNamespace, &filters.Options{})
if err != nil {
t.Errorf("Expected no error, got %v", err)
}
- if len(completedJobs) != 2 {
- t.Errorf("Expected 2 job been completed, got %d", len(completedJobs))
+ if len(unusedJobs) != 3 {
+ t.Errorf("Expected 3 jobs unused got %d", len(unusedJobs))
}
- if completedJobs[0] != "test-job2" && completedJobs[1] != "test-job4" {
- t.Errorf("job2', got %s", completedJobs[0])
+ expectedJobs := []string{"test-job2", "test-job4", "test-job5"}
+ for _, jobName := range expectedJobs {
+ if !contains(unusedJobs, jobName) {
+ t.Errorf("Expected job %s to be considered unused, but it was not found", jobName)
+ }
}
}
@@ -113,6 +134,7 @@ func TestGetUnusedJobsStructured(t *testing.T) {
"Job": {
"test-job2",
"test-job4",
+ "test-job5",
},
},
}
diff --git a/pkg/kor/kor.go b/pkg/kor/kor.go
index 21713548..650d9c83 100644
--- a/pkg/kor/kor.go
+++ b/pkg/kor/kor.go
@@ -38,6 +38,7 @@ type Config struct {
ExceptionServiceAccounts []ExceptionResource `json:"exceptionServiceAccounts"`
ExceptionServices []ExceptionResource `json:"exceptionServices"`
ExceptionStorageClasses []ExceptionResource `json:"exceptionStorageClasses"`
+ ExceptionJobs []ExceptionResource `json:"exceptionJobs"`
// Add other configurations if needed
}
@@ -200,7 +201,7 @@ func formatOutputForResource(resource string, resources map[string][]string, opt
}
}
table.Render()
- return fmt.Sprintf("Unused %ss:\n%s", resource, buf.String())
+ return fmt.Sprintf("Unused %ss:\n%s\n", resource, buf.String())
}
func formatOutputForNamespace(namespace string, resources map[string][]string, opts Opts) string {
@@ -224,7 +225,7 @@ func formatOutputForNamespace(namespace string, resources map[string][]string, o
return ""
}
table.Render()
- return fmt.Sprintf("Unused resources in namespace: %q\n%s", namespace, buf.String())
+ return fmt.Sprintf("Unused resources in namespace: %q\n%s\n", namespace, buf.String())
}
func FormatOutputAll(namespace string, allDiffs []ResourceDiff, opts Opts) string {
@@ -248,7 +249,7 @@ func FormatOutputAll(namespace string, allDiffs []ResourceDiff, opts Opts) strin
return ""
}
table.Render()
- return fmt.Sprintf("Unused resources in namespace: %q\n%s", namespace, buf.String())
+ return fmt.Sprintf("Unused resources in namespace: %q\n%s\n", namespace, buf.String())
}
// TODO create formatter by resource "#", "Resource Name", "Namespace"
diff --git a/pkg/kor/multi.go b/pkg/kor/multi.go
index 17ced677..09086537 100644
--- a/pkg/kor/multi.go
+++ b/pkg/kor/multi.go
@@ -109,7 +109,6 @@ func GetUnusedMulti(resourceNames string, filterOpts *filters.Options, clientset
if len(diff.diff) != 0 {
output := FormatOutputAll("", []ResourceDiff{diff}, opts)
outputBuffer.WriteString(output)
- outputBuffer.WriteString("\n")
resourceMap := make(map[string][]string)
resourceMap[diff.resourceType] = diff.diff
@@ -139,7 +138,6 @@ func GetUnusedMulti(resourceNames string, filterOpts *filters.Options, clientset
output := FormatOutputAll(namespace, allDiffs, opts)
if output != "" {
outputBuffer.WriteString(output)
- outputBuffer.WriteString("\n")
resourceMap := make(map[string][]string)
for _, diff := range allDiffs {
@@ -147,7 +145,6 @@ func GetUnusedMulti(resourceNames string, filterOpts *filters.Options, clientset
}
response[namespace] = resourceMap
}
-
}
jsonResponse, err := json.MarshalIndent(response, "", " ")
diff --git a/pkg/kor/secrets.go b/pkg/kor/secrets.go
index 7680d559..7c824ecf 100644
--- a/pkg/kor/secrets.go
+++ b/pkg/kor/secrets.go
@@ -19,6 +19,7 @@ import (
var exceptionSecretTypes = []string{
`helm.sh/release.v1`,
`kubernetes.io/dockerconfigjson`,
+ `kubernetes.io/dockercfg`,
`kubernetes.io/service-account-token`,
}