Skip to content

Commit b922c91

Browse files
jvanzCopilot
andcommitted
refactor: remove auto-injected affinity, add independent affinity config
Remove the controller's auto-injected pod anti-affinity logic (mergeAffinityWithHostNetworkAntiAffinity, portsOverlap, computeHostNetworkConflicts) and the associated controller port fields that were only used for port-conflict detection. Users are now responsible for configuring affinity/anti-affinity rules independently for the controller and the default PolicyServer through their respective Helm charts. Each chart provides a dedicated 'affinity' key that takes precedence over 'global.affinity' for backward compatibility. The --host-network flag and CRD port fields (webhookPort, readinessProbePort, metricsPort) are preserved unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 6089afc commit b922c91

11 files changed

Lines changed: 113 additions & 234 deletions

File tree

charts/kubewarden-controller/templates/_helpers.tpl

Lines changed: 7 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -184,38 +184,16 @@ are configured.
184184

185185
{{/*
186186
Compute the effective affinity for the controller deployment.
187-
When hostNetwork is enabled, a required podAntiAffinity rule is injected to
188-
forbid multiple controller pods from being scheduled on the same node, avoiding
189-
host-port conflicts between controller replicas. The rule matches only
190-
controller pods using the chart's selector labels.
187+
Uses the controller-specific affinity if set, otherwise falls back to
188+
global.affinity for backward compatibility.
191189
192-
PolicyServer anti-affinity (same-PS replicas, cross-PS conflicts, and
193-
controller-PS port overlap) is managed separately by the controller reconciler
194-
at runtime, not by this helper.
195-
196-
When global.affinity is provided by the user:
197-
- podAntiAffinity fully replaces the auto-generated rule.
198-
- podAffinity and nodeAffinity are carried through unchanged.
199-
When hostNetwork is disabled, global.affinity is passed through as-is.
190+
NOTE: When hostNetwork is enabled, users are responsible for setting
191+
appropriate podAntiAffinity rules to prevent host-port conflicts between
192+
controller replicas on the same node.
200193
*/}}
201194
{{- define "kubewarden-controller.effectiveAffinity" -}}
202-
{{- if .Values.hostNetwork -}}
203-
{{- $matchLabels := (include "kubewarden-controller.selectorLabels" . | fromYaml) -}}
204-
{{- $affinityTerm := dict "labelSelector" (dict "matchLabels" $matchLabels) "topologyKey" "kubernetes.io/hostname" -}}
205-
{{- $autoAntiAffinity := dict "requiredDuringSchedulingIgnoredDuringExecution" (list $affinityTerm) -}}
206-
{{- $affinity := dict "podAntiAffinity" $autoAntiAffinity -}}
207-
{{- if .Values.global.affinity -}}
208-
{{- if hasKey .Values.global.affinity "podAntiAffinity" -}}
209-
{{- $_ := set $affinity "podAntiAffinity" .Values.global.affinity.podAntiAffinity -}}
210-
{{- end -}}
211-
{{- if hasKey .Values.global.affinity "podAffinity" -}}
212-
{{- $_ := set $affinity "podAffinity" .Values.global.affinity.podAffinity -}}
213-
{{- end -}}
214-
{{- if hasKey .Values.global.affinity "nodeAffinity" -}}
215-
{{- $_ := set $affinity "nodeAffinity" .Values.global.affinity.nodeAffinity -}}
216-
{{- end -}}
217-
{{- end -}}
218-
{{- toYaml $affinity -}}
195+
{{- if .Values.affinity -}}
196+
{{- toYaml .Values.affinity -}}
219197
{{- else if .Values.global.affinity -}}
220198
{{- toYaml .Values.global.affinity -}}
221199
{{- end -}}

charts/kubewarden-controller/tests/host_network_test.yaml

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,23 +27,50 @@ tests:
2727
path: spec.template.spec.containers[0].args
2828
content: "--host-network"
2929

30-
- it: "should inject required podAntiAffinity using controller selector labels when hostNetwork is true"
30+
- it: "should not set affinity when hostNetwork is true and no affinity is configured"
3131
set:
3232
hostNetwork: true
33+
asserts:
34+
- notExists:
35+
path: spec.template.spec.affinity
36+
37+
- it: "should not set affinity when hostNetwork is false and no affinity is set"
38+
asserts:
39+
- notExists:
40+
path: spec.template.spec.affinity
41+
42+
- it: "should use controller-specific affinity when set"
43+
set:
44+
affinity:
45+
podAntiAffinity:
46+
requiredDuringSchedulingIgnoredDuringExecution:
47+
- labelSelector:
48+
matchLabels:
49+
app.kubernetes.io/name: kubewarden-controller
50+
topologyKey: kubernetes.io/hostname
3351
asserts:
3452
- equal:
3553
path: spec.template.spec.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution[0].topologyKey
3654
value: kubernetes.io/hostname
3755
- equal:
3856
path: spec.template.spec.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution[0].labelSelector.matchLabels["app.kubernetes.io/name"]
3957
value: kubewarden-controller
40-
- exists:
41-
path: spec.template.spec.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution[0].labelSelector.matchLabels["app.kubernetes.io/instance"]
4258

43-
- it: "should not inject podAntiAffinity when hostNetwork is false and no affinity is set"
59+
- it: "should fall back to global.affinity when controller-specific affinity is not set"
60+
set:
61+
global.affinity:
62+
nodeAffinity:
63+
requiredDuringSchedulingIgnoredDuringExecution:
64+
nodeSelectorTerms:
65+
- matchExpressions:
66+
- key: kubernetes.io/os
67+
operator: In
68+
values:
69+
- linux
4470
asserts:
45-
- notExists:
46-
path: spec.template.spec.affinity
71+
- equal:
72+
path: spec.template.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].key
73+
value: kubernetes.io/os
4774

4875
- it: "should use custom ports when overridden in values"
4976
set:

charts/kubewarden-controller/values.schema.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
"alwaysAcceptAdmissionReviewsOnDeploymentsNamespace": {
1515
"type": "boolean"
1616
},
17+
"affinity": {
18+
"type": "object",
19+
"description": "Affinity rules for the controller pod. Takes precedence over global.affinity."
20+
},
1721
"auditScanner": {
1822
"type": "object",
1923
"properties": {

charts/kubewarden-controller/values.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,18 @@ preDeleteHook:
151151
# evaluations that could interfere with the Kubewarden stack running in the
152152
# admission controller namespace.
153153
alwaysAcceptAdmissionReviewsOnDeploymentsNamespace: true
154+
# affinity configures affinity rules for the controller pod.
155+
# This takes precedence over global.affinity when set.
156+
# When hostNetwork is enabled, users should set appropriate podAntiAffinity
157+
# rules here to prevent host-port conflicts between controller replicas.
158+
# affinity:
159+
# podAntiAffinity:
160+
# requiredDuringSchedulingIgnoredDuringExecution:
161+
# - labelSelector:
162+
# matchLabels:
163+
# app.kubernetes.io/name: kubewarden-controller
164+
# topologyKey: kubernetes.io/hostname
165+
affinity: {}
154166
# hostNetwork enables host network mode for the controller pod and all
155167
# PolicyServer pods managed by this controller.
156168
# WARNING: enabling this increases the attack surface by exposing webhook

charts/kubewarden-defaults/templates/policyserver-default.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ spec:
2020
{{- if .Values.policyServer.maxUnavailable }}
2121
maxUnavailable: {{ .Values.policyServer.maxUnavailable }}
2222
{{- end }}
23-
{{- if .Values.global.affinity }}
23+
{{- if .Values.policyServer.affinity }}
24+
affinity: {{ .Values.policyServer.affinity | toYaml | nindent 4 }}
25+
{{- else if .Values.global.affinity }}
2426
affinity: {{ .Values.global.affinity | toYaml | nindent 4 }}
2527
{{- end }}
2628
{{- if .Values.global.tolerations }}

charts/kubewarden-defaults/tests/host_network_test.yaml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,41 @@ tests:
5050
- equal:
5151
path: spec.metricsPort
5252
value: 9080
53+
54+
- it: "should not set affinity by default"
55+
asserts:
56+
- notExists:
57+
path: spec.affinity
58+
59+
- it: "should use policyServer.affinity when set"
60+
set:
61+
policyServer.affinity:
62+
podAntiAffinity:
63+
requiredDuringSchedulingIgnoredDuringExecution:
64+
- labelSelector:
65+
matchLabels:
66+
kubewarden/policy-server: default
67+
topologyKey: kubernetes.io/hostname
68+
asserts:
69+
- equal:
70+
path: spec.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution[0].topologyKey
71+
value: kubernetes.io/hostname
72+
- equal:
73+
path: spec.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution[0].labelSelector.matchLabels["kubewarden/policy-server"]
74+
value: default
75+
76+
- it: "should fall back to global.affinity when policyServer.affinity is not set"
77+
set:
78+
global.affinity:
79+
nodeAffinity:
80+
requiredDuringSchedulingIgnoredDuringExecution:
81+
nodeSelectorTerms:
82+
- matchExpressions:
83+
- key: kubernetes.io/os
84+
operator: In
85+
values:
86+
- linux
87+
asserts:
88+
- equal:
89+
path: spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].key
90+
value: kubernetes.io/os

charts/kubewarden-defaults/values.schema.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@
3030
"minimum": 1,
3131
"maximum": 65535,
3232
"description": "Port for the PolicyServer metrics endpoint. When omitted, the controller uses the default (8080)."
33+
},
34+
"affinity": {
35+
"type": "object",
36+
"description": "Affinity rules for the default PolicyServer. Takes precedence over global.affinity."
3337
}
3438
},
3539
"anyOf": [

charts/kubewarden-defaults/values.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,18 @@ policyServer:
199199
# webhookPort: 9443
200200
# readinessProbePort: 9081
201201
# metricsPort: 9080
202+
# affinity configures affinity rules for the default PolicyServer.
203+
# This takes precedence over global.affinity when set.
204+
# When hostNetwork is enabled, users should set appropriate podAntiAffinity
205+
# rules here to prevent host-port conflicts between PolicyServer replicas.
206+
# affinity:
207+
# podAntiAffinity:
208+
# requiredDuringSchedulingIgnoredDuringExecution:
209+
# - labelSelector:
210+
# matchLabels:
211+
# kubewarden/policy-server: default
212+
# topologyKey: kubernetes.io/hostname
213+
affinity: {}
202214
crdVersion: "policies.kubewarden.io/v1"
203215
recommendedPolicies:
204216
enabled: False

cmd/controller/main.go

Lines changed: 0 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,8 @@ import (
2121
"errors"
2222
"flag"
2323
"fmt"
24-
"net"
2524
"os"
2625
"path/filepath"
27-
"strconv"
2826
"strings"
2927

3028
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
@@ -90,16 +88,6 @@ type Configuration struct {
9088
// the Kubernetes API server cannot reach pod-network webhook endpoints
9189
// (e.g. clusters using a non-VPC CNI with NAT).
9290
HostNetwork bool
93-
// WebhookServerPort is the port the controller webhook server listens on.
94-
WebhookServerPort int
95-
// ProbeAddr is the address the probe endpoint binds to (e.g. ":8081").
96-
ProbeAddr string
97-
// MetricsAddr is the address the metrics endpoint binds to (e.g. ":8088").
98-
MetricsAddr string
99-
// ControllerHealthProbePort is the parsed port from ProbeAddr.
100-
ControllerHealthProbePort int32
101-
// ControllerMetricsPort is the parsed port from MetricsAddr.
102-
ControllerMetricsPort int32
10391
}
10492

10593
func init() {
@@ -167,29 +155,8 @@ func main() {
167155
flag.Parse()
168156
mgrOpts.EnableMutualTLS = config.ClientCAConfigMapName != ""
169157
config.ImagePullSecrets = parseImagePullSecrets(imagePullSecretsFlag)
170-
config.WebhookServerPort = mgrOpts.WebhookServerPort
171-
config.ProbeAddr = mgrOpts.ProbeAddr
172-
config.MetricsAddr = mgrOpts.MetricsAddr
173-
config.ControllerHealthProbePort = parsePortFromAddress(mgrOpts.ProbeAddr)
174-
config.ControllerMetricsPort = parsePortFromAddress(mgrOpts.MetricsAddr)
175158
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
176159

177-
if config.ControllerHealthProbePort == 0 {
178-
setupLog.Error(fmt.Errorf("cannot extract port from %q", mgrOpts.ProbeAddr), "invalid --health-probe-bind-address")
179-
retcode = 1
180-
return
181-
}
182-
if config.ControllerMetricsPort == 0 {
183-
setupLog.Error(fmt.Errorf("cannot extract port from %q", mgrOpts.MetricsAddr), "invalid --metrics-bind-address")
184-
retcode = 1
185-
return
186-
}
187-
if config.WebhookServerPort < 1 || config.WebhookServerPort > 65535 {
188-
setupLog.Error(fmt.Errorf("port %d is out of range 1-65535", config.WebhookServerPort), "invalid --webhook-server-port")
189-
retcode = 1
190-
return
191-
}
192-
193160
if enableMetrics {
194161
shutdown, err := metrics.New()
195162
if err != nil {
@@ -358,9 +325,6 @@ func setupReconcilers(mgr ctrl.Manager,
358325
ClientCAConfigMapName: config.ClientCAConfigMapName,
359326
ImagePullSecrets: config.ImagePullSecrets,
360327
HostNetwork: config.HostNetwork,
361-
ControllerWebhookPort: int32(config.WebhookServerPort), //nolint:gosec // validated above: 1-65535
362-
ControllerHealthProbePort: config.ControllerHealthProbePort,
363-
ControllerMetricsPort: config.ControllerMetricsPort,
364328
}).SetupWithManager(mgr); err != nil {
365329
return errors.Join(errors.New("unable to create PolicyServer controller"), err)
366330
}
@@ -453,17 +417,3 @@ func parseImagePullSecrets(s string) []corev1.LocalObjectReference {
453417
}
454418
return refs
455419
}
456-
457-
// parsePortFromAddress extracts the port number from an address string like
458-
// ":8081" or "0.0.0.0:8088". Returns 0 if parsing fails.
459-
func parsePortFromAddress(addr string) int32 {
460-
_, portStr, err := net.SplitHostPort(addr)
461-
if err != nil {
462-
return 0
463-
}
464-
port, err := strconv.ParseInt(portStr, 10, 32)
465-
if err != nil {
466-
return 0
467-
}
468-
return int32(port)
469-
}

internal/controller/policyserver_controller.go

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,6 @@ type PolicyServerReconciler struct {
7171
// When true, the controller also sets dnsPolicy to ClusterFirstWithHostNet
7272
// so that in-cluster DNS resolution keeps working.
7373
HostNetwork bool
74-
// ControllerWebhookPort is the port the controller webhook server listens on.
75-
// Used for port-conflict detection when injecting anti-affinity.
76-
ControllerWebhookPort int32
77-
// ControllerHealthProbePort is the port the controller health probe binds to.
78-
ControllerHealthProbePort int32
79-
// ControllerMetricsPort is the port the controller metrics endpoint binds to.
80-
ControllerMetricsPort int32
8174
}
8275

8376
// TelemetryConfiguration is a struct that contains the configuration for the

0 commit comments

Comments
 (0)