Skip to content

Commit 527ea2e

Browse files
committed
feat: extend cleaner utility with support for StatefulSet, DaemonSet, ReplicaSet, Job, CronJob, Ingress, PodDisruptionBudget, Role, ClusterRole, RoleBinding, and ClusterRoleBinding
1 parent b166f22 commit 527ea2e

File tree

1 file changed

+238
-11
lines changed

1 file changed

+238
-11
lines changed

pkg/utils/cleaner.go

Lines changed: 238 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ import (
44
"strings"
55

66
appsv1 "k8s.io/api/apps/v1"
7+
batchv1 "k8s.io/api/batch/v1"
78
v1 "k8s.io/api/core/v1"
9+
networkingv1 "k8s.io/api/networking/v1"
10+
policyv1 "k8s.io/api/policy/v1"
11+
rbacv1 "k8s.io/api/rbac/v1"
812
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
913
)
1014

@@ -20,6 +24,46 @@ func CleanObject(obj interface{}) {
2024
case *appsv1.Deployment:
2125
CleanDeployment(typedObj)
2226

27+
// StatefulSet
28+
case *appsv1.StatefulSet:
29+
CleanStatefulSet(typedObj)
30+
31+
// DaemonSet
32+
case *appsv1.DaemonSet:
33+
CleanDaemonSet(typedObj)
34+
35+
// ReplicaSet
36+
case *appsv1.ReplicaSet:
37+
CleanReplicaSet(typedObj)
38+
39+
// Job
40+
case *batchv1.Job:
41+
CleanJob(typedObj)
42+
43+
// CronJob
44+
case *batchv1.CronJob:
45+
CleanCronJob(typedObj)
46+
47+
// Ingress
48+
case *networkingv1.Ingress:
49+
CleanIngress(typedObj)
50+
51+
// PodDisruptionBudget
52+
case *policyv1.PodDisruptionBudget:
53+
CleanPDB(typedObj)
54+
55+
// Role and ClusterRole
56+
case *rbacv1.Role:
57+
CleanRole(typedObj)
58+
case *rbacv1.ClusterRole:
59+
CleanClusterRole(typedObj)
60+
61+
// RoleBinding and ClusterRoleBinding
62+
case *rbacv1.RoleBinding:
63+
CleanRoleBinding(typedObj)
64+
case *rbacv1.ClusterRoleBinding:
65+
CleanClusterRoleBinding(typedObj)
66+
2367
// Service
2468
case *v1.Service:
2569
CleanService(typedObj)
@@ -209,7 +253,6 @@ func inferAPIVersionAndKind(obj map[string]interface{}) {
209253
// IsSystemAnnotation checks if an annotation is system-managed
210254
func IsSystemAnnotation(key string) bool {
211255
systemPrefixes := []string{
212-
"kubernetes.io/",
213256
"k8s.io/",
214257
"control-plane.alpha.kubernetes.io/",
215258
"app.kubernetes.io/",
@@ -396,7 +439,6 @@ func CleanMetadata(meta *metav1.ObjectMeta) {
396439
meta.DeletionGracePeriodSeconds = nil
397440
meta.Generation = 0
398441
meta.ResourceVersion = ""
399-
meta.SelfLink = ""
400442
meta.UID = ""
401443
meta.ManagedFields = nil
402444

@@ -410,18 +452,203 @@ func CleanMetadata(meta *metav1.ObjectMeta) {
410452
meta.Name = name
411453
meta.Namespace = namespace
412454
meta.Labels = labels
413-
414455
// Clean annotations but keep user ones
415-
if annotations != nil {
416-
for k := range annotations {
417-
if IsSystemAnnotation(k) {
418-
delete(annotations, k)
456+
// if annotations != nil {
457+
// for k := range annotations {
458+
// if IsSystemAnnotation(k) {
459+
// delete(annotations, k)
460+
// }
461+
// }
462+
if len(annotations) == 0 {
463+
meta.Annotations = nil
464+
} else {
465+
meta.Annotations = annotations
466+
}
467+
// }
468+
}
469+
470+
// CleanStatefulSet removes server-side fields from a StatefulSet
471+
func CleanStatefulSet(statefulset *appsv1.StatefulSet) {
472+
// Remove status
473+
statefulset.Status = appsv1.StatefulSetStatus{}
474+
475+
// Clean metadata
476+
CleanMetadata(&statefulset.ObjectMeta)
477+
478+
// Clean template metadata but preserve essential fields
479+
CleanMetadata(&statefulset.Spec.Template.ObjectMeta)
480+
481+
// Set API version and kind for valid Kubernetes manifests
482+
statefulset.APIVersion = "apps/v1"
483+
statefulset.Kind = "StatefulSet"
484+
485+
// Ensure selector is preserved (it's required for statefulsets)
486+
if statefulset.Spec.Selector == nil || len(statefulset.Spec.Selector.MatchLabels) == 0 {
487+
// If no selector, create one that matches template labels
488+
if statefulset.Spec.Template.Labels != nil && len(statefulset.Spec.Template.Labels) > 0 {
489+
if statefulset.Spec.Selector == nil {
490+
statefulset.Spec.Selector = &metav1.LabelSelector{}
491+
}
492+
statefulset.Spec.Selector.MatchLabels = statefulset.Spec.Template.Labels
493+
}
494+
}
495+
}
496+
497+
// CleanDaemonSet removes server-side fields from a DaemonSet
498+
func CleanDaemonSet(daemonset *appsv1.DaemonSet) {
499+
// Remove status
500+
daemonset.Status = appsv1.DaemonSetStatus{}
501+
502+
// Clean metadata
503+
CleanMetadata(&daemonset.ObjectMeta)
504+
505+
// Clean template metadata but preserve essential fields
506+
CleanMetadata(&daemonset.Spec.Template.ObjectMeta)
507+
508+
// Set API version and kind for valid Kubernetes manifests
509+
daemonset.APIVersion = "apps/v1"
510+
daemonset.Kind = "DaemonSet"
511+
512+
// Ensure selector is preserved (it's required for daemonsets)
513+
if daemonset.Spec.Selector == nil || len(daemonset.Spec.Selector.MatchLabels) == 0 {
514+
// If no selector, create one that matches template labels
515+
if daemonset.Spec.Template.Labels != nil && len(daemonset.Spec.Template.Labels) > 0 {
516+
if daemonset.Spec.Selector == nil {
517+
daemonset.Spec.Selector = &metav1.LabelSelector{}
419518
}
519+
daemonset.Spec.Selector.MatchLabels = daemonset.Spec.Template.Labels
420520
}
421-
if len(annotations) == 0 {
422-
meta.Annotations = nil
423-
} else {
424-
meta.Annotations = annotations
521+
}
522+
}
523+
524+
// CleanReplicaSet removes server-side fields from a ReplicaSet
525+
func CleanReplicaSet(replicaset *appsv1.ReplicaSet) {
526+
// Remove status
527+
replicaset.Status = appsv1.ReplicaSetStatus{}
528+
529+
// Clean metadata
530+
CleanMetadata(&replicaset.ObjectMeta)
531+
532+
// Clean template metadata but preserve essential fields
533+
CleanMetadata(&replicaset.Spec.Template.ObjectMeta)
534+
535+
// Set API version and kind for valid Kubernetes manifests
536+
replicaset.APIVersion = "apps/v1"
537+
replicaset.Kind = "ReplicaSet"
538+
539+
// Ensure selector is preserved (it's required for replicasets)
540+
if replicaset.Spec.Selector == nil || len(replicaset.Spec.Selector.MatchLabels) == 0 {
541+
// If no selector, create one that matches template labels
542+
if replicaset.Spec.Template.Labels != nil && len(replicaset.Spec.Template.Labels) > 0 {
543+
if replicaset.Spec.Selector == nil {
544+
replicaset.Spec.Selector = &metav1.LabelSelector{}
545+
}
546+
replicaset.Spec.Selector.MatchLabels = replicaset.Spec.Template.Labels
425547
}
426548
}
427549
}
550+
551+
// CleanJob removes server-side fields from a Job
552+
func CleanJob(job *batchv1.Job) {
553+
// Remove status
554+
job.Status = batchv1.JobStatus{}
555+
556+
// Clean metadata
557+
CleanMetadata(&job.ObjectMeta)
558+
559+
// Clean template metadata but preserve essential fields
560+
CleanMetadata(&job.Spec.Template.ObjectMeta)
561+
562+
// Set API version and kind for valid Kubernetes manifests
563+
job.APIVersion = "batch/v1"
564+
job.Kind = "Job"
565+
566+
// Reset fields that are typically server-assigned
567+
job.Spec.Selector = nil // Selector is automatically generated for Jobs
568+
}
569+
570+
// CleanCronJob removes server-side fields from a CronJob
571+
func CleanCronJob(cronjob *batchv1.CronJob) {
572+
// Remove status
573+
cronjob.Status = batchv1.CronJobStatus{}
574+
575+
// Clean metadata
576+
CleanMetadata(&cronjob.ObjectMeta)
577+
578+
// Clean template metadata but preserve essential fields
579+
CleanMetadata(&cronjob.Spec.JobTemplate.ObjectMeta)
580+
CleanMetadata(&cronjob.Spec.JobTemplate.Spec.Template.ObjectMeta)
581+
582+
// Set API version and kind for valid Kubernetes manifests
583+
cronjob.APIVersion = "batch/v1"
584+
cronjob.Kind = "CronJob"
585+
586+
// Reset fields that are typically server-assigned
587+
cronjob.Spec.JobTemplate.Spec.Selector = nil // Selector is automatically generated for Jobs
588+
}
589+
590+
// CleanIngress removes server-side fields from an Ingress
591+
func CleanIngress(ingress *networkingv1.Ingress) {
592+
// Remove status
593+
ingress.Status = networkingv1.IngressStatus{}
594+
595+
// Clean metadata
596+
CleanMetadata(&ingress.ObjectMeta)
597+
598+
// Set API version and kind for valid Kubernetes manifests
599+
ingress.APIVersion = "networking.k8s.io/v1"
600+
ingress.Kind = "Ingress"
601+
}
602+
603+
// CleanPDB removes server-side fields from a PodDisruptionBudget
604+
func CleanPDB(pdb *policyv1.PodDisruptionBudget) {
605+
// Remove status
606+
pdb.Status = policyv1.PodDisruptionBudgetStatus{}
607+
608+
// Clean metadata
609+
CleanMetadata(&pdb.ObjectMeta)
610+
611+
// Set API version and kind for valid Kubernetes manifests
612+
pdb.APIVersion = "policy/v1"
613+
pdb.Kind = "PodDisruptionBudget"
614+
}
615+
616+
// CleanRole removes server-side fields from a Role
617+
func CleanRole(role *rbacv1.Role) {
618+
// Clean metadata
619+
CleanMetadata(&role.ObjectMeta)
620+
621+
// Set API version and kind for valid Kubernetes manifests
622+
role.APIVersion = "rbac.authorization.k8s.io/v1"
623+
role.Kind = "Role"
624+
}
625+
626+
// CleanClusterRole removes server-side fields from a ClusterRole
627+
func CleanClusterRole(clusterRole *rbacv1.ClusterRole) {
628+
// Clean metadata
629+
CleanMetadata(&clusterRole.ObjectMeta)
630+
631+
// Set API version and kind for valid Kubernetes manifests
632+
clusterRole.APIVersion = "rbac.authorization.k8s.io/v1"
633+
clusterRole.Kind = "ClusterRole"
634+
}
635+
636+
// CleanRoleBinding removes server-side fields from a RoleBinding
637+
func CleanRoleBinding(roleBinding *rbacv1.RoleBinding) {
638+
// Clean metadata
639+
CleanMetadata(&roleBinding.ObjectMeta)
640+
641+
// Set API version and kind for valid Kubernetes manifests
642+
roleBinding.APIVersion = "rbac.authorization.k8s.io/v1"
643+
roleBinding.Kind = "RoleBinding"
644+
}
645+
646+
// CleanClusterRoleBinding removes server-side fields from a ClusterRoleBinding
647+
func CleanClusterRoleBinding(clusterRoleBinding *rbacv1.ClusterRoleBinding) {
648+
// Clean metadata
649+
CleanMetadata(&clusterRoleBinding.ObjectMeta)
650+
651+
// Set API version and kind for valid Kubernetes manifests
652+
clusterRoleBinding.APIVersion = "rbac.authorization.k8s.io/v1"
653+
clusterRoleBinding.Kind = "ClusterRoleBinding"
654+
}

0 commit comments

Comments
 (0)