Skip to content

Commit bd7e005

Browse files
committed
feat(controller): support hostNetwork
This commit adds the support to enable host network in the Kubewarden stack. This is done by enabling a CLI flag in the controller. Once this is done, all the policy server deployments are configured to use host network as well. Furthermore, to allow user to fix port conflicts issues, 3 new fields have been added to the policy server spec. This fields allow users to define the ports to be used by the policy server deployment. Assisted-by: Github Copilot Signed-off-by: José Guilherme Vanz <jguilhermevanz@suse.com>
1 parent 9462fe1 commit bd7e005

28 files changed

Lines changed: 1175 additions & 123 deletions

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ helm-unittest:
5454
test-e2e: controller-image audit-scanner-image policy-server-image
5555
$(GO_BUILD_ENV) go test ./e2e/ -v
5656

57+
.PHONY: test-all
58+
test-all: test helm-unittest test-e2e
59+
5760
.PHONY: fmt-go
5861
fmt-go:
5962
$(GO_BUILD_ENV) go fmt ./...

api/policies/v1/factories.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,9 @@ type PolicyServerBuilder struct {
487487
requests corev1.ResourceList
488488
sigstoreTrustConfigMap string
489489
namespacedPoliciesCapabilities []string
490+
webhookPort *int32
491+
readinessProbePort *int32
492+
metricsPort *int32
490493
}
491494

492495
func NewPolicyServerFactory() *PolicyServerBuilder {
@@ -535,6 +538,21 @@ func (f *PolicyServerBuilder) WithNamespacedPoliciesCapabilities(capabilities []
535538
return f
536539
}
537540

541+
func (f *PolicyServerBuilder) WithWebhookPort(port int32) *PolicyServerBuilder {
542+
f.webhookPort = &port
543+
return f
544+
}
545+
546+
func (f *PolicyServerBuilder) WithReadinessProbePort(port int32) *PolicyServerBuilder {
547+
f.readinessProbePort = &port
548+
return f
549+
}
550+
551+
func (f *PolicyServerBuilder) WithMetricsPort(port int32) *PolicyServerBuilder {
552+
f.metricsPort = &port
553+
return f
554+
}
555+
538556
func (f *PolicyServerBuilder) Build() *PolicyServer {
539557
policyServer := PolicyServer{
540558
ObjectMeta: metav1.ObjectMeta{
@@ -559,6 +577,9 @@ func (f *PolicyServerBuilder) Build() *PolicyServer {
559577
Requests: f.requests,
560578
SigstoreTrustConfig: f.sigstoreTrustConfigMap,
561579
NamespacedPoliciesCapabilities: f.namespacedPoliciesCapabilities,
580+
WebhookPort: f.webhookPort,
581+
ReadinessProbePort: f.readinessProbePort,
582+
MetricsPort: f.metricsPort,
562583
Env: []corev1.EnvVar{
563584
{
564585
Name: "KUBEWARDEN_LOG_LEVEL",

api/policies/v1/policyserver_types.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,28 @@ type PolicyServerSpec struct {
150150
// - Specific capability paths (e.g. "oci/v1/verify", "net/v1/dns_lookup_host")
151151
// +optional
152152
NamespacedPoliciesCapabilities []string `json:"namespacedPoliciesCapabilities,omitempty"`
153+
154+
// Port where the policy server listens for incoming webhook requests.
155+
// When unset, defaults to 8443. This is the port the Kubernetes API server
156+
// reaches when evaluating admission requests.
157+
// +optional
158+
// +kubebuilder:validation:Minimum=1
159+
// +kubebuilder:validation:Maximum=65535
160+
WebhookPort *int32 `json:"webhookPort,omitempty"`
161+
162+
// Port used by the policy server to expose the readiness probe endpoint.
163+
// When unset, defaults to 8081.
164+
// +optional
165+
// +kubebuilder:validation:Minimum=1
166+
// +kubebuilder:validation:Maximum=65535
167+
ReadinessProbePort *int32 `json:"readinessProbePort,omitempty"`
168+
169+
// Port used by the policy server to expose the metrics endpoint.
170+
// When unset, defaults to 8080. Only relevant when metrics are enabled.
171+
// +optional
172+
// +kubebuilder:validation:Minimum=1
173+
// +kubebuilder:validation:Maximum=65535
174+
MetricsPort *int32 `json:"metricsPort,omitempty"`
153175
}
154176

155177
type ReconciliationTransitionReason string
@@ -222,6 +244,34 @@ func (ps *PolicyServer) AppLabel() string {
222244
return "kubewarden-" + ps.NameWithPrefix()
223245
}
224246

247+
// EffectiveWebhookPort returns the port the policy server listens on for
248+
// admission webhook requests, using the CRD field when set or the default constant.
249+
func (ps *PolicyServer) EffectiveWebhookPort() int32 {
250+
if ps.Spec.WebhookPort != nil {
251+
return *ps.Spec.WebhookPort
252+
}
253+
return constants.PolicyServerListenPort
254+
}
255+
256+
// EffectiveReadinessProbePort returns the port used for the readiness probe,
257+
// using the CRD field when set or the default constant.
258+
func (ps *PolicyServer) EffectiveReadinessProbePort() int32 {
259+
if ps.Spec.ReadinessProbePort != nil {
260+
return *ps.Spec.ReadinessProbePort
261+
}
262+
return constants.PolicyServerReadinessProbePort
263+
}
264+
265+
// EffectiveMetricsPort returns the port used to expose the metrics endpoint.
266+
// It returns the CRD-level override when set, otherwise it falls back to defaultPort
267+
// (which is typically the controller-wide default configured via environment variable).
268+
func (ps *PolicyServer) EffectiveMetricsPort(defaultPort int32) int32 {
269+
if ps.Spec.MetricsPort != nil {
270+
return *ps.Spec.MetricsPort
271+
}
272+
return defaultPort
273+
}
274+
225275
// CommonLabels returns the common labels to be used with the resources
226276
// associated to a Policy Server. The labels defined follow
227277
// Kubernetes guidelines: https://kubernetes.io/docs/concepts/overview/working-with-objects/common-labels/#labels

api/policies/v1/policyserver_webhook.go

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ var capabilityTree = capabilityNode{
7979
}
8080

8181
// SetupWebhookWithManager registers the PolicyServer webhook with the controller manager.
82-
func (ps *PolicyServer) SetupWebhookWithManager(mgr ctrl.Manager, deploymentsNamespace string) error {
82+
// defaultMetricsPort is the controller-wide default metrics port (possibly overridden via env
83+
// var) and is used by the validator to detect port conflicts involving the metrics port.
84+
func (ps *PolicyServer) SetupWebhookWithManager(mgr ctrl.Manager, deploymentsNamespace string, defaultMetricsPort int32) error {
8385
logger := mgr.GetLogger().WithName("policyserver-webhook")
8486

8587
err := ctrl.NewWebhookManagedBy(mgr, ps).
@@ -88,6 +90,7 @@ func (ps *PolicyServer) SetupWebhookWithManager(mgr ctrl.Manager, deploymentsNam
8890
}).
8991
WithValidator(&policyServerValidator{
9092
deploymentsNamespace: deploymentsNamespace,
93+
defaultMetricsPort: defaultMetricsPort,
9194
k8sClient: mgr.GetClient(),
9295
logger: logger,
9396
}).
@@ -122,8 +125,12 @@ func (d *policyServerDefaulter) Default(_ context.Context, policyServer *PolicyS
122125
// polyServerCustomValidator validates PolicyServers when they are created, updated, or deleted.
123126
type policyServerValidator struct {
124127
deploymentsNamespace string
125-
k8sClient client.Client
126-
logger logr.Logger
128+
// defaultMetricsPort is the controller-wide default for the PolicyServer metrics port,
129+
// which may be overridden via the KUBEWARDEN_POLICY_SERVER_SERVICES_METRICS_PORT env var.
130+
// It is used when spec.metricsPort is not explicitly set on a PolicyServer.
131+
defaultMetricsPort int32
132+
k8sClient client.Client
133+
logger logr.Logger
127134
}
128135

129136
// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type.
@@ -175,6 +182,7 @@ func (v *policyServerValidator) validate(ctx context.Context, policyServer *Poli
175182

176183
allErrs = append(allErrs, validateLimitsAndRequests(policyServer.Spec.Limits, policyServer.Spec.Requests)...)
177184
allErrs = append(allErrs, validateNamespacedPoliciesCapabilities(policyServer.Spec.NamespacedPoliciesCapabilities)...)
185+
allErrs = append(allErrs, v.validatePorts(policyServer)...)
178186

179187
if len(allErrs) == 0 {
180188
return nil
@@ -274,7 +282,6 @@ func validateNamespacedPoliciesCapabilities(capabilities []string) field.ErrorLi
274282
allErrs = append(allErrs, err)
275283
}
276284
}
277-
278285
return allErrs
279286
}
280287

@@ -327,3 +334,39 @@ func validateSingleCapability(pattern string, path *field.Path) *field.Error {
327334
fmt.Sprintf("%q is not a complete capability path; use %q to allow all capabilities under it",
328335
pattern, pattern+"/*"))
329336
}
337+
338+
// validatePorts checks that the port fields in the PolicyServer spec do not
339+
// conflict with each other. Effective ports (field value or controller-configured default)
340+
// are compared so that partial overrides are also caught. The validator's defaultMetricsPort
341+
// is used so that the env-var-configured controller default is taken into account.
342+
func (v *policyServerValidator) validatePorts(policyServer *PolicyServer) field.ErrorList {
343+
var allErrs field.ErrorList
344+
345+
webhookPort := policyServer.EffectiveWebhookPort()
346+
readinessPort := policyServer.EffectiveReadinessProbePort()
347+
metricsPort := policyServer.EffectiveMetricsPort(v.defaultMetricsPort)
348+
349+
if webhookPort == readinessPort {
350+
allErrs = append(allErrs, field.Invalid(
351+
field.NewPath("spec").Child("readinessProbePort"),
352+
readinessPort,
353+
fmt.Sprintf("readinessProbePort must differ from webhookPort (%d)", webhookPort),
354+
))
355+
}
356+
if webhookPort == metricsPort {
357+
allErrs = append(allErrs, field.Invalid(
358+
field.NewPath("spec").Child("metricsPort"),
359+
metricsPort,
360+
fmt.Sprintf("metricsPort must differ from webhookPort (%d)", webhookPort),
361+
))
362+
}
363+
if readinessPort == metricsPort {
364+
allErrs = append(allErrs, field.Invalid(
365+
field.NewPath("spec").Child("metricsPort"),
366+
metricsPort,
367+
fmt.Sprintf("metricsPort must differ from readinessProbePort (%d)", readinessPort),
368+
))
369+
}
370+
371+
return allErrs
372+
}

api/policies/v1/policyserver_webhook_test.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,3 +412,104 @@ func TestPolicyServerValidateNamespacedPoliciesCapabilities(t *testing.T) {
412412
})
413413
}
414414
}
415+
416+
func TestValidatePorts(t *testing.T) {
417+
tests := []struct {
418+
name string
419+
webhookPort *int32
420+
readiness *int32
421+
metrics *int32
422+
defaultMetricsPort int32
423+
errContains string
424+
}{
425+
{
426+
name: "all defaults, no conflict",
427+
defaultMetricsPort: constants.PolicyServerMetricsPort,
428+
errContains: "",
429+
},
430+
{
431+
name: "webhookPort equals readinessProbePort",
432+
webhookPort: ptr.To[int32](8081),
433+
readiness: ptr.To[int32](8081),
434+
defaultMetricsPort: constants.PolicyServerMetricsPort,
435+
errContains: "readinessProbePort must differ from webhookPort",
436+
},
437+
{
438+
name: "webhookPort equals metricsPort",
439+
webhookPort: ptr.To[int32](8080),
440+
metrics: ptr.To[int32](8080),
441+
defaultMetricsPort: constants.PolicyServerMetricsPort,
442+
errContains: "metricsPort must differ from webhookPort",
443+
},
444+
{
445+
name: "readinessProbePort equals metricsPort",
446+
readiness: ptr.To[int32](9000),
447+
metrics: ptr.To[int32](9000),
448+
defaultMetricsPort: constants.PolicyServerMetricsPort,
449+
errContains: "metricsPort must differ from readinessProbePort",
450+
},
451+
{
452+
name: "all three ports the same",
453+
webhookPort: ptr.To[int32](9999),
454+
readiness: ptr.To[int32](9999),
455+
metrics: ptr.To[int32](9999),
456+
defaultMetricsPort: constants.PolicyServerMetricsPort,
457+
errContains: "readinessProbePort must differ from webhookPort",
458+
},
459+
{
460+
name: "all three ports distinct custom values",
461+
webhookPort: ptr.To[int32](9443),
462+
readiness: ptr.To[int32](9081),
463+
metrics: ptr.To[int32](9080),
464+
defaultMetricsPort: constants.PolicyServerMetricsPort,
465+
errContains: "",
466+
},
467+
{
468+
// When the controller default metrics port is overridden via env var to 9090,
469+
// a PolicyServer with readinessProbePort=9090 (and no explicit metricsPort)
470+
// must be rejected because the effective metrics port is 9090.
471+
name: "readinessProbePort conflicts with env-var-configured default metrics port",
472+
readiness: ptr.To[int32](9090),
473+
defaultMetricsPort: 9090,
474+
errContains: "metricsPort must differ from readinessProbePort",
475+
},
476+
{
477+
// When the controller default metrics port is overridden via env var to 9090,
478+
// a PolicyServer with no explicit port overrides should be valid.
479+
name: "all defaults with non-standard env-var default metrics port, no conflict",
480+
defaultMetricsPort: 9090,
481+
errContains: "",
482+
},
483+
}
484+
485+
for _, test := range tests {
486+
t.Run(test.name, func(t *testing.T) {
487+
k8sClient := fake.NewClientBuilder().Build()
488+
builder := NewPolicyServerFactory()
489+
if test.webhookPort != nil {
490+
builder = builder.WithWebhookPort(*test.webhookPort)
491+
}
492+
if test.readiness != nil {
493+
builder = builder.WithReadinessProbePort(*test.readiness)
494+
}
495+
if test.metrics != nil {
496+
builder = builder.WithMetricsPort(*test.metrics)
497+
}
498+
policyServer := builder.Build()
499+
500+
validator := policyServerValidator{
501+
deploymentsNamespace: "default",
502+
defaultMetricsPort: test.defaultMetricsPort,
503+
k8sClient: k8sClient,
504+
logger: logr.Discard(),
505+
}
506+
err := validator.validate(t.Context(), policyServer)
507+
508+
if test.errContains != "" {
509+
require.ErrorContains(t, err, test.errContains)
510+
} else {
511+
require.NoError(t, err)
512+
}
513+
})
514+
}
515+
}

api/policies/v1/zz_generated.deepcopy.go

Lines changed: 30 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

charts/kubewarden-controller/templates/_helpers.tpl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,3 +181,20 @@ are configured.
181181
- {{ .Values.auditScanner.reportCRDsKind }}
182182
{{- end -}}
183183
{{- end -}}
184+
185+
{{/*
186+
Compute the effective affinity for the controller deployment.
187+
Uses the controller-specific affinity if set, otherwise falls back to
188+
global.affinity for backward compatibility.
189+
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.
193+
*/}}
194+
{{- define "kubewarden-controller.effectiveAffinity" -}}
195+
{{- if .Values.affinity -}}
196+
{{- toYaml .Values.affinity -}}
197+
{{- else if .Values.global.affinity -}}
198+
{{- toYaml .Values.global.affinity -}}
199+
{{- end -}}
200+
{{- end -}}

0 commit comments

Comments
 (0)