Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package kor for openSUSE:Factory checked in at 2025-11-21 16:54:37 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/kor (Old) and /work/SRC/openSUSE:Factory/.kor.new.2061 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "kor" Fri Nov 21 16:54:37 2025 rev:32 rq:1318774 version:0.6.6 Changes: -------- --- /work/SRC/openSUSE:Factory/kor/kor.changes 2025-10-17 17:29:29.111350150 +0200 +++ /work/SRC/openSUSE:Factory/.kor.new.2061/kor.changes 2025-11-21 16:55:20.318973049 +0100 @@ -1,0 +2,12 @@ +Thu Nov 20 05:48:11 UTC 2025 - Johannes Kastl <[email protected]> + +- Update to version 0.6.6: + * chore: 0.6.6 preversion bump (#523) + * exceptions: support new GKE exception resources (#522) + * exceptions: add new kind resource exceptions (#521) + * feat: add PriorityClass resource detection (#519) + * chore(deps): bump k8s.io/apiextensions-apiserver from 0.34.1 to + 0.34.2 (#515) + * Fix: cron issue in helm chart (#513) + +------------------------------------------------------------------- Old: ---- kor-0.6.5.obscpio New: ---- kor-0.6.6.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ kor.spec ++++++ --- /var/tmp/diff_new_pack.0HHLpj/_old 2025-11-21 16:55:21.475021764 +0100 +++ /var/tmp/diff_new_pack.0HHLpj/_new 2025-11-21 16:55:21.479021932 +0100 @@ -17,7 +17,7 @@ Name: kor -Version: 0.6.5 +Version: 0.6.6 Release: 0 Summary: Tool to discover unused Kubernetes Resources License: MIT ++++++ _service ++++++ --- /var/tmp/diff_new_pack.0HHLpj/_old 2025-11-21 16:55:21.515023450 +0100 +++ /var/tmp/diff_new_pack.0HHLpj/_new 2025-11-21 16:55:21.523023787 +0100 @@ -3,8 +3,7 @@ <param name="url">https://github.com/yonahd/kor</param> <param name="scm">git</param> <param name="exclude">.git</param> - <param name="revision">v0.6.5</param> - <!-- the helm-related tag kor-0.2.xx is sitting on the same commit... --> + <param name="revision">v0.6.6</param> <param name="match-tag">v*</param> <param name="versionformat">@PARENT_TAG@</param> <param name="versionrewrite-pattern">v(.*)</param> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.0HHLpj/_old 2025-11-21 16:55:21.547024798 +0100 +++ /var/tmp/diff_new_pack.0HHLpj/_new 2025-11-21 16:55:21.551024966 +0100 @@ -1,6 +1,6 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/yonahd/kor</param> - <param name="changesrevision">8367b39f23b83dfe75a5d59007c22a641fe26e9d</param></service></servicedata> + <param name="changesrevision">71e4db7ff4146842ce5c34c65a407ef6d491f2a7</param></service></servicedata> (No newline at EOF) ++++++ kor-0.6.5.obscpio -> kor-0.6.6.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kor-0.6.5/README.md new/kor-0.6.6/README.md --- old/kor-0.6.5/README.md 2025-10-16 22:30:43.000000000 +0200 +++ new/kor-0.6.6/README.md 2025-11-19 21:20:12.000000000 +0100 @@ -34,6 +34,7 @@ - NetworkPolicies - RoleBindings - VolumeAttachments +- PriorityClasses  @@ -135,6 +136,7 @@ - `replicaset` - Gets unused replicaSets for the specified namespace or all namespaces. - `daemonset`- Gets unused DaemonSets for the specified namespace or all namespaces. - `volumeattachment` - Gets unused VolumeAttachments in the cluster (non-namespaced resource). +- `priorityclass` - Gets unused PriorityClasses in the cluster (non-namespaced resource). - `finalizer` - Gets unused pending deletion resources for the specified namespace or all namespaces. - `networkpolicy` - Gets unused NetworkPolicies for the specified namespace or all namespaces. - `exporter` - Export Prometheus metrics. @@ -201,6 +203,7 @@ | StorageClasses | StorageClasses not used by any PVs / PVCs | | NetworkPolicies | NetworkPolicies with no Pods selected by podSelector or Ingress / Egress rules | | VolumeAttachments | VolumeAttachments referencing a non-existent Node, PV, or CSIDriver | +| PriorityClasses | PriorityClasses not used by any Pods | ### Deleting Unused resources diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kor-0.6.5/charts/kor/Chart.yaml new/kor-0.6.6/charts/kor/Chart.yaml --- old/kor-0.6.5/charts/kor/Chart.yaml 2025-10-16 22:30:43.000000000 +0200 +++ new/kor-0.6.6/charts/kor/Chart.yaml 2025-11-19 21:20:12.000000000 +0100 @@ -2,8 +2,8 @@ name: kor description: A Kubernetes Helm Chart to discover orphaned resources using kor type: application -version: 0.2.5 -appVersion: "0.6.5" +version: 0.2.8 +appVersion: "0.6.6" maintainers: - name: "yonahd" url: "https://github.com/yonahd/kor" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kor-0.6.5/charts/kor/README.md new/kor-0.6.6/charts/kor/README.md --- old/kor-0.6.5/charts/kor/README.md 2025-10-16 22:30:43.000000000 +0200 +++ new/kor-0.6.6/charts/kor/README.md 2025-11-19 21:20:12.000000000 +0100 @@ -1,6 +1,6 @@ # kor -   +   A Kubernetes Helm Chart to discover orphaned resources using kor diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kor-0.6.5/charts/kor/templates/cronjob.yaml new/kor-0.6.6/charts/kor/templates/cronjob.yaml --- old/kor-0.6.5/charts/kor/templates/cronjob.yaml 2025-10-16 22:30:43.000000000 +0200 +++ new/kor-0.6.6/charts/kor/templates/cronjob.yaml 2025-11-19 21:20:12.000000000 +0100 @@ -7,7 +7,7 @@ {{- include "kor.labels" . | nindent 4 }} app: {{ .Release.Name }} spec: - schedule: {{ .Values.cronJob.schedule }} + schedule: "{{ .Values.cronJob.schedule }}" jobTemplate: spec: template: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kor-0.6.5/charts/kor/templates/role.yaml new/kor-0.6.6/charts/kor/templates/role.yaml --- old/kor-0.6.5/charts/kor/templates/role.yaml 2025-10-16 22:30:43.000000000 +0200 +++ new/kor-0.6.6/charts/kor/templates/role.yaml 2025-11-19 21:20:12.000000000 +0100 @@ -66,6 +66,7 @@ - customresourcedefinitions - storageclasses - volumeattachments + - priorityclasses verbs: - get - list diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kor-0.6.5/cmd/kor/priorityclasses.go new/kor-0.6.6/cmd/kor/priorityclasses.go --- old/kor-0.6.5/cmd/kor/priorityclasses.go 1970-01-01 01:00:00.000000000 +0100 +++ new/kor-0.6.6/cmd/kor/priorityclasses.go 2025-11-19 21:20:12.000000000 +0100 @@ -0,0 +1,32 @@ +package kor + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/yonahd/kor/pkg/kor" + "github.com/yonahd/kor/pkg/utils" +) + +var priorityClassCmd = &cobra.Command{ + Use: "priorityclass", + Aliases: []string{"pc", "priorityclasses"}, + Short: "Gets unused priorityClasses", + Args: cobra.NoArgs, + Run: func(cmd *cobra.Command, args []string) { + clientset := kor.GetKubeClient(kubeconfig) + + if response, err := kor.GetUnusedPriorityClasses(filterOptions, clientset, outputFormat, opts); err != nil { + fmt.Println(err) + } else { + utils.PrintLogo(outputFormat) + fmt.Println(response) + } + + }, +} + +func init() { + rootCmd.AddCommand(priorityClassCmd) +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kor-0.6.5/go.mod new/kor-0.6.6/go.mod --- old/kor-0.6.5/go.mod 2025-10-16 22:30:43.000000000 +0200 +++ new/kor-0.6.6/go.mod 2025-11-19 21:20:12.000000000 +0100 @@ -8,10 +8,10 @@ github.com/prometheus/client_golang v1.23.2 github.com/spf13/cobra v1.10.1 github.com/spf13/viper v1.21.0 - k8s.io/api v0.34.1 - k8s.io/apiextensions-apiserver v0.34.1 - k8s.io/apimachinery v0.34.1 - k8s.io/client-go v0.34.1 + k8s.io/api v0.34.2 + k8s.io/apiextensions-apiserver v0.34.2 + k8s.io/apimachinery v0.34.2 + k8s.io/client-go v0.34.2 k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 sigs.k8s.io/yaml v1.6.0 ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kor-0.6.5/go.sum new/kor-0.6.6/go.sum --- old/kor-0.6.5/go.sum 2025-10-16 22:30:43.000000000 +0200 +++ new/kor-0.6.6/go.sum 2025-11-19 21:20:12.000000000 +0100 @@ -197,14 +197,14 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM= -k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk= -k8s.io/apiextensions-apiserver v0.34.1 h1:NNPBva8FNAPt1iSVwIE0FsdrVriRXMsaWFMqJbII2CI= -k8s.io/apiextensions-apiserver v0.34.1/go.mod h1:hP9Rld3zF5Ay2Of3BeEpLAToP+l4s5UlxiHfqRaRcMc= -k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4= -k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= -k8s.io/client-go v0.34.1 h1:ZUPJKgXsnKwVwmKKdPfw4tB58+7/Ik3CrjOEhsiZ7mY= -k8s.io/client-go v0.34.1/go.mod h1:kA8v0FP+tk6sZA0yKLRG67LWjqufAoSHA2xVGKw9Of8= +k8s.io/api v0.34.2 h1:fsSUNZhV+bnL6Aqrp6O7lMTy6o5x2C4XLjnh//8SLYY= +k8s.io/api v0.34.2/go.mod h1:MMBPaWlED2a8w4RSeanD76f7opUoypY8TFYkSM+3XHw= +k8s.io/apiextensions-apiserver v0.34.2 h1:WStKftnGeoKP4AZRz/BaAAEJvYp4mlZGN0UCv+uvsqo= +k8s.io/apiextensions-apiserver v0.34.2/go.mod h1:398CJrsgXF1wytdaanynDpJ67zG4Xq7yj91GrmYN2SE= +k8s.io/apimachinery v0.34.2 h1:zQ12Uk3eMHPxrsbUJgNF8bTauTVR2WgqJsTmwTE/NW4= +k8s.io/apimachinery v0.34.2/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= +k8s.io/client-go v0.34.2 h1:Co6XiknN+uUZqiddlfAjT68184/37PS4QAzYvQvDR8M= +k8s.io/client-go v0.34.2/go.mod h1:2VYDl1XXJsdcAxw7BenFslRQX28Dxz91U9MWKjX97fE= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kor-0.6.5/pkg/kor/all.go new/kor-0.6.6/pkg/kor/all.go --- old/kor-0.6.5/pkg/kor/all.go 2025-10-16 22:30:43.000000000 +0200 +++ new/kor-0.6.6/pkg/kor/all.go 2025-11-19 21:20:12.000000000 +0100 @@ -277,6 +277,18 @@ return allVattsDiff } +func getUnusedPriorityClasses(clientset kubernetes.Interface, filterOpts *filters.Options) ResourceDiff { + pcDiff, err := processPriorityClasses(clientset, filterOpts) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to get %s: %v\n", "PriorityClasses", err) + } + allPcDiff := ResourceDiff{ + "PriorityClass", + pcDiff, + } + return allPcDiff +} + func getUnusedNetworkPolicies(clientset kubernetes.Interface, namespace string, filterOpts *filters.Options, opts common.Opts) ResourceDiff { netpolDiff, err := processNamespaceNetworkPolicies(clientset, namespace, filterOpts, opts) if err != nil { @@ -377,6 +389,7 @@ resources[""]["ClusterRoleBinding"] = getUnusedClusterRoleBindings(clientset, filterOpts, opts).diff resources[""]["StorageClass"] = getUnusedStorageClasses(clientset, filterOpts).diff resources[""]["VolumeAttachment"] = getUnusedVolumeAttachments(clientset, filterOpts).diff + resources[""]["PriorityClass"] = getUnusedPriorityClasses(clientset, filterOpts).diff case "resource": appendResources(resources, "Crd", "", getUnusedCrds(apiExtClient, dynamicClient, filterOpts).diff) appendResources(resources, "Pv", "", getUnusedPvs(clientset, filterOpts).diff) @@ -384,6 +397,7 @@ appendResources(resources, "ClusterRoleBinding", "", getUnusedClusterRoleBindings(clientset, filterOpts, opts).diff) appendResources(resources, "StorageClass", "", getUnusedStorageClasses(clientset, filterOpts).diff) appendResources(resources, "VolumeAttachment", "", getUnusedVolumeAttachments(clientset, filterOpts).diff) + appendResources(resources, "PriorityClass", "", getUnusedPriorityClasses(clientset, filterOpts).diff) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kor-0.6.5/pkg/kor/create_test_resources.go new/kor-0.6.6/pkg/kor/create_test_resources.go --- old/kor-0.6.5/pkg/kor/create_test_resources.go 2025-10-16 22:30:43.000000000 +0200 +++ new/kor-0.6.6/pkg/kor/create_test_resources.go 2025-11-19 21:20:12.000000000 +0100 @@ -11,6 +11,7 @@ networkingv1 "k8s.io/api/networking/v1" policyv1 "k8s.io/api/policy/v1" rbacv1 "k8s.io/api/rbac/v1" + schedulingv1 "k8s.io/api/scheduling/v1" storagev1 "k8s.io/api/storage/v1" "k8s.io/apimachinery/pkg/api/resource" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -505,3 +506,10 @@ ObjectMeta: v1.ObjectMeta{Name: name}, } } + +func CreateTestPriorityClass(name string, value int32) *schedulingv1.PriorityClass { + return &schedulingv1.PriorityClass{ + ObjectMeta: v1.ObjectMeta{Name: name}, + Value: value, + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kor-0.6.5/pkg/kor/delete.go new/kor-0.6.6/pkg/kor/delete.go --- old/kor-0.6.5/pkg/kor/delete.go 2025-10-16 22:30:43.000000000 +0200 +++ new/kor-0.6.6/pkg/kor/delete.go 2025-11-19 21:20:12.000000000 +0100 @@ -90,6 +90,9 @@ "VolumeAttachment": func(clientset kubernetes.Interface, namespace, name string) error { return clientset.StorageV1().VolumeAttachments().Delete(context.TODO(), name, metav1.DeleteOptions{}) }, + "PriorityClass": func(clientset kubernetes.Interface, namespace, name string) error { + return clientset.SchedulingV1().PriorityClasses().Delete(context.TODO(), name, metav1.DeleteOptions{}) + }, } return deleteResourceApiMap diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kor-0.6.5/pkg/kor/deployments.go new/kor-0.6.6/pkg/kor/deployments.go --- old/kor-0.6.5/pkg/kor/deployments.go 2025-10-16 22:30:43.000000000 +0200 +++ new/kor-0.6.6/pkg/kor/deployments.go 2025-11-19 21:20:12.000000000 +0100 @@ -3,6 +3,7 @@ import ( "bytes" "context" + _ "embed" "encoding/json" "fmt" "os" @@ -14,12 +15,20 @@ "github.com/yonahd/kor/pkg/filters" ) +//go:embed exceptions/deployments/deployments.json +var deploymentsConfig []byte + func processNamespaceDeployments(clientset kubernetes.Interface, namespace string, filterOpts *filters.Options, opts common.Opts) ([]ResourceInfo, error) { deploymentsList, err := clientset.AppsV1().Deployments(namespace).List(context.TODO(), metav1.ListOptions{LabelSelector: filterOpts.IncludeLabels}) if err != nil { return nil, err } + config, err := unmarshalConfig(deploymentsConfig) + if err != nil { + return nil, err + } + var deploymentsWithoutReplicas []ResourceInfo for _, deployment := range deploymentsList.Items { @@ -38,6 +47,15 @@ continue } + exceptionFound, err := isResourceException(deployment.Name, namespace, config.ExceptionConfigMaps) + if err != nil { + return nil, err + } + + if exceptionFound { + continue + } + if *deployment.Spec.Replicas == 0 { reason := "Deployment has no replicas" deploymentsWithoutReplicas = append(deploymentsWithoutReplicas, ResourceInfo{Name: deployment.Name, Reason: reason}) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kor-0.6.5/pkg/kor/exceptions/clusterrolebindings/clusterrolebindings.json new/kor-0.6.6/pkg/kor/exceptions/clusterrolebindings/clusterrolebindings.json --- old/kor-0.6.5/pkg/kor/exceptions/clusterrolebindings/clusterrolebindings.json 2025-10-16 22:30:43.000000000 +0200 +++ new/kor-0.6.6/pkg/kor/exceptions/clusterrolebindings/clusterrolebindings.json 2025-11-19 21:20:12.000000000 +0100 @@ -1,3 +1,76 @@ { - "exceptionClusterRoleBindings": [] + "exceptionClusterRoleBindings": [ + { + "namespace": "", + "name": "kubeadm:kubelet-bootstrap" + }, + { + "namespace": "", + "name": "kubeadm:node-autoapprove-bootstrap" + }, + { + "namespace": "", + "name": "kubeadm:node-autoapprove-certificate-rotation" + }, + { + "namespace": "", + "name": "system:controller:route-controller" + }, + { + "namespace": "", + "name": "system:kube-dns" + }, + { + "namespace": "", + "name": "system:node" + }, + { + "Namespace": "", + "ResourceName": "event-exporter-rb" + }, + { + "Namespace": "", + "ResourceName": "kubelet-bootstrap" + }, + { + "Namespace": "", + "ResourceName": "kubelet-bootstrap-node-bootstrapper" + }, + { + "Namespace": "", + "ResourceName": "kubelet-cluster-admin" + }, + { + "Namespace": "", + "ResourceName": "kubelet-nodepool-bootstrapper" + }, + { + "Namespace": "", + "ResourceName": "kubelet-user-npd-binding" + }, + { + "Namespace": "", + "ResourceName": "metrics-server-nanny:system:auth-delegator" + }, + { + "Namespace": "", + "ResourceName": "metrics-server:system:auth-delegator" + }, + { + "Namespace": "", + "ResourceName": "npd-binding" + }, + { + "Namespace": "", + "ResourceName": "system:controller:horizontal-pod-autoscaler" + }, + { + "Namespace": "", + "ResourceName": "system:controller:selinux-warning-controller" + }, + { + "Namespace": "", + "ResourceName": "system:konnectivity-server" + } + ] } \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kor-0.6.5/pkg/kor/exceptions/configmaps/configmaps.json new/kor-0.6.6/pkg/kor/exceptions/configmaps/configmaps.json --- old/kor-0.6.5/pkg/kor/exceptions/configmaps/configmaps.json 2025-10-16 22:30:43.000000000 +0200 +++ new/kor-0.6.6/pkg/kor/exceptions/configmaps/configmaps.json 2025-11-19 21:20:12.000000000 +0100 @@ -118,6 +118,34 @@ "Namespace": "openshift-.*", "ResourceName": ".*", "MatchRegex": true + }, + { + "Namespace": "gke-managed-system", + "ResourceName": "dcgm-exporter-metrics" + }, + { + "Namespace": "gmp-system", + "ResourceName": "rule-evaluator" + }, + { + "Namespace": "gmp-system", + "ResourceName": "rules-generated" + }, + { + "Namespace": "kube-system", + "ResourceName": "efficiency-daemon-config" + }, + { + "Namespace": "kube-system", + "ResourceName": "metrics-agent-linux-config-images" + }, + { + "Namespace": "kube-system", + "ResourceName": "metrics-agent-windows-config-images" + }, + { + "Namespace": "kube-system", + "ResourceName": "nvidia-metrics-collector-config-map" } ] } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kor-0.6.5/pkg/kor/exceptions/crds/crds.json new/kor-0.6.6/pkg/kor/exceptions/crds/crds.json --- old/kor-0.6.5/pkg/kor/exceptions/crds/crds.json 2025-10-16 22:30:43.000000000 +0200 +++ new/kor-0.6.6/pkg/kor/exceptions/crds/crds.json 2025-11-19 21:20:12.000000000 +0100 @@ -379,6 +379,38 @@ { "Namespace": "", "ResourceName": "volumesnapshots.snapshot.storage.k8s.io" + }, + { + "Namespace": "", + "ResourceName": "allowlistsynchronizers.auto.gke.io" + }, + { + "Namespace": "", + "ResourceName": "audits.warden.gke.io" + }, + { + "Namespace": "", + "ResourceName": "computeclasses.cloud.google.com" + }, + { + "Namespace": "", + "ResourceName": "gcpdatasources.datalayer.gke.io" + }, + { + "Namespace": "", + "ResourceName": "gcpresourceallowlists.node.gke.io" + }, + { + "Namespace": "", + "ResourceName": "servicefunctionchains.networking.gke.io" + }, + { + "Namespace": "", + "ResourceName": "trafficselectors.networking.gke.io" + }, + { + "Namespace": "", + "ResourceName": "workloadallowlists.auto.gke.io" } ] } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kor-0.6.5/pkg/kor/exceptions/daemonsets/daemonsets.json new/kor-0.6.6/pkg/kor/exceptions/daemonsets/daemonsets.json --- old/kor-0.6.5/pkg/kor/exceptions/daemonsets/daemonsets.json 2025-10-16 22:30:43.000000000 +0200 +++ new/kor-0.6.6/pkg/kor/exceptions/daemonsets/daemonsets.json 2025-11-19 21:20:12.000000000 +0100 @@ -103,6 +103,26 @@ { "Namespace": "kube-system", "ResourceName": "tpu-device-plugin" + }, + { + "Namespace": "gke-managed-system", + "ResourceName": "dcgm-exporter" + }, + { + "Namespace": "kube-system", + "ResourceName": "efficiency-daemon" + }, + { + "Namespace": "kube-system", + "ResourceName": "fluentbit-gke-managed-node-big" + }, + { + "Namespace": "kube-system", + "ResourceName": "fluentbit-gke-managed-node-small" + }, + { + "Namespace": "kube-system", + "ResourceName": "maintenance-handler" } ] } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kor-0.6.5/pkg/kor/exceptions/deployments/deployments.json new/kor-0.6.6/pkg/kor/exceptions/deployments/deployments.json --- old/kor-0.6.5/pkg/kor/exceptions/deployments/deployments.json 1970-01-01 01:00:00.000000000 +0100 +++ new/kor-0.6.6/pkg/kor/exceptions/deployments/deployments.json 2025-11-19 21:20:12.000000000 +0100 @@ -0,0 +1,8 @@ +{ + "exceptionDeployments": [ + { + "Namespace": "gmp-system", + "ResourceName": "rule-evaluator" + } + ] +} \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kor-0.6.5/pkg/kor/exceptions/priorityclasses/priorityclasses.json new/kor-0.6.6/pkg/kor/exceptions/priorityclasses/priorityclasses.json --- old/kor-0.6.5/pkg/kor/exceptions/priorityclasses/priorityclasses.json 1970-01-01 01:00:00.000000000 +0100 +++ new/kor-0.6.6/pkg/kor/exceptions/priorityclasses/priorityclasses.json 2025-11-19 21:20:12.000000000 +0100 @@ -0,0 +1 @@ +{"exceptionPriorityClasses": []} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kor-0.6.5/pkg/kor/exceptions/rolebindings/rolebindings.json new/kor-0.6.6/pkg/kor/exceptions/rolebindings/rolebindings.json --- old/kor-0.6.5/pkg/kor/exceptions/rolebindings/rolebindings.json 2025-10-16 22:30:43.000000000 +0200 +++ new/kor-0.6.6/pkg/kor/exceptions/rolebindings/rolebindings.json 2025-11-19 21:20:12.000000000 +0100 @@ -29,6 +29,14 @@ "Namespace": "kube-system", "ResourceName": "system:controller:*", "MatchRegex": true + }, + { + "Namespace": "gmp-public", + "ResourceName": "operator" + }, + { + "Namespace": "kube-system", + "ResourceName": "gce:podsecuritypolicy:pdcsi-node-sa" } ] } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kor-0.6.5/pkg/kor/exceptions/secrets/secrets.json new/kor-0.6.6/pkg/kor/exceptions/secrets/secrets.json --- old/kor-0.6.5/pkg/kor/exceptions/secrets/secrets.json 2025-10-16 22:30:43.000000000 +0200 +++ new/kor-0.6.6/pkg/kor/exceptions/secrets/secrets.json 2025-11-19 21:20:12.000000000 +0100 @@ -38,6 +38,18 @@ "Namespace": "openshift-.*", "ResourceName": ".*", "MatchRegex": true + }, + { + "Namespace": "gmp-system", + "ResourceName": "alertmanager" + }, + { + "Namespace": "gmp-system", + "ResourceName": "rules" + }, + { + "Namespace": "gmp-system", + "ResourceName": "webhook-tls" } ] } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kor-0.6.5/pkg/kor/exceptions/serviceaccounts/serviceaccounts.json new/kor-0.6.6/pkg/kor/exceptions/serviceaccounts/serviceaccounts.json --- old/kor-0.6.5/pkg/kor/exceptions/serviceaccounts/serviceaccounts.json 2025-10-16 22:30:43.000000000 +0200 +++ new/kor-0.6.6/pkg/kor/exceptions/serviceaccounts/serviceaccounts.json 2025-11-19 21:20:12.000000000 +0100 @@ -17,6 +17,10 @@ "Namespace": "openshift-.*", "ResourceName": ".*", "MatchRegex": true + }, + { + "Namespace": "kube-system", + "ResourceName": "gke-system-balloon-pod" } ] } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kor-0.6.5/pkg/kor/exceptions/services/services.json new/kor-0.6.6/pkg/kor/exceptions/services/services.json --- old/kor-0.6.5/pkg/kor/exceptions/services/services.json 2025-10-16 22:30:43.000000000 +0200 +++ new/kor-0.6.6/pkg/kor/exceptions/services/services.json 2025-11-19 21:20:12.000000000 +0100 @@ -20,6 +20,14 @@ "Namespace": "openshift-.*", "ResourceName": ".*", "MatchRegex": true + }, + { + "Namespace": "gmp-system", + "ResourceName": "alertmanager" + }, + { + "Namespace": "gmp-system", + "ResourceName": "rule-evaluator" } ] } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kor-0.6.5/pkg/kor/exceptions/statefulsets/statefulsets.json new/kor-0.6.6/pkg/kor/exceptions/statefulsets/statefulsets.json --- old/kor-0.6.5/pkg/kor/exceptions/statefulsets/statefulsets.json 1970-01-01 01:00:00.000000000 +0100 +++ new/kor-0.6.6/pkg/kor/exceptions/statefulsets/statefulsets.json 2025-11-19 21:20:12.000000000 +0100 @@ -0,0 +1,8 @@ +{ + "exceptionStatefulSets": [ + { + "Namespace": "gmp-system", + "ResourceName": "alertmanager" + } + ] +} \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kor-0.6.5/pkg/kor/exceptions/storageclasses/storageclasses.json new/kor-0.6.6/pkg/kor/exceptions/storageclasses/storageclasses.json --- old/kor-0.6.5/pkg/kor/exceptions/storageclasses/storageclasses.json 2025-10-16 22:30:43.000000000 +0200 +++ new/kor-0.6.6/pkg/kor/exceptions/storageclasses/storageclasses.json 2025-11-19 21:20:12.000000000 +0100 @@ -51,6 +51,10 @@ { "Namespace": "", "ResourceName": "standard-rwo" + }, + { + "Namespace": "", + "ResourceName": "hostpath" } ] } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kor-0.6.5/pkg/kor/kor.go new/kor-0.6.6/pkg/kor/kor.go --- old/kor-0.6.5/pkg/kor/kor.go 2025-10-16 22:30:43.000000000 +0200 +++ new/kor-0.6.6/pkg/kor/kor.go 2025-11-19 21:20:12.000000000 +0100 @@ -46,6 +46,7 @@ ExceptionJobs []ExceptionResource `json:"exceptionJobs"` ExceptionPdbs []ExceptionResource `json:"exceptionPdbs"` ExceptionRoleBindings []ExceptionResource `json:"exceptionRoleBindings"` + ExceptionPriorityClasses []ExceptionResource `json:"exceptionPriorityClasses"` // Add other configurations if needed } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kor-0.6.5/pkg/kor/multi.go new/kor-0.6.6/pkg/kor/multi.go --- old/kor-0.6.5/pkg/kor/multi.go 2025-10-16 22:30:43.000000000 +0200 +++ new/kor-0.6.6/pkg/kor/multi.go 2025-11-19 21:20:12.000000000 +0100 @@ -68,6 +68,10 @@ vattsDiff := getUnusedVolumeAttachments(clientset, filterOpts) noNamespaceDiff = append(noNamespaceDiff, vattsDiff) markedForRemoval[counter] = true + case "priorityclass": + pcDiff := getUnusedPriorityClasses(clientset, filterOpts) + noNamespaceDiff = append(noNamespaceDiff, pcDiff) + markedForRemoval[counter] = true } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kor-0.6.5/pkg/kor/priorityclasses.go new/kor-0.6.6/pkg/kor/priorityclasses.go --- old/kor-0.6.5/pkg/kor/priorityclasses.go 1970-01-01 01:00:00.000000000 +0100 +++ new/kor-0.6.6/pkg/kor/priorityclasses.go 2025-11-19 21:20:12.000000000 +0100 @@ -0,0 +1,135 @@ +package kor + +import ( + "bytes" + "context" + _ "embed" + "encoding/json" + "fmt" + "os" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + _ "k8s.io/client-go/plugin/pkg/client/auth/oidc" + + "github.com/yonahd/kor/pkg/common" + "github.com/yonahd/kor/pkg/filters" +) + +//go:embed exceptions/priorityclasses/priorityclasses.json +var priorityClassesConfig []byte + +func retrieveUsedPriorityClasses(clientset kubernetes.Interface) ([]string, error) { + pods, err := clientset.CoreV1().Pods("").List(context.TODO(), metav1.ListOptions{}) + if err != nil { + return nil, fmt.Errorf("failed to list Pods: %v", err) + } + + var usedPriorityClasses []string + + // Iterate through each Pod and check for PriorityClass usage + for _, pod := range pods.Items { + if pod.Spec.PriorityClassName != "" { + usedPriorityClasses = append(usedPriorityClasses, pod.Spec.PriorityClassName) + } + } + + return usedPriorityClasses, nil +} + +func processPriorityClasses(clientset kubernetes.Interface, filterOpts *filters.Options) ([]ResourceInfo, error) { + pcs, err := clientset.SchedulingV1().PriorityClasses().List(context.TODO(), metav1.ListOptions{LabelSelector: filterOpts.IncludeLabels}) + if err != nil { + return nil, err + } + + config, err := unmarshalConfig(priorityClassesConfig) + if err != nil { + return nil, err + } + + var unusedPriorityClasses []ResourceInfo + priorityClassNames := make([]string, 0, len(pcs.Items)) + + for _, pc := range pcs.Items { + // Skip global default PriorityClasses as they are used by pods without explicit priority class + if pc.GlobalDefault { + continue + } + + // Skip resources with ownerReferences if the general flag is set + if filterOpts.IgnoreOwnerReferences && len(pc.OwnerReferences) > 0 { + continue + } + + if pass, _ := filter.SetObject(&pc).Run(filterOpts); pass { + continue + } + + if pc.Labels["kor/used"] == "false" { + unusedPriorityClasses = append(unusedPriorityClasses, ResourceInfo{Name: pc.Name, Reason: "Marked with unused label"}) + continue + } + + exceptionFound, err := isResourceException(pc.Name, "", config.ExceptionPriorityClasses) + if err != nil { + return nil, err + } + + if exceptionFound { + continue + } + + priorityClassNames = append(priorityClassNames, pc.Name) + } + + usedPriorityClasses, err := retrieveUsedPriorityClasses(clientset) + if err != nil { + return nil, err + } + + diff := CalculateResourceDifference(usedPriorityClasses, priorityClassNames) + for _, name := range diff { + unusedPriorityClasses = append(unusedPriorityClasses, ResourceInfo{Name: name, Reason: "Not in Use"}) + } + return unusedPriorityClasses, nil +} + +func GetUnusedPriorityClasses(filterOpts *filters.Options, clientset kubernetes.Interface, outputFormat string, opts common.Opts) (string, error) { + resources := make(map[string]map[string][]ResourceInfo) + diff, err := processPriorityClasses(clientset, filterOpts) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to process priorityClasses: %v\n", err) + } + if opts.DeleteFlag { + if diff, err = DeleteResource(diff, clientset, "", "PriorityClass", opts.NoInteractive); err != nil { + fmt.Fprintf(os.Stderr, "Failed to delete PriorityClass %s: %v\n", diff, err) + } + } + switch opts.GroupBy { + case "namespace": + resources[""] = make(map[string][]ResourceInfo) + resources[""]["PriorityClass"] = diff + case "resource": + appendResources(resources, "PriorityClass", "", 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 + } + } + + unusedPriorityClasses, err := unusedResourceFormatter(outputFormat, outputBuffer, opts, jsonResponse) + if err != nil { + fmt.Printf("err: %v\n", err) + } + + return unusedPriorityClasses, nil +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kor-0.6.5/pkg/kor/priorityclasses_test.go new/kor-0.6.6/pkg/kor/priorityclasses_test.go --- old/kor-0.6.5/pkg/kor/priorityclasses_test.go 1970-01-01 01:00:00.000000000 +0100 +++ new/kor-0.6.6/pkg/kor/priorityclasses_test.go 2025-11-19 21:20:12.000000000 +0100 @@ -0,0 +1,199 @@ +package kor + +import ( + "context" + "encoding/json" + "reflect" + "testing" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/fake" + + "github.com/yonahd/kor/pkg/common" + "github.com/yonahd/kor/pkg/filters" +) + +func createTestPriorityClass(t *testing.T) *fake.Clientset { + clientset := fake.NewSimpleClientset() + + pc1 := CreateTestPriorityClass("test-pc1", 1000) + _, err := clientset.SchedulingV1().PriorityClasses().Create(context.TODO(), pc1, v1.CreateOptions{}) + if err != nil { + t.Fatalf("Error creating fake %s: %v", "PriorityClass", err) + } + + return clientset +} + +func TestRetrieveUsedPriorityClasses(t *testing.T) { + clientset := fake.NewSimpleClientset() + + pc1 := CreateTestPriorityClass("test-pc1", 1000) + _, err := clientset.SchedulingV1().PriorityClasses().Create(context.TODO(), pc1, v1.CreateOptions{}) + if err != nil { + t.Fatalf("Error creating fake PriorityClass: %v", err) + } + + pod := CreateTestPod(testNamespace, "test-pod", "", nil, AppLabels) + pod.Spec.PriorityClassName = "test-pc1" + _, err = clientset.CoreV1().Pods(testNamespace).Create(context.TODO(), pod, v1.CreateOptions{}) + if err != nil { + t.Fatalf("Error creating fake Pod: %v", err) + } + + usedPriorityClasses, err := retrieveUsedPriorityClasses(clientset) + + if err != nil { + t.Errorf("Expected no error, got %v", err) + } + + if !contains(usedPriorityClasses, "test-pc1") { + t.Errorf("Expected 'test-pc1', got %v", usedPriorityClasses) + } +} + +func TestProcessPriorityClasses(t *testing.T) { + clientset := createTestPriorityClass(t) + unusedPriorityClasses, err := processPriorityClasses(clientset, &filters.Options{}) + if err != nil { + t.Errorf("Expected no error, got %v", err) + } + + if len(unusedPriorityClasses) != 1 { + t.Errorf("Expected 1 unused PriorityClass, got %d", len(unusedPriorityClasses)) + } + + if unusedPriorityClasses[0].Name != "test-pc1" { + t.Errorf("Expected 'test-pc1', got %s", unusedPriorityClasses[0].Name) + } +} + +func TestGetUnusedPriorityClassesStructured(t *testing.T) { + clientset := createTestPriorityClass(t) + + opts := common.Opts{ + WebhookURL: "", + Channel: "", + Token: "", + DeleteFlag: false, + NoInteractive: true, + GroupBy: "namespace", + } + + output, err := GetUnusedPriorityClasses(&filters.Options{}, clientset, "json", opts) + if err != nil { + t.Fatalf("Error calling GetUnusedPriorityClasses: %v", err) + } + + expectedOutput := map[string]map[string][]string{ + "": { + "PriorityClass": {"test-pc1"}, + }, + } + + 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 TestFilterOwnerReferencedPriorityClasses(t *testing.T) { + clientset := fake.NewSimpleClientset() + + // Create two priority classes - one owned by another resource, one standalone + // PriorityClass owned by another resource + ownedPC := CreateTestPriorityClass("owned-pc", 1000) + // Add owner reference to another resource + ownedPC.OwnerReferences = []v1.OwnerReference{ + { + Kind: "Application", + Name: "test-application", + }, + } + + // Standalone PriorityClass + standalonePC := CreateTestPriorityClass("standalone-pc", 2000) + + _, err := clientset.SchedulingV1().PriorityClasses().Create(context.TODO(), ownedPC, v1.CreateOptions{}) + if err != nil { + t.Fatalf("Error creating fake PriorityClass: %v", err) + } + + _, err = clientset.SchedulingV1().PriorityClasses().Create(context.TODO(), standalonePC, v1.CreateOptions{}) + if err != nil { + t.Fatalf("Error creating fake PriorityClass: %v", err) + } + + // Test without filter - should return both + filterOptsNoSkip := &filters.Options{IgnoreOwnerReferences: false} + unusedWithoutFilter, err := processPriorityClasses(clientset, filterOptsNoSkip) + if err != nil { + t.Fatalf("Error retrieving unused PriorityClasses: %v", err) + } + + if len(unusedWithoutFilter) != 2 { + t.Errorf("Expected 2 unused PriorityClass objects without filter, got %d", len(unusedWithoutFilter)) + } + + // Test with filter - should return only standalone + filterOptsWithSkip := &filters.Options{IgnoreOwnerReferences: true} + unusedWithFilter, err := processPriorityClasses(clientset, filterOptsWithSkip) + if err != nil { + t.Fatalf("Error retrieving unused PriorityClasses: %v", err) + } + + if len(unusedWithFilter) != 1 { + t.Errorf("Expected 1 unused PriorityClass object with filter, got %d", len(unusedWithFilter)) + } + + if unusedWithFilter[0].Name != "standalone-pc" { + t.Errorf("Expected standalone-pc to be unused, got %s", unusedWithFilter[0].Name) + } +} + +func TestSkipGlobalDefaultPriorityClasses(t *testing.T) { + clientset := fake.NewSimpleClientset() + + // Create a global default PriorityClass + globalDefaultPC := CreateTestPriorityClass("global-default-pc", 0) + globalDefaultPC.GlobalDefault = true + + // Create a regular PriorityClass + regularPC := CreateTestPriorityClass("regular-pc", 100) + + _, err := clientset.SchedulingV1().PriorityClasses().Create(context.TODO(), globalDefaultPC, v1.CreateOptions{}) + if err != nil { + t.Fatalf("Error creating fake PriorityClass: %v", err) + } + + _, err = clientset.SchedulingV1().PriorityClasses().Create(context.TODO(), regularPC, v1.CreateOptions{}) + if err != nil { + t.Fatalf("Error creating fake PriorityClass: %v", err) + } + + // Process PriorityClasses - global default should be skipped + unusedPriorityClasses, err := processPriorityClasses(clientset, &filters.Options{}) + if err != nil { + t.Fatalf("Error processing PriorityClasses: %v", err) + } + + // Should only return the regular PriorityClass as unused + if len(unusedPriorityClasses) != 1 { + t.Errorf("Expected 1 unused PriorityClass, got %d", len(unusedPriorityClasses)) + } + + if len(unusedPriorityClasses) > 0 && unusedPriorityClasses[0].Name != "regular-pc" { + t.Errorf("Expected regular-pc to be unused, got %s", unusedPriorityClasses[0].Name) + } + + // Verify that global-default-pc is not in the unused list + for _, pc := range unusedPriorityClasses { + if pc.Name == "global-default-pc" { + t.Errorf("Global default PriorityClass should not be marked as unused") + } + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kor-0.6.5/pkg/kor/statefulsets.go new/kor-0.6.6/pkg/kor/statefulsets.go --- old/kor-0.6.5/pkg/kor/statefulsets.go 2025-10-16 22:30:43.000000000 +0200 +++ new/kor-0.6.6/pkg/kor/statefulsets.go 2025-11-19 21:20:12.000000000 +0100 @@ -3,6 +3,7 @@ import ( "bytes" "context" + _ "embed" "encoding/json" "fmt" "os" @@ -14,11 +15,18 @@ "github.com/yonahd/kor/pkg/filters" ) +//go:embed exceptions/statefulsets/statefulsets.json +var statefulsetConfig []byte + func processNamespaceStatefulSets(clientset kubernetes.Interface, namespace string, filterOpts *filters.Options, opts common.Opts) ([]ResourceInfo, error) { statefulSetsList, err := clientset.AppsV1().StatefulSets(namespace).List(context.TODO(), metav1.ListOptions{LabelSelector: filterOpts.IncludeLabels}) if err != nil { return nil, err } + config, err := unmarshalConfig(statefulsetConfig) + if err != nil { + return nil, err + } var statefulSetsWithoutReplicas []ResourceInfo @@ -32,6 +40,15 @@ continue } + exceptionFound, err := isResourceException(statefulSet.Name, namespace, config.ExceptionConfigMaps) + if err != nil { + return nil, err + } + + if exceptionFound { + continue + } + status := ResourceInfo{Name: statefulSet.Name} if statefulSet.Labels["kor/used"] == "false" { ++++++ kor.obsinfo ++++++ --- /var/tmp/diff_new_pack.0HHLpj/_old 2025-11-21 16:55:21.919040475 +0100 +++ /var/tmp/diff_new_pack.0HHLpj/_new 2025-11-21 16:55:21.923040643 +0100 @@ -1,5 +1,5 @@ name: kor -version: 0.6.5 -mtime: 1760646643 -commit: 8367b39f23b83dfe75a5d59007c22a641fe26e9d +version: 0.6.6 +mtime: 1763583612 +commit: 71e4db7ff4146842ce5c34c65a407ef6d491f2a7 ++++++ vendor.tar.gz ++++++ /work/SRC/openSUSE:Factory/kor/vendor.tar.gz /work/SRC/openSUSE:Factory/.kor.new.2061/vendor.tar.gz differ: char 31, line 1
