Skip to content

Commit de7798a

Browse files
committed
Patch
1 parent aa1fb42 commit de7798a

File tree

2 files changed

+555
-5
lines changed

2 files changed

+555
-5
lines changed

pkg/k8sutil/patch_options.go

Lines changed: 129 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,12 @@ func IgnoreMutationWebhookFields() patch.CalculateOption {
3737
return current, modified, nil
3838
}
3939

40+
// Check if ScaleOps is managing resources in EITHER pod
41+
isScaleOpsManaged := isScaleOpsManagedPod(currentPod) || isScaleOpsManagedPod(modifiedPod)
42+
4043
// Remove fields that mutation webhooks commonly modify
41-
currentPod = cleanMutationWebhookFields(currentPod)
42-
modifiedPod = cleanMutationWebhookFields(modifiedPod)
44+
currentPod = cleanMutationWebhookFields(currentPod, isScaleOpsManaged)
45+
modifiedPod = cleanMutationWebhookFields(modifiedPod, isScaleOpsManaged)
4346

4447
currentBytes, err := json.Marshal(currentPod)
4548
if err != nil {
@@ -55,14 +58,137 @@ func IgnoreMutationWebhookFields() patch.CalculateOption {
5558
}
5659
}
5760

58-
func cleanMutationWebhookFields(pod *corev1.Pod) *corev1.Pod {
61+
// isScaleOpsManagedPod checks if a pod is managed by ScaleOps
62+
func isScaleOpsManagedPod(pod *corev1.Pod) bool {
63+
return pod.Annotations != nil && (pod.Annotations["scaleops.sh/managed-containers"] != "" ||
64+
pod.Annotations["scaleops.sh/pod-owner-grouping"] != "")
65+
}
66+
67+
func cleanMutationWebhookFields(pod *corev1.Pod, isScaleOpsManaged bool) *corev1.Pod {
5968
// Create a copy to avoid modifying the original
6069
cleaned := pod.DeepCopy()
6170

6271
// Remove mutation webhook annotations that should not trigger reconciliation
6372
if cleaned.Annotations != nil {
73+
// Gatekeeper annotations
6474
delete(cleaned.Annotations, "gatekeeper.sh/mutation-id")
6575
delete(cleaned.Annotations, "gatekeeper.sh/mutations")
76+
77+
// ScaleOps annotations
78+
delete(cleaned.Annotations, "scaleops.sh/admission")
79+
delete(cleaned.Annotations, "scaleops.sh/applied-policy")
80+
delete(cleaned.Annotations, "scaleops.sh/last-applied-resources")
81+
delete(cleaned.Annotations, "scaleops.sh/managed-containers")
82+
delete(cleaned.Annotations, "scaleops.sh/managed-keep-limit-cpu")
83+
delete(cleaned.Annotations, "scaleops.sh/managed-keep-limit-memory")
84+
delete(cleaned.Annotations, "scaleops.sh/origin-resources")
85+
delete(cleaned.Annotations, "scaleops.sh/pod-owner-grouping")
86+
delete(cleaned.Annotations, "scaleops.sh/pod-owner-identifier")
87+
88+
// Remove the last-applied annotation that may contain ScaleOps fields
89+
// Note: This is regenerated on updates by the k8s-objectmatcher library
90+
delete(cleaned.Annotations, "banzaicloud.com/last-applied")
91+
92+
// If annotations map is empty, set to nil to normalize comparison
93+
if len(cleaned.Annotations) == 0 {
94+
cleaned.Annotations = nil
95+
}
96+
}
97+
98+
// Remove ScaleOps labels
99+
if cleaned.Labels != nil {
100+
delete(cleaned.Labels, "scaleops.sh/applied-recommendation")
101+
delete(cleaned.Labels, "scaleops.sh/managed")
102+
delete(cleaned.Labels, "scaleops.sh/managed-unevictable")
103+
delete(cleaned.Labels, "scaleops.sh/pod-owner-grouping")
104+
delete(cleaned.Labels, "scaleops.sh/pod-owner-identifier")
105+
106+
// If labels map is empty, set to nil to normalize comparison
107+
if len(cleaned.Labels) == 0 {
108+
cleaned.Labels = nil
109+
}
110+
}
111+
112+
// Remove ScaleOps-added affinity rules (preferred scheduling only)
113+
if cleaned.Spec.Affinity != nil {
114+
if cleaned.Spec.Affinity.NodeAffinity != nil {
115+
// Remove preferred node affinity added by ScaleOps (node-packing)
116+
if cleaned.Spec.Affinity.NodeAffinity.PreferredDuringSchedulingIgnoredDuringExecution != nil {
117+
var filtered []corev1.PreferredSchedulingTerm
118+
for _, term := range cleaned.Spec.Affinity.NodeAffinity.PreferredDuringSchedulingIgnoredDuringExecution {
119+
// Keep only terms that are NOT ScaleOps node-packing preferences
120+
isScaleOpsTerm := false
121+
for _, expr := range term.Preference.MatchExpressions {
122+
if expr.Key == "scaleops.sh/node-packing" {
123+
isScaleOpsTerm = true
124+
break
125+
}
126+
}
127+
if !isScaleOpsTerm {
128+
filtered = append(filtered, term)
129+
}
130+
}
131+
if len(filtered) == 0 {
132+
cleaned.Spec.Affinity.NodeAffinity.PreferredDuringSchedulingIgnoredDuringExecution = nil
133+
} else {
134+
cleaned.Spec.Affinity.NodeAffinity.PreferredDuringSchedulingIgnoredDuringExecution = filtered
135+
}
136+
}
137+
// Clean up empty NodeAffinity
138+
if cleaned.Spec.Affinity.NodeAffinity.PreferredDuringSchedulingIgnoredDuringExecution == nil &&
139+
cleaned.Spec.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution == nil {
140+
cleaned.Spec.Affinity.NodeAffinity = nil
141+
}
142+
}
143+
144+
if cleaned.Spec.Affinity.PodAffinity != nil {
145+
// Remove preferred pod affinity added by ScaleOps (managed-unevictable)
146+
if cleaned.Spec.Affinity.PodAffinity.PreferredDuringSchedulingIgnoredDuringExecution != nil {
147+
var filtered []corev1.WeightedPodAffinityTerm
148+
for _, term := range cleaned.Spec.Affinity.PodAffinity.PreferredDuringSchedulingIgnoredDuringExecution {
149+
// Keep only terms that are NOT ScaleOps managed-unevictable preferences
150+
isScaleOpsTerm := false
151+
if term.PodAffinityTerm.LabelSelector != nil {
152+
for _, expr := range term.PodAffinityTerm.LabelSelector.MatchExpressions {
153+
if expr.Key == "scaleops.sh/managed-unevictable" {
154+
isScaleOpsTerm = true
155+
break
156+
}
157+
}
158+
}
159+
if !isScaleOpsTerm {
160+
filtered = append(filtered, term)
161+
}
162+
}
163+
if len(filtered) == 0 {
164+
cleaned.Spec.Affinity.PodAffinity.PreferredDuringSchedulingIgnoredDuringExecution = nil
165+
} else {
166+
cleaned.Spec.Affinity.PodAffinity.PreferredDuringSchedulingIgnoredDuringExecution = filtered
167+
}
168+
}
169+
// Clean up empty PodAffinity
170+
if cleaned.Spec.Affinity.PodAffinity.PreferredDuringSchedulingIgnoredDuringExecution == nil &&
171+
cleaned.Spec.Affinity.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution == nil {
172+
cleaned.Spec.Affinity.PodAffinity = nil
173+
}
174+
}
175+
176+
// Clean up empty Affinity
177+
if cleaned.Spec.Affinity.NodeAffinity == nil &&
178+
cleaned.Spec.Affinity.PodAffinity == nil &&
179+
cleaned.Spec.Affinity.PodAntiAffinity == nil {
180+
cleaned.Spec.Affinity = nil
181+
}
182+
}
183+
184+
// Clean resources if ScaleOps is managing them
185+
if isScaleOpsManaged {
186+
for i := range cleaned.Spec.InitContainers {
187+
cleaned.Spec.InitContainers[i].Resources = corev1.ResourceRequirements{}
188+
}
189+
for i := range cleaned.Spec.Containers {
190+
cleaned.Spec.Containers[i].Resources = corev1.ResourceRequirements{}
191+
}
66192
}
67193

68194
// Clean security context fields commonly set by PSPs/Gatekeeper

0 commit comments

Comments
 (0)